Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create api types package for structured responses #10986

Merged
merged 2 commits into from
Mar 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 22 additions & 39 deletions api/client/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/stats"
"github.com/docker/docker/api/types"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -2259,54 +2252,46 @@ 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.")
if _, ok := <-hijacked; ok {
log.Errorf("Hijack did not finish (chan still open)")
}
}()

if config.AttachStdin || config.AttachStdout || config.AttachStderr {
var (
out, stderr io.Writer
in io.ReadCloser
v = url.Values{}
)
v.Set("stream", "1")

if config.AttachStdin {
v.Set("stdin", "1")
in = cli.in
Expand All @@ -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:
Expand All @@ -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)
}
}
Expand All @@ -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
}
}
Expand Down Expand Up @@ -2640,7 +2623,7 @@ func (s *containerStats) Collect(cli *DockerCli) {
)
go func() {
for {
var v *stats.Stats
var v *types.Stats
if err := dec.Decode(&v); err != nil {
u <- err
return
Expand Down Expand Up @@ -2757,7 +2740,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
return nil
}

func calculateCpuPercent(previousCpu, previousSystem uint64, v *stats.Stats) float64 {
func calculateCpuPercent(previousCpu, previousSystem uint64, v *types.Stats) float64 {
var (
cpuPercent = 0.0
// calculate the change for the cpu usage of the container in between readings
Expand Down
37 changes: 23 additions & 14 deletions api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion api/stats/stats.go → api/types/stats.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This package is used for API stability in the types and response to the
// consumers of the API stats endpoint.
package stats
package types

import "time"

Expand Down
11 changes: 11 additions & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
@@ -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"`
}
24 changes: 12 additions & 12 deletions daemon/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package daemon
import (
"encoding/json"

"github.com/docker/docker/api/stats"
"github.com/docker/docker/api/types"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/engine"
"github.com/docker/libcontainer"
Expand Down Expand Up @@ -33,10 +33,10 @@ func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status {

// convertToAPITypes converts the libcontainer.ContainerStats to the api specific
// structs. This is done to preserve API compatibility and versioning.
func convertToAPITypes(ls *libcontainer.ContainerStats) *stats.Stats {
s := &stats.Stats{}
func convertToAPITypes(ls *libcontainer.ContainerStats) *types.Stats {
s := &types.Stats{}
if ls.NetworkStats != nil {
s.Network = stats.Network{
s.Network = types.Network{
RxBytes: ls.NetworkStats.RxBytes,
RxPackets: ls.NetworkStats.RxPackets,
RxErrors: ls.NetworkStats.RxErrors,
Expand All @@ -49,7 +49,7 @@ func convertToAPITypes(ls *libcontainer.ContainerStats) *stats.Stats {
}
cs := ls.CgroupStats
if cs != nil {
s.BlkioStats = stats.BlkioStats{
s.BlkioStats = types.BlkioStats{
IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive),
IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive),
IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive),
Expand All @@ -60,21 +60,21 @@ func convertToAPITypes(ls *libcontainer.ContainerStats) *stats.Stats {
SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive),
}
cpu := cs.CpuStats
s.CpuStats = stats.CpuStats{
CpuUsage: stats.CpuUsage{
s.CpuStats = types.CpuStats{
CpuUsage: types.CpuUsage{
TotalUsage: cpu.CpuUsage.TotalUsage,
PercpuUsage: cpu.CpuUsage.PercpuUsage,
UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode,
UsageInUsermode: cpu.CpuUsage.UsageInUsermode,
},
ThrottlingData: stats.ThrottlingData{
ThrottlingData: types.ThrottlingData{
Periods: cpu.ThrottlingData.Periods,
ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods,
ThrottledTime: cpu.ThrottlingData.ThrottledTime,
},
}
mem := cs.MemoryStats
s.MemoryStats = stats.MemoryStats{
s.MemoryStats = types.MemoryStats{
Usage: mem.Usage,
MaxUsage: mem.MaxUsage,
Stats: mem.Stats,
Expand All @@ -84,10 +84,10 @@ func convertToAPITypes(ls *libcontainer.ContainerStats) *stats.Stats {
return s
}

func copyBlkioEntry(entries []cgroups.BlkioStatEntry) []stats.BlkioStatEntry {
out := make([]stats.BlkioStatEntry, len(entries))
func copyBlkioEntry(entries []cgroups.BlkioStatEntry) []types.BlkioStatEntry {
out := make([]types.BlkioStatEntry, len(entries))
for i, re := range entries {
out[i] = stats.BlkioStatEntry{
out[i] = types.BlkioStatEntry{
Major: re.Major,
Minor: re.Minor,
Op: re.Op,
Expand Down