Browse Source

Merge pull request #5839 from unclejack/improve_build_rm

add --force-rm to clean up after a failed build
Michael Crosby 11 years ago
parent
commit
db1a3551a3

+ 7 - 0
api/client/commands.go

@@ -110,6 +110,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
 	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
 	rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
+	forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds")
 	if err := cmd.Parse(args); err != nil {
 		return nil
 	}
@@ -197,6 +198,12 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	}
 	if *rm {
 		v.Set("rm", "1")
+	} else {
+		v.Set("rm", "0")
+	}
+
+	if *forceRm {
+		v.Set("forcerm", "1")
 	}
 
 	cli.LoadConfigFile()

+ 1 - 1
api/common.go

@@ -11,7 +11,7 @@ import (
 )
 
 const (
-	APIVERSION        version.Version = "1.11"
+	APIVERSION        version.Version = "1.12"
 	DEFAULTHTTPHOST                   = "127.0.0.1"
 	DEFAULTUNIXSOCKET                 = "/var/run/docker.sock"
 )

+ 9 - 1
api/server/server.go

@@ -901,12 +901,20 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	} else {
 		job.Stdout.Add(utils.NewWriteFlusher(w))
 	}
+
+	if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
+		job.Setenv("rm", "1")
+	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
+		job.Setenv("rm", "1")
+	} else {
+		job.Setenv("rm", r.FormValue("rm"))
+	}
 	job.Stdin.Add(r.Body)
 	job.Setenv("remote", r.FormValue("remote"))
 	job.Setenv("t", r.FormValue("t"))
 	job.Setenv("q", r.FormValue("q"))
 	job.Setenv("nocache", r.FormValue("nocache"))
-	job.Setenv("rm", r.FormValue("rm"))
+	job.Setenv("forcerm", r.FormValue("forcerm"))
 	job.SetenvJson("authConfig", authConfig)
 	job.SetenvJson("configFile", configFile)
 

+ 13 - 3
docs/sources/reference/api/docker_remote_api.md

@@ -20,13 +20,23 @@ page_keywords: API, Docker, rcli, REST, documentation
 
 
 
-The current version of the API is v1.11
+The current version of the API is v1.12
 
 Calling /images/<name>/insert is the same as calling
-/v1.11/images/<name>/insert
+/v1.12/images/<name>/insert
 
 You can still call an old version of the api using
-/v1.11/images/<name>/insert
+/v1.12/images/<name>/insert
+
+## v1.12
+
+### Full Documentation
+
+[*Docker Remote API v1.12*](/reference/api/docker_remote_api_v1.12/)
+
+### What's new
+
+docker build now has support for the `forcerm` parameter to always remove containers
 
 ## v1.11
 

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.10.md

@@ -1023,6 +1023,7 @@ Build an image from Dockerfile via stdin
         the resulting image in case of success
     -   **q** – suppress verbose build output
     -   **nocache** – do not use the cache when building the image
+    -   **rm** - remove intermediate containers after a successful build
 
     Request Headers:
 

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.11.md

@@ -1063,6 +1063,7 @@ Build an image from Dockerfile via stdin
         the resulting image in case of success
     -   **q** – suppress verbose build output
     -   **nocache** – do not use the cache when building the image
+    -   **rm** - remove intermediate containers after a successful build
 
     Request Headers:
 

+ 1363 - 0
docs/sources/reference/api/docker_remote_api_v1.12.md

@@ -0,0 +1,1363 @@
+page_title: Remote API v1.12
+page_description: API Documentation for Docker
+page_keywords: API, Docker, rcli, REST, documentation
+
+# Docker Remote API v1.12
+
+## 1. Brief introduction
+
+ - The Remote API has replaced rcli
+ - The daemon listens on `unix:///var/run/docker.sock` but you can
+   [*Bind Docker to another host/port or a Unix socket*](
+   /use/basics/#bind-docker).
+ - The API tends to be REST, but for some complex commands, like `attach`
+   or `pull`, the HTTP connection is hijacked to transport `stdout, stdin`
+   and `stderr`
+
+# 2. Endpoints
+
+## 2.1 Containers
+
+### List containers
+
+`GET /containers/json`
+
+List containers
+
+    **Example request**:
+
+        GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        [
+             {
+                     "Id": "8dfafdbc3a40",
+                     "Image": "base:latest",
+                     "Command": "echo 1",
+                     "Created": 1367854155,
+                     "Status": "Exit 0",
+                     "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
+                     "SizeRw":12288,
+                     "SizeRootFs":0
+             },
+             {
+                     "Id": "9cd87474be90",
+                     "Image": "base:latest",
+                     "Command": "echo 222222",
+                     "Created": 1367854155,
+                     "Status": "Exit 0",
+                     "Ports":[],
+                     "SizeRw":12288,
+                     "SizeRootFs":0
+             },
+             {
+                     "Id": "3176a2479c92",
+                     "Image": "base:latest",
+                     "Command": "echo 3333333333333333",
+                     "Created": 1367854154,
+                     "Status": "Exit 0",
+                     "Ports":[],
+                     "SizeRw":12288,
+                     "SizeRootFs":0
+             },
+             {
+                     "Id": "4cb07b47f9fb",
+                     "Image": "base:latest",
+                     "Command": "echo 444444444444444444444444444444444",
+                     "Created": 1367854152,
+                     "Status": "Exit 0",
+                     "Ports":[],
+                     "SizeRw":12288,
+                     "SizeRootFs":0
+             }
+        ]
+
+    Query Parameters:
+
+     
+
+    -   **all** – 1/True/true or 0/False/false, Show all containers.
+        Only running containers are shown by default
+    -   **limit** – Show `limit` last created
+        containers, include non-running ones.
+    -   **since** – Show only containers created since Id, include
+        non-running ones.
+    -   **before** – Show only containers created before Id, include
+        non-running ones.
+    -   **size** – 1/True/true or 0/False/false, Show the containers
+        sizes
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **400** – bad parameter
+    -   **500** – server error
+
+### Create a container
+
+`POST /containers/create`
+
+Create a container
+
+    **Example request**:
+
+        POST /containers/create HTTP/1.1
+        Content-Type: application/json
+
+        {
+             "Hostname":"",
+             "User":"",
+             "Memory":0,
+             "MemorySwap":0,
+             "AttachStdin":false,
+             "AttachStdout":true,
+             "AttachStderr":true,
+             "PortSpecs":null,
+             "Tty":false,
+             "OpenStdin":false,
+             "StdinOnce":false,
+             "Env":null,
+             "Cmd":[
+                     "date"
+             ],
+             "Dns":null,
+             "Image":"base",
+             "Volumes":{
+                     "/tmp": {}
+             },
+             "VolumesFrom":"",
+             "WorkingDir":"",
+             "DisableNetwork": false,
+             "ExposedPorts":{
+                     "22/tcp": {}
+             }
+        }
+
+    **Example response**:
+
+        HTTP/1.1 201 OK
+        Content-Type: application/json
+
+        {
+             "Id":"e90e34656806"
+             "Warnings":[]
+        }
+
+    Json Parameters:
+
+     
+
+    -   **config** – the container's configuration
+
+    Query Parameters:
+
+     
+
+    -   **name** – Assign the specified name to the container. Must
+        match `/?[a-zA-Z0-9_-]+`.
+
+    Status Codes:
+
+    -   **201** – no error
+    -   **404** – no such container
+    -   **406** – impossible to attach (container not running)
+    -   **500** – server error
+
+### Inspect a container
+
+`GET /containers/(id)/json`
+
+Return low-level information on the container `id`
+
+
+    **Example request**:
+
+        GET /containers/4fa6e0f0c678/json HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {
+                     "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
+                     "Created": "2013-05-07T14:51:42.041847+02:00",
+                     "Path": "date",
+                     "Args": [],
+                     "Config": {
+                             "Hostname": "4fa6e0f0c678",
+                             "User": "",
+                             "Memory": 0,
+                             "MemorySwap": 0,
+                             "AttachStdin": false,
+                             "AttachStdout": true,
+                             "AttachStderr": true,
+                             "PortSpecs": null,
+                             "Tty": false,
+                             "OpenStdin": false,
+                             "StdinOnce": false,
+                             "Env": null,
+                             "Cmd": [
+                                     "date"
+                             ],
+                             "Dns": null,
+                             "Image": "base",
+                             "Volumes": {},
+                             "VolumesFrom": "",
+                             "WorkingDir":""
+
+                     },
+                     "State": {
+                             "Running": false,
+                             "Pid": 0,
+                             "ExitCode": 0,
+                             "StartedAt": "2013-05-07T14:51:42.087658+02:01360",
+                             "Ghost": false
+                     },
+                     "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
+                     "NetworkSettings": {
+                             "IpAddress": "",
+                             "IpPrefixLen": 0,
+                             "Gateway": "",
+                             "Bridge": "",
+                             "PortMapping": null
+                     },
+                     "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
+                     "ResolvConfPath": "/etc/resolv.conf",
+                     "Volumes": {},
+                     "HostConfig": {
+                         "Binds": null,
+                         "ContainerIDFile": "",
+                         "LxcConf": [],
+                         "Privileged": false,
+                         "PortBindings": {
+                            "80/tcp": [
+                                {
+                                    "HostIp": "0.0.0.0",
+                                    "HostPort": "49153"
+                                }
+                            ]
+                         },
+                         "Links": null,
+                         "PublishAllPorts": false
+                     }
+        }
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### List processes running inside a container
+
+`GET /containers/(id)/top`
+
+List processes running inside the container `id`
+
+    **Example request**:
+
+        GET /containers/4fa6e0f0c678/top HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {
+             "Titles":[
+                     "USER",
+                     "PID",
+                     "%CPU",
+                     "%MEM",
+                     "VSZ",
+                     "RSS",
+                     "TTY",
+                     "STAT",
+                     "START",
+                     "TIME",
+                     "COMMAND"
+                     ],
+             "Processes":[
+                     ["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"],
+                     ["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"]
+             ]
+        }
+
+    Query Parameters:
+
+     
+
+    -   **ps_args** – ps arguments to use (eg. aux)
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Get container logs
+
+`GET /containers/(id)/logs`
+
+Get stdout and stderr logs from the container ``id``
+
+    **Example request**:
+
+       GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1&timestamps=1&follow=1 HTTP/1.1
+
+    **Example response**:
+
+       HTTP/1.1 200 OK
+       Content-Type: application/vnd.docker.raw-stream
+
+       {{ STREAM }}
+
+    Query Parameters:
+
+     
+
+    -   **follow** – 1/True/true or 0/False/false, return stream.
+        Default false
+    -   **stdout** – 1/True/true or 0/False/false, if logs=true, return
+        stdout log. Default false
+    -   **stderr** – 1/True/true or 0/False/false, if logs=true, return
+        stderr log. Default false
+    -   **timestamps** – 1/True/true or 0/False/false, if logs=true, print
+        timestamps for every log line. Default false
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Inspect changes on a container's filesystem
+
+`GET /containers/(id)/changes`
+
+Inspect changes on container `id`'s filesystem
+
+    **Example request**:
+
+        GET /containers/4fa6e0f0c678/changes HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        [
+             {
+                     "Path":"/dev",
+                     "Kind":0
+             },
+             {
+                     "Path":"/dev/kmsg",
+                     "Kind":1
+             },
+             {
+                     "Path":"/test",
+                     "Kind":1
+             }
+        ]
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Export a container
+
+`GET /containers/(id)/export`
+
+Export the contents of container `id`
+
+    **Example request**:
+
+        GET /containers/4fa6e0f0c678/export HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/octet-stream
+
+        {{ STREAM }}
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Start a container
+
+`POST /containers/(id)/start`
+
+Start the container `id`
+
+    **Example request**:
+
+        POST /containers/(id)/start HTTP/1.1
+        Content-Type: application/json
+
+        {
+             "Binds":["/tmp:/tmp"],
+             "LxcConf":{"lxc.utsname":"docker"},
+             "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] },
+             "PublishAllPorts":false,
+             "Privileged":false
+        }
+
+    **Example response**:
+
+        HTTP/1.1 204 No Content
+        Content-Type: text/plain
+
+    Json Parameters:
+
+     
+
+    -   **hostConfig** – the container's host configuration (optional)
+
+    Status Codes:
+
+    -   **204** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Stop a container
+
+`POST /containers/(id)/stop`
+
+Stop the container `id`
+
+    **Example request**:
+
+        POST /containers/e90e34656806/stop?t=5 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 204 OK
+
+    Query Parameters:
+
+     
+
+    -   **t** – number of seconds to wait before killing the container
+
+    Status Codes:
+
+    -   **204** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Restart a container
+
+`POST /containers/(id)/restart`
+
+Restart the container `id`
+
+    **Example request**:
+
+        POST /containers/e90e34656806/restart?t=5 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 204 OK
+
+    Query Parameters:
+
+     
+
+    -   **t** – number of seconds to wait before killing the container
+
+    Status Codes:
+
+    -   **204** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Kill a container
+
+`POST /containers/(id)/kill`
+
+Kill the container `id`
+
+    **Example request**:
+
+        POST /containers/e90e34656806/kill HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 204 OK
+
+    Query Parameters
+
+    -   **signal** - Signal to send to the container: integer or string like "SIGINT".
+        When not set, SIGKILL is assumed and the call will waits for the container to exit.
+
+    Status Codes:
+
+    -   **204** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Attach to a container
+
+`POST /containers/(id)/attach`
+
+Attach to the container `id`
+
+    **Example request**:
+
+        POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/vnd.docker.raw-stream
+
+        {{ STREAM }}
+
+    Query Parameters:
+
+     
+
+    -   **logs** – 1/True/true or 0/False/false, return logs. Default
+        false
+    -   **stream** – 1/True/true or 0/False/false, return stream.
+        Default false
+    -   **stdin** – 1/True/true or 0/False/false, if stream=true, attach
+        to stdin. Default false
+    -   **stdout** – 1/True/true or 0/False/false, if logs=true, return
+        stdout log, if stream=true, attach to stdout. Default false
+    -   **stderr** – 1/True/true or 0/False/false, if logs=true, return
+        stderr log, if stream=true, attach to stderr. Default false
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **400** – bad parameter
+    -   **404** – no such container
+    -   **500** – server error
+
+    **Stream details**:
+
+    When using the TTY setting is enabled in
+    [`POST /containers/create`
+    ](../docker_remote_api_v1.9/#post--containers-create "POST /containers/create"),
+    the stream is the raw data from the process PTY and client's stdin.
+    When the TTY is disabled, then the stream is multiplexed to separate
+    stdout and stderr.
+
+    The format is a **Header** and a **Payload** (frame).
+
+    **HEADER**
+
+    The header will contain the information on which stream write the
+    stream (stdout or stderr). It also contain the size of the
+    associated frame encoded on the last 4 bytes (uint32).
+
+    It is encoded on the first 8 bytes like this:
+
+        header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
+
+    `STREAM_TYPE` can be:
+
+    -   0: stdin (will be writen on stdout)
+    -   1: stdout
+    -   2: stderr
+
+    `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of
+    the uint32 size encoded as big endian.
+
+    **PAYLOAD**
+
+    The payload is the raw stream.
+
+    **IMPLEMENTATION**
+
+    The simplest way to implement the Attach protocol is the following:
+
+    1.  Read 8 bytes
+    2.  chose stdout or stderr depending on the first byte
+    3.  Extract the frame size from the last 4 byets
+    4.  Read the extracted size and output it on the correct output
+    5.  Goto 1)
+
+### Wait a container
+
+`POST /containers/(id)/wait`
+
+Block until container `id` stops, then returns the exit code
+
+    **Example request**:
+
+        POST /containers/16253994b7c4/wait HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"StatusCode":0}
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Remove a container
+
+`DELETE /containers/(id)`
+
+Remove the container `id` from the filesystem
+
+    **Example request**:
+
+        DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 204 OK
+
+    Query Parameters:
+
+     
+
+    -   **v** – 1/True/true or 0/False/false, Remove the volumes
+        associated to the container. Default false
+    -   **force** – 1/True/true or 0/False/false, Removes the container
+        even if it was running. Default false
+
+    Status Codes:
+
+    -   **204** – no error
+    -   **400** – bad parameter
+    -   **404** – no such container
+    -   **500** – server error
+
+### Copy files or folders from a container
+
+`POST /containers/(id)/copy`
+
+Copy files or folders of container `id`
+
+    **Example request**:
+
+        POST /containers/4fa6e0f0c678/copy HTTP/1.1
+        Content-Type: application/json
+
+        {
+             "Resource":"test.txt"
+        }
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/octet-stream
+
+        {{ STREAM }}
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+## 2.2 Images
+
+### List Images
+
+`GET /images/json`
+
+**Example request**:
+
+        GET /images/json?all=0 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        [
+          {
+             "RepoTags": [
+               "ubuntu:12.04",
+               "ubuntu:precise",
+               "ubuntu:latest"
+             ],
+             "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
+             "Created": 1365714795,
+             "Size": 131506275,
+             "VirtualSize": 131506275
+          },
+          {
+             "RepoTags": [
+               "ubuntu:12.10",
+               "ubuntu:quantal"
+             ],
+             "ParentId": "27cf784147099545",
+             "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
+             "Created": 1364102658,
+             "Size": 24653,
+             "VirtualSize": 180116135
+          }
+        ]
+
+### Create an image
+
+`POST /images/create`
+
+Create an image, either by pull it from the registry or by importing it
+
+    **Example request**:
+
+        POST /images/create?fromImage=base HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"status":"Pulling..."}
+        {"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}}
+        {"error":"Invalid..."}
+        ...
+
+    When using this endpoint to pull an image from the registry, the
+    `X-Registry-Auth` header can be used to include
+    a base64-encoded AuthConfig object.
+
+    Query Parameters:
+
+     
+
+    -   **fromImage** – name of the image to pull
+    -   **fromSrc** – source to import, - means stdin
+    -   **repo** – repository
+    -   **tag** – tag
+    -   **registry** – the registry to pull from
+
+    Request Headers:
+
+     
+
+    -   **X-Registry-Auth** – base64-encoded AuthConfig object
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Insert a file in an image
+
+`POST /images/(name)/insert`
+
+Insert a file from `url` in the image `name` at `path`
+
+    **Example request**:
+
+        POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"status":"Inserting..."}
+        {"status":"Inserting", "progress":"1/? (n/a)", "progressDetail":{"current":1}}
+        {"error":"Invalid..."}
+        ...
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Inspect an image
+
+`GET /images/(name)/json`
+
+Return low-level information on the image `name`
+
+    **Example request**:
+
+        GET /images/base/json HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {
+             "id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
+             "parent":"27cf784147099545",
+             "created":"2013-03-23T22:24:18.818426-07:00",
+             "container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
+             "container_config":
+                     {
+                             "Hostname":"",
+                             "User":"",
+                             "Memory":0,
+                             "MemorySwap":0,
+                             "AttachStdin":false,
+                             "AttachStdout":false,
+                             "AttachStderr":false,
+                             "PortSpecs":null,
+                             "Tty":true,
+                             "OpenStdin":true,
+                             "StdinOnce":false,
+                             "Env":null,
+                             "Cmd": ["/bin/bash"]
+                             ,"Dns":null,
+                             "Image":"base",
+                             "Volumes":null,
+                             "VolumesFrom":"",
+                             "WorkingDir":""
+                     },
+             "Size": 6824592
+        }
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such image
+    -   **500** – server error
+
+### Get the history of an image
+
+`GET /images/(name)/history`
+
+Return the history of the image `name`
+
+    **Example request**:
+
+        GET /images/base/history HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        [
+             {
+                     "Id":"b750fe79269d",
+                     "Created":1364102658,
+                     "CreatedBy":"/bin/bash"
+             },
+             {
+                     "Id":"27cf78414709",
+                     "Created":1364068391,
+                     "CreatedBy":""
+             }
+        ]
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such image
+    -   **500** – server error
+
+### Push an image on the registry
+
+`POST /images/(name)/push`
+
+Push the image `name` on the registry
+
+    **Example request**:
+
+        POST /images/test/push HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"status":"Pushing..."}
+        {"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}}
+        {"error":"Invalid..."}
+        ...
+
+    Query Parameters:
+
+     
+
+    -   **registry** – the registry you wan to push, optional
+
+    Request Headers:
+
+     
+
+    -   **X-Registry-Auth** – include a base64-encoded AuthConfig
+        object.
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such image
+    -   **500** – server error
+
+### Tag an image into a repository
+
+`POST /images/(name)/tag`
+
+Tag the image `name` into a repository
+
+    **Example request**:
+
+        POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 201 OK
+
+    Query Parameters:
+
+     
+
+    -   **repo** – The repository to tag in
+    -   **force** – 1/True/true or 0/False/false, default false
+
+    Status Codes:
+
+    -   **201** – no error
+    -   **400** – bad parameter
+    -   **404** – no such image
+    -   **409** – conflict
+    -   **500** – server error
+
+### Remove an image
+
+`DELETE /images/(name)`
+
+Remove the image `name` from the filesystem
+
+    **Example request**:
+
+        DELETE /images/test HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-type: application/json
+
+        [
+         {"Untagged":"3e2f21a89f"},
+         {"Deleted":"3e2f21a89f"},
+         {"Deleted":"53b4f83ac9"}
+        ]
+
+    Query Parameters:
+
+     
+
+    -   **force** – 1/True/true or 0/False/false, default false
+    -   **noprune** – 1/True/true or 0/False/false, default false
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **404** – no such image
+    -   **409** – conflict
+    -   **500** – server error
+
+### Search images
+
+`GET /images/search`
+
+Search for an image on [Docker.io](https://index.docker.io).
+
+> **Note**:
+> The response keys have changed from API v1.6 to reflect the JSON
+> sent by the registry server to the docker daemon's request.
+
+    **Example request**:
+
+        GET /images/search?term=sshd HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        [
+                {
+                    "description": "",
+                    "is_official": false,
+                    "is_trusted": false,
+                    "name": "wma55/u1210sshd",
+                    "star_count": 0
+                },
+                {
+                    "description": "",
+                    "is_official": false,
+                    "is_trusted": false,
+                    "name": "jdswinbank/sshd",
+                    "star_count": 0
+                },
+                {
+                    "description": "",
+                    "is_official": false,
+                    "is_trusted": false,
+                    "name": "vgauthier/sshd",
+                    "star_count": 0
+                }
+        ...
+        ]
+
+    Query Parameters:
+
+     
+
+    -   **term** – term to search
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+## 2.3 Misc
+
+### Build an image from Dockerfile via stdin
+
+`POST /build`
+
+Build an image from Dockerfile via stdin
+
+    **Example request**:
+
+        POST /build HTTP/1.1
+
+        {{ STREAM }}
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"stream":"Step 1..."}
+        {"stream":"..."}
+        {"error":"Error...", "errorDetail":{"code": 123, "message": "Error..."}}
+
+    The stream must be a tar archive compressed with one of the
+    following algorithms: identity (no compression), gzip, bzip2, xz.
+
+    The archive must include a file called `Dockerfile`
+    at its root. It may include any number of other files,
+    which will be accessible in the build context (See the [*ADD build
+    command*](/reference/builder/#dockerbuilder)).
+
+    Query Parameters:
+
+     
+
+    -   **t** – repository name (and optionally a tag) to be applied to
+        the resulting image in case of success
+    -   **q** – suppress verbose build output
+    -   **nocache** – do not use the cache when building the image
+    -   **rm** - remove intermediate containers after a successful build (default behavior)
+    -   **forcerm - always remove intermediate containers (includes rm)
+
+    Request Headers:
+
+     
+
+    -   **Content-type** – should be set to
+        `"application/tar"`.
+    -   **X-Registry-Config** – base64-encoded ConfigFile object
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Check auth configuration
+
+`POST /auth`
+
+Get the default username and email
+
+    **Example request**:
+
+        POST /auth HTTP/1.1
+        Content-Type: application/json
+
+        {
+             "username":"hannibal",
+             "password:"xxxx",
+             "email":"hannibal@a-team.com",
+             "serveraddress":"https://index.docker.io/v1/"
+        }
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **204** – no error
+    -   **500** – server error
+
+### Display system-wide information
+
+`GET /info`
+
+Display system-wide information
+
+    **Example request**:
+
+        GET /info HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {
+             "Containers":11,
+             "Images":16,
+             "Debug":false,
+             "NFd": 11,
+             "NGoroutines":21,
+             "MemoryLimit":true,
+             "SwapLimit":false,
+             "IPv4Forwarding":true
+        }
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Show the docker version information
+
+`GET /version`
+
+Show the docker version information
+
+    **Example request**:
+
+        GET /version HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {
+             "Version":"0.2.2",
+             "GitCommit":"5a2a5cc+CHANGES",
+             "GoVersion":"go1.0.3"
+        }
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Ping the docker server
+
+`GET /_ping`
+
+Ping the docker server
+
+    **Example request**:
+
+        GET /_ping HTTP/1.1
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+
+        OK
+
+    Status Codes:
+
+    -   **200** - no error
+    -   **500** - server error
+
+### Create a new image from a container's changes
+
+`POST /commit`
+
+Create a new image from a container's changes
+
+    **Example request**:
+
+        POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+        Content-Type: application/json
+
+        {
+             "Hostname":"",
+             "User":"",
+             "Memory":0,
+             "MemorySwap":0,
+             "AttachStdin":false,
+             "AttachStdout":true,
+             "AttachStderr":true,
+             "PortSpecs":null,
+             "Tty":false,
+             "OpenStdin":false,
+             "StdinOnce":false,
+             "Env":null,
+             "Cmd":[
+                     "date"
+             ],
+             "Volumes":{
+                     "/tmp": {}
+             },
+             "WorkingDir":"",
+             "DisableNetwork": false,
+             "ExposedPorts":{
+                     "22/tcp": {}
+             }
+        }
+
+    **Example response**:
+
+        HTTP/1.1 201 OK
+            Content-Type: application/vnd.docker.raw-stream
+
+        {"Id":"596069db4bf5"}
+
+    Json Parameters:
+
+
+
+    -  **config** - the container's configuration
+
+    Query Parameters:
+
+     
+
+    -   **container** – source container
+    -   **repo** – repository
+    -   **tag** – tag
+    -   **m** – commit message
+    -   **author** – author (eg. "John Hannibal Smith
+        <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>")
+
+    Status Codes:
+
+    -   **201** – no error
+    -   **404** – no such container
+    -   **500** – server error
+
+### Monitor Docker's events
+
+`GET /events`
+
+Get events from docker, either in real time via streaming, or
+via polling (using since)
+
+    **Example request**:
+
+        GET /events?since=1374067924
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/json
+
+        {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
+        {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
+        {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
+        {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
+
+    Query Parameters:
+
+     
+
+    -   **since** – timestamp used for polling
+    -   **until** – timestamp used for polling
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Get a tarball containing all images and tags in a repository
+
+`GET /images/(name)/get`
+
+Get a tarball containing all images and metadata for the repository
+specified by `name`.
+
+    **Example request**
+
+        GET /images/ubuntu/get
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+        Content-Type: application/x-tar
+
+        Binary data stream
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+### Load a tarball with a set of images and tags into docker
+
+`POST /images/load`
+
+Load a set of images and tags into the docker repository.
+
+    **Example request**
+
+        POST /images/load
+
+        Tarball in body
+
+    **Example response**:
+
+        HTTP/1.1 200 OK
+
+    Status Codes:
+
+    -   **200** – no error
+    -   **500** – server error
+
+# 3. Going further
+
+## 3.1 Inside `docker run`
+
+Here are the steps of `docker run`:
+
+- Create the container
+
+- If the status code is 404, it means the image doesn't exists:
+    - Try to pull it
+    - Then retry to create the container
+
+- Start the container
+
+- If you are not in detached mode:
+    - Attach to the container, using logs=1 (to have stdout and
+      stderr from the container's start) and stream=1
+
+- If in detached mode or only stdin is attached:
+    - Display the container's id
+
+## 3.2 Hijacking
+
+In this version of the API, /attach, uses hijacking to transport stdin,
+stdout and stderr on the same socket. This might change in the future.
+
+## 3.3 CORS Requests
+
+To enable cross origin requests to the remote api add the flag
+"–api-enable-cors" when running docker in daemon mode.
+
+    $ docker -d -H="192.168.1.9:4243" --api-enable-cors

+ 1 - 0
docs/sources/reference/commandline/cli.md

@@ -192,6 +192,7 @@ To kill the container, use `docker kill`.
 
     Build a new container image from the source code at PATH
 
+      --force-rm=false     Always remove intermediate containers, even after unsuccessful builds
       --no-cache=false     Do not use cache when building the image
       -q, --quiet=false    Suppress the verbose output generated by the containers
       --rm=true            Remove intermediate containers after a successful build

+ 3 - 0
integration-cli/build_tests/TestBuildForceRm/Dockerfile

@@ -0,0 +1,3 @@
+FROM busybox
+RUN true
+RUN thiswillfail

+ 4 - 0
integration-cli/build_tests/TestBuildRm/Dockerfile

@@ -0,0 +1,4 @@
+FROM busybox
+ADD foo /
+ADD foo /
+

+ 1 - 0
integration-cli/build_tests/TestBuildRm/foo

@@ -0,0 +1 @@
+bar

+ 112 - 0
integration-cli/docker_cli_build_test.go

@@ -264,6 +264,118 @@ func TestBuildWithInaccessibleFilesInContext(t *testing.T) {
 	logDone("build - ADD from context with accessible links must work")
 }
 
+func TestBuildForceRm(t *testing.T) {
+	containerCountBefore, err := getContainerCount()
+	if err != nil {
+		t.Fatalf("failed to get the container count: %s", err)
+	}
+
+	buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildForceRm")
+	buildCmd := exec.Command(dockerBinary, "build", "--force-rm", ".")
+	buildCmd.Dir = buildDirectory
+	_, exitCode, err := runCommandWithOutput(buildCmd)
+
+	if err == nil || exitCode == 0 {
+		t.Fatal("failed to build the image")
+	}
+
+	containerCountAfter, err := getContainerCount()
+	if err != nil {
+		t.Fatalf("failed to get the container count: %s", err)
+	}
+
+	if containerCountBefore != containerCountAfter {
+		t.Fatalf("--force-rm shouldn't have left containers behind")
+	}
+
+	logDone("build - ensure --force-rm doesn't leave containers behind")
+}
+
+func TestBuildRm(t *testing.T) {
+	{
+		containerCountBefore, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
+		buildCmd := exec.Command(dockerBinary, "build", "--rm", "-t", "testbuildrm", ".")
+		buildCmd.Dir = buildDirectory
+		_, exitCode, err := runCommandWithOutput(buildCmd)
+
+		if err != nil || exitCode != 0 {
+			t.Fatal("failed to build the image")
+		}
+
+		containerCountAfter, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		if containerCountBefore != containerCountAfter {
+			t.Fatalf("-rm shouldn't have left containers behind")
+		}
+		deleteImages("testbuildrm")
+	}
+
+	{
+		containerCountBefore, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
+		buildCmd := exec.Command(dockerBinary, "build", "-t", "testbuildrm", ".")
+		buildCmd.Dir = buildDirectory
+		_, exitCode, err := runCommandWithOutput(buildCmd)
+
+		if err != nil || exitCode != 0 {
+			t.Fatal("failed to build the image")
+		}
+
+		containerCountAfter, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		if containerCountBefore != containerCountAfter {
+			t.Fatalf("--rm shouldn't have left containers behind")
+		}
+		deleteImages("testbuildrm")
+	}
+
+	{
+		containerCountBefore, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
+		buildCmd := exec.Command(dockerBinary, "build", "--rm=false", "-t", "testbuildrm", ".")
+		buildCmd.Dir = buildDirectory
+		_, exitCode, err := runCommandWithOutput(buildCmd)
+
+		if err != nil || exitCode != 0 {
+			t.Fatal("failed to build the image")
+		}
+
+		containerCountAfter, err := getContainerCount()
+		if err != nil {
+			t.Fatalf("failed to get the container count: %s", err)
+		}
+
+		if containerCountBefore == containerCountAfter {
+			t.Fatalf("--rm=false should have left containers behind")
+		}
+		deleteAllContainers()
+		deleteImages("testbuildrm")
+
+	}
+
+	logDone("build - ensure --rm doesn't leave containers behind and that --rm=true is the default")
+	logDone("build - ensure --rm=false overrides the default")
+}
+
 // TODO: TestCaching
 
 // TODO: TestADDCacheInvalidation

+ 26 - 0
integration-cli/docker_utils.go

@@ -3,6 +3,7 @@ package main
 import (
 	"fmt"
 	"os/exec"
+	"strconv"
 	"strings"
 	"testing"
 )
@@ -71,3 +72,28 @@ func findContainerIp(t *testing.T, id string) string {
 
 	return strings.Trim(out, " \r\n'")
 }
+
+func getContainerCount() (int, error) {
+	const containers = "Containers:"
+
+	cmd := exec.Command(dockerBinary, "info")
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		return 0, err
+	}
+
+	lines := strings.Split(out, "\n")
+	for _, line := range lines {
+		if strings.Contains(line, containers) {
+			output := stripTrailingCharacters(line)
+			output = strings.TrimLeft(output, containers)
+			output = strings.Trim(output, " ")
+			containerCount, err := strconv.Atoi(output)
+			if err != nil {
+				return 0, err
+			}
+			return containerCount, nil
+		}
+	}
+	return 0, fmt.Errorf("couldn't find the Container count in the output")
+}

+ 3 - 3
integration/buildfile_test.go

@@ -397,7 +397,7 @@ func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, u
 	}
 	dockerfile := constructDockerfile(context.dockerfile, ip, port)
 
-	buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
+	buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
 	id, err := buildfile.Build(context.Archive(dockerfile, t))
 	if err != nil {
 		return nil, err
@@ -839,7 +839,7 @@ func TestForbiddenContextPath(t *testing.T) {
 	}
 	dockerfile := constructDockerfile(context.dockerfile, ip, port)
 
-	buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
+	buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, true, false, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
 	_, err = buildfile.Build(context.Archive(dockerfile, t))
 
 	if err == nil {
@@ -885,7 +885,7 @@ func TestBuildADDFileNotFound(t *testing.T) {
 	}
 	dockerfile := constructDockerfile(context.dockerfile, ip, port)
 
-	buildfile := server.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
+	buildfile := server.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
 	_, err = buildfile.Build(context.Archive(dockerfile, t))
 
 	if err == nil {

+ 6 - 1
server/buildfile.go

@@ -52,6 +52,7 @@ type buildFile struct {
 	verbose      bool
 	utilizeCache bool
 	rm           bool
+	forceRm      bool
 
 	authConfig *registry.AuthConfig
 	configFile *registry.ConfigFile
@@ -817,6 +818,9 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
 			continue
 		}
 		if err := b.BuildStep(fmt.Sprintf("%d", stepN), line); err != nil {
+			if b.forceRm {
+				b.clearTmp(b.tmpContainers)
+			}
 			return "", err
 		} else if b.rm {
 			b.clearTmp(b.tmpContainers)
@@ -869,7 +873,7 @@ func stripComments(raw []byte) string {
 	return strings.Join(out, "\n")
 }
 
-func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
+func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, forceRm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
 	return &buildFile{
 		daemon:        srv.daemon,
 		srv:           srv,
@@ -881,6 +885,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
 		verbose:       verbose,
 		utilizeCache:  utilizeCache,
 		rm:            rm,
+		forceRm:       forceRm,
 		sf:            sf,
 		authConfig:    auth,
 		configFile:    authConfigFile,

+ 2 - 1
server/server.go

@@ -424,6 +424,7 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
 		suppressOutput = job.GetenvBool("q")
 		noCache        = job.GetenvBool("nocache")
 		rm             = job.GetenvBool("rm")
+		forceRm        = job.GetenvBool("forcerm")
 		authConfig     = &registry.AuthConfig{}
 		configFile     = &registry.ConfigFile{}
 		tag            string
@@ -482,7 +483,7 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
 			Writer:          job.Stdout,
 			StreamFormatter: sf,
 		},
-		!suppressOutput, !noCache, rm, job.Stdout, sf, authConfig, configFile)
+		!suppressOutput, !noCache, rm, forceRm, job.Stdout, sf, authConfig, configFile)
 	id, err := b.Build(context)
 	if err != nil {
 		return job.Error(err)