diff --git a/api/client/commands.go b/api/client/commands.go index 8530c02285..9710f0c563 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -2120,7 +2120,7 @@ func (cid *cidFile) Write(id string) error { return nil } -func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { +func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) { containerValues := url.Values{} if name != "" { containerValues.Set("name", name) @@ -2159,23 +2159,19 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc return nil, err } - var result engine.Env - if err := result.Decode(stream); err != nil { + var response types.ContainerCreateResponse + if err := json.NewDecoder(stream).Decode(&response); err != nil { return nil, err } - - for _, warning := range result.GetList("Warnings") { + for _, warning := range response.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } - if containerIDFile != nil { - if err = containerIDFile.Write(result.Get("Id")); err != nil { + if err = containerIDFile.Write(response.ID); err != nil { return nil, err } } - - return result, nil - + return &response, nil } func (cli *DockerCli) CmdCreate(args ...string) error { @@ -2194,14 +2190,11 @@ func (cli *DockerCli) CmdCreate(args ...string) error { cmd.Usage() return nil } - - createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) + response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) if err != nil { return err } - - fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id")) - + fmt.Fprintf(cli.out, "%s\n", response.ID) return nil } @@ -2259,38 +2252,32 @@ func (cli *DockerCli) CmdRun(args ...string) error { sigProxy = false } - runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) + createResponse, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) if err != nil { return err } - if sigProxy { - sigc := cli.forwardAllSignals(runResult.Get("Id")) + sigc := cli.forwardAllSignals(createResponse.ID) defer signal.StopCatch(sigc) } - var ( waitDisplayId chan struct{} errCh chan error ) - if !config.AttachStdout && !config.AttachStderr { // Make this asynchronous to allow the client to write to stdin before having to read the ID waitDisplayId = make(chan struct{}) go func() { defer close(waitDisplayId) - fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id")) + fmt.Fprintf(cli.out, "%s\n", createResponse.ID) }() } - if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") { return ErrConflictRestartPolicyAndAutoRemove } - // We need to instantiate the chan because the select needs it. It can // be closed but can't be uninitialized. hijacked := make(chan io.Closer) - // Block the return until the chan gets closed defer func() { log.Debugf("End of CmdRun(), Waiting for hijack to finish.") @@ -2298,7 +2285,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { log.Errorf("Hijack did not finish (chan still open)") } }() - if config.AttachStdin || config.AttachStdout || config.AttachStderr { var ( out, stderr io.Writer @@ -2306,7 +2292,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { v = url.Values{} ) v.Set("stream", "1") - if config.AttachStdin { v.Set("stdin", "1") in = cli.in @@ -2323,14 +2308,12 @@ func (cli *DockerCli) CmdRun(args ...string) error { stderr = cli.err } } - errCh = promise.Go(func() error { - return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) + return cli.hijack("POST", "/containers/"+createResponse.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) }) } else { close(hijacked) } - // Acknowledge the hijack before starting select { case closer := <-hijacked: @@ -2347,12 +2330,12 @@ func (cli *DockerCli) CmdRun(args ...string) error { } //start the container - if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", nil, false)); err != nil { + if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil { return err } if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { - if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { + if err := cli.monitorTtySize(createResponse.ID, false); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } } @@ -2377,26 +2360,26 @@ func (cli *DockerCli) CmdRun(args ...string) error { if *flAutoRemove { // Autoremove: wait for the container to finish, retrieve // the exit code and remove the container - if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil { + if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil { return err } - if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { + if _, status, err = getExitCode(cli, createResponse.ID); err != nil { return err } - if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil { + if _, _, err := readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil { return err } } else { // No Autoremove: Simply retrieve the exit code if !config.Tty { // In non-TTY mode, we can't detach, so we must wait for container exit - if status, err = waitForExit(cli, runResult.Get("Id")); err != nil { + if status, err = waitForExit(cli, createResponse.ID); err != nil { return err } } else { // In TTY mode, there is a race: if the process dies too slowly, the state could // be updated after the getExitCode call and result in the wrong exit code being reported - if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { + if _, status, err = getExitCode(cli, createResponse.ID); err != nil { return err } } diff --git a/api/server/server.go b/api/server/server.go index 78ca4b7829..02ff0375a0 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -27,6 +27,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/api" + "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/networkdriver/portallocator" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/listenbuffer" @@ -140,12 +141,22 @@ func httpError(w http.ResponseWriter, err error) { } } -func writeJSON(w http.ResponseWriter, code int, v engine.Env) error { +// writeJSONEnv writes the engine.Env values to the http response stream as a +// json encoded body. +func writeJSONEnv(w http.ResponseWriter, code int, v engine.Env) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) return v.Encode(w) } +// writeJSON writes the value v to the http response stream as json with standard +// json encoding. +func writeJSON(w http.ResponseWriter, code int, v interface{}) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + return json.NewEncoder(w).Encode(v) +} + func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) { w.Header().Set("Content-Type", "application/json") if flush { @@ -183,7 +194,7 @@ func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter if status := engine.Tail(stdoutBuffer, 1); status != "" { var env engine.Env env.Set("Status", status) - return writeJSON(w, http.StatusOK, env) + return writeJSONEnv(w, http.StatusOK, env) } w.WriteHeader(http.StatusNoContent) return nil @@ -525,7 +536,7 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit return err } env.Set("Id", engine.Tail(stdoutBuffer, 1)) - return writeJSON(w, http.StatusCreated, env) + return writeJSONEnv(w, http.StatusCreated, env) } // Creates an image from Pull or from Import @@ -703,18 +714,16 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re if err := parseForm(r); err != nil { return nil } + if err := checkForJson(r); err != nil { + return err + } var ( - out engine.Env job = eng.Job("create", r.Form.Get("name")) outWarnings []string stdoutBuffer = bytes.NewBuffer(nil) warnings = bytes.NewBuffer(nil) ) - if err := checkForJson(r); err != nil { - return err - } - if err := job.DecodeEnv(r.Body); err != nil { return err } @@ -730,10 +739,10 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re for scanner.Scan() { outWarnings = append(outWarnings, scanner.Text()) } - out.Set("Id", engine.Tail(stdoutBuffer, 1)) - out.SetList("Warnings", outWarnings) - - return writeJSON(w, http.StatusCreated, out) + return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{ + ID: engine.Tail(stdoutBuffer, 1), + Warnings: outWarnings, + }) } func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -876,7 +885,7 @@ func postContainersWait(eng *engine.Engine, version version.Version, w http.Resp } env.Set("StatusCode", engine.Tail(stdoutBuffer, 1)) - return writeJSON(w, http.StatusOK, env) + return writeJSONEnv(w, http.StatusOK, env) } func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -1147,7 +1156,7 @@ func postContainerExecCreate(eng *engine.Engine, version version.Version, w http // Return the ID out.Set("Id", engine.Tail(stdoutBuffer, 1)) - return writeJSON(w, http.StatusCreated, out) + return writeJSONEnv(w, http.StatusCreated, out) } // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. diff --git a/api/types/types.go b/api/types/types.go new file mode 100644 index 0000000000..f1b1d041ea --- /dev/null +++ b/api/types/types.go @@ -0,0 +1,11 @@ +package types + +// ContainerCreateResponse contains the information returned to a client on the +// creation of a new container. +type ContainerCreateResponse struct { + // ID is the ID of the created container. + ID string `json:"Id"` + + // Warnings are any warnings encountered during the creation of the container. + Warnings []string `json:"Warnings"` +}