docker/docker — pull request #9509 by duglin (2014-12-04 19:58:09+00:00)
Title: Proposal: turn errors from server into machines readable info

# Problem:
Right now the daemon will return errors as plain text in the HTTP body. For
example:
```
$ docker stop qwe
results in:

HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Date: Fri, 21 Nov 2014 19:24:28 GMT
Content-Length: 23

No such container: qwe
```

This makes it impossible for any kind of i18n support in the future. This
also encourages people to write very brittle code that searches the output,
and errors, for static English strings. For example, there are places in
the code where we search for "no such" and then make decisions based on the
absence or presence of that string. This is very brittle and error-prone
if someone decides to tweak the text of the messages.

# Proposal:

For backwards compatibility we need keep the HTTP body as is.

However, we can add new HTTP headers to the response that will allow for
better error processing on the client

For example:
```
X-Docker-Msg-Id: NoContainerID
X-Docker-Msg-Arg-1: qwe
```

could be returned in the sample error case shown above - where "Msg-Id"
is the unique strings associated with the message, which can then be used
in a message string lookup, and the "Msg-Arg-N" values can be used as
string/variable substitutions into the message string returned from the
lookup.

In subsequent versions of Docker, like v2, when can break compatibility,
we can move these headers into the HTTP body - e.g.:
```
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Date: Fri, 21 Nov 2014 19:24:28 GMT
Content-Length: xx

{ "Msg-Id": NoContainerID,
"Arg-1": "qwe",
"Text": "No such container: qwe"
}
```

The `Text` field is still provided in the native language of the daemon
to cover cases where something goes wrong with the client-side lookup.
In order for this to work, we need to make sure all errors that bubble
back to the HTTP daemon contain enough information to prodce the correct
output on the HTTP response flows:
```
type Derror struct {
error
messageID string // message ID (string lookup code)
args []interface{} // values to substitutions into error string
}
```

Note: we may want to add:
`status int // http status code`

Notice that Derror extends 'error', which means we can return it wherever
we currently 'error'. Hopefully, this should minize the impact on the
code during the migration from 'error' to 'Derror'. Ultimately, I think
all errors generated by the Docker code should be Derrors - however, we'll
still need to deal with non-Derror's since non-Docker code will generate
those.

**Stage 1:**
During stage 1 we should just convert things over to use Derrors and
have just a static English look-up table. This will allow us to work on
the migration of the code to use Derrors and ensure we have the basic
error reporting infrastructure correct.

CLI support will initially consist of the DockerCli.call() method
looking for the new X-Docker-Msg... HTTP headers and, if present,
generate 'Derror's instead of 'error's. We can then convert any code
that prints static English to use our the new look-up methods.

**Stage 2:**
Convert the English look-up table to full-i18n support. We can either
develop our own or use an existing one like:
https://github.com/nicksnyder/go-i18n

Note we will eventually need this for non-error messages as well but we
can start with the error cases first. Actually, I'd prefer if the REST API
always returned JSON for all ops, when possible, but we can deal with
those later. I think dealing with the error cases with the current APIs
is easier and less intrusive.

# Additional Things:

Looking at the go-i18n repo, it looks nice but what kind of bothers me
is how verbose the usage can be. For example:
```
fmt.Println(T("person_greeting", map[string]interface{}{
"Person": "Bob",
}))
```

Having to create the 'map' seems like overkill. I'm not sure, but I think
you can rewrite it as:
`fmt.Println(T("person_greeting", "Bob"))`
and it will just grab the args in the order specified. Which is nice.
However, it assumes the order of the %s's will be the same in all languages.
Unless I missed it, it would be nice if you could specify the substitution
strings as {{.1}} (meaning the 1st arg).

For now I've pretty much ignored this part of the problem in the docs
and proof-of-concept code I wrote as I think its a secondary issue. An
important one, but secondary and we can work on that once we agree to
the high-level direction

This PR has the doc changes necessary for this and the basic code changes
needed for the infrastructure to support this. I'm not 100% sure the code is
fully ready but before I went too much further I wanted to get the overall idea
reviewed.
To play with it use the "docker inspect" command as well as:
curl http://..../containers/foo/json
to see the new headers in action.

Signed-off-by: Doug Davis