Browse Source

Merge pull request #10986 from crosbymichael/create-api-type

Create api types package for structured responses
Jessie Frazelle 10 years ago
parent
commit
2bfa701f45
6 changed files with 71 additions and 68 deletions
  1. 22 39
      api/client/commands.go
  2. 23 14
      api/server/server.go
  3. 1 1
      api/types/stats.go
  4. 11 0
      api/types/types.go
  5. 12 12
      daemon/stats.go
  6. 2 2
      integration-cli/docker_api_containers_test.go

+ 22 - 39
api/client/commands.go

@@ -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"
@@ -2133,7 +2133,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)
@@ -2172,23 +2172,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 {
@@ -2207,14 +2203,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
 }
 
@@ -2272,38 +2265,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.")
@@ -2311,7 +2298,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
@@ -2319,7 +2305,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
@@ -2336,14 +2321,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:
@@ -2360,12 +2343,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)
 		}
 	}
@@ -2390,26 +2373,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
 			}
 		}
@@ -2653,7 +2636,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
@@ -2770,7 +2753,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

+ 23 - 14
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
@@ -526,7 +537,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
@@ -705,18 +716,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
 	}
@@ -732,10 +741,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 {
@@ -878,7 +887,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 {
@@ -1149,7 +1158,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.

+ 1 - 1
api/stats/stats.go → api/types/stats.go

@@ -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"
 

+ 11 - 0
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"`
+}

+ 12 - 12
daemon/stats.go

@@ -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"
@@ -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,
@@ -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),
@@ -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,
@@ -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,

+ 2 - 2
integration-cli/docker_api_containers_test.go

@@ -9,7 +9,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/docker/docker/api/stats"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
 )
 
@@ -311,7 +311,7 @@ func TestGetContainerStats(t *testing.T) {
 		}
 
 		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
-		var s *stats.Stats
+		var s *types.Stats
 		// decode only one object from the stream
 		if err := dec.Decode(&s); err != nil {
 			t.Fatal(err)