From 8b454dd79e6a11c3c881f8a755423713c0491287 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Mon, 31 Aug 2015 07:55:51 -0700 Subject: [PATCH 1/2] Add a "context" to the api/server/* code This defines a 'context' object that is passed to each API handler. Right now the context just has a unique 'requestID' for each API call. The next steps would be: - use this 'requestID' in our logging. - determine the best way to format the logging to include this info. In particular for log events that generate multiple entries in the log we can use the requestID to help correlate the log entries. Adding the requestID to the logging will be a challenge since it could mean changing every single logrus.XXX() call to pass in the 'context' object. But first step is to agree on a format, which we can discus in a subsequent PR, but my initial thoughts are to add it right after the timestamp: current format: INFO[0039] POST /v1.21/build?buildargs=%7B%22foo%22%3A%22xxx%22%7D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&memory=0&memswap=0&rm=1&t=&ulimits=null proposed format: INFO[0039-83dea1222191] POST /v1.21/build?buildargs=%7B%22foo%22%3A%22xxx%22%7D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&memory=0&memswap=0&rm=1&t=&ulimits=null Signed-off-by: Doug Davis --- api/server/auth.go | 5 +++-- api/server/container.go | 41 ++++++++++++++++++++++------------------- api/server/copy.go | 11 ++++++----- api/server/daemon.go | 10 +++++++--- api/server/exec.go | 11 ++++++----- api/server/image.go | 28 ++++++++++++++++------------ api/server/inspect.go | 6 +++++- api/server/server.go | 38 +++++++++++++++++++++++++++----------- api/server/volume.go | 11 ++++++----- 9 files changed, 98 insertions(+), 63 deletions(-) diff --git a/api/server/auth.go b/api/server/auth.go index 326a0e9e40..6562b97ed0 100644 --- a/api/server/auth.go +++ b/api/server/auth.go @@ -4,12 +4,13 @@ import ( "encoding/json" "net/http" + "golang.org/x/net/context" + "github.com/docker/docker/api/types" "github.com/docker/docker/cliconfig" - "github.com/docker/docker/pkg/version" ) -func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var config *cliconfig.AuthConfig err := json.NewDecoder(r.Body).Decode(&config) r.Body.Close() diff --git a/api/server/container.go b/api/server/container.go index 08c3ed4860..36d86472f1 100644 --- a/api/server/container.go +++ b/api/server/container.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "golang.org/x/net/context" "golang.org/x/net/websocket" "github.com/Sirupsen/logrus" @@ -19,7 +20,7 @@ import ( "github.com/docker/docker/runconfig" ) -func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -48,7 +49,7 @@ func (s *Server) getContainersJSON(version version.Version, w http.ResponseWrite return writeJSON(w, http.StatusOK, containers) } -func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -89,7 +90,7 @@ func (s *Server) getContainersStats(version version.Version, w http.ResponseWrit return s.daemon.ContainerStats(container, config) } -func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -146,7 +147,7 @@ func (s *Server) getContainersLogs(version version.Version, w http.ResponseWrite return nil } -func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -154,7 +155,7 @@ func (s *Server) getContainersExport(version version.Version, w http.ResponseWri return s.daemon.ContainerExport(vars["name"], w) } -func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -190,7 +191,7 @@ func (s *Server) postContainersStart(version version.Version, w http.ResponseWri return nil } -func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -212,7 +213,7 @@ func (s *Server) postContainersStop(version version.Version, w http.ResponseWrit return nil } -func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -250,6 +251,7 @@ func (s *Server) postContainersKill(version version.Version, w http.ResponseWrit // Return error that's not caused because the container is stopped. // Return error if the container is not running and the api is >= 1.20 // to keep backwards compatibility. + version, _ := ctx.Value("api-version").(version.Version) if version.GreaterThanOrEqualTo("1.20") || !isStopped { return fmt.Errorf("Cannot kill container %s: %v", name, err) } @@ -259,7 +261,7 @@ func (s *Server) postContainersKill(version version.Version, w http.ResponseWrit return nil } -func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -278,7 +280,7 @@ func (s *Server) postContainersRestart(version version.Version, w http.ResponseW return nil } -func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -295,7 +297,7 @@ func (s *Server) postContainersPause(version version.Version, w http.ResponseWri return nil } -func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -312,7 +314,7 @@ func (s *Server) postContainersUnpause(version version.Version, w http.ResponseW return nil } -func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -327,7 +329,7 @@ func (s *Server) postContainersWait(version version.Version, w http.ResponseWrit }) } -func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -340,7 +342,7 @@ func (s *Server) getContainersChanges(version version.Version, w http.ResponseWr return writeJSON(w, http.StatusOK, changes) } -func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -357,7 +359,7 @@ func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter return writeJSON(w, http.StatusOK, procList) } -func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -374,7 +376,7 @@ func (s *Server) postContainerRename(version version.Version, w http.ResponseWri return nil } -func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -390,6 +392,7 @@ func (s *Server) postContainersCreate(version version.Version, w http.ResponseWr if err != nil { return err } + version, _ := ctx.Value("api-version").(version.Version) adjustCPUShares := version.LessThan("1.19") container, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares) @@ -403,7 +406,7 @@ func (s *Server) postContainersCreate(version version.Version, w http.ResponseWr }) } -func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -431,7 +434,7 @@ func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter return nil } -func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -451,7 +454,7 @@ func (s *Server) postContainersResize(version version.Version, w http.ResponseWr return s.daemon.ContainerResize(vars["name"], height, width) } -func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -493,7 +496,7 @@ func (s *Server) postContainersAttach(version version.Version, w http.ResponseWr return nil } -func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } diff --git a/api/server/copy.go b/api/server/copy.go index 2d26fb983a..86c4a4fabf 100644 --- a/api/server/copy.go +++ b/api/server/copy.go @@ -9,12 +9,13 @@ import ( "os" "strings" + "golang.org/x/net/context" + "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/version" ) // postContainersCopy is deprecated in favor of getContainersArchive. -func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -68,7 +69,7 @@ func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Heade return nil } -func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := archiveFormValues(r, vars) if err != nil { return err @@ -82,7 +83,7 @@ func (s *Server) headContainersArchive(version version.Version, w http.ResponseW return setContainerPathStatHeader(stat, w.Header()) } -func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := archiveFormValues(r, vars) if err != nil { return err @@ -104,7 +105,7 @@ func (s *Server) getContainersArchive(version version.Version, w http.ResponseWr return err } -func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := archiveFormValues(r, vars) if err != nil { return err diff --git a/api/server/daemon.go b/api/server/daemon.go index 97c9100374..c70b8fe646 100644 --- a/api/server/daemon.go +++ b/api/server/daemon.go @@ -8,6 +8,8 @@ import ( "strings" "time" + "golang.org/x/net/context" + "github.com/Sirupsen/logrus" "github.com/docker/docker/api" "github.com/docker/docker/api/types" @@ -20,7 +22,7 @@ import ( "github.com/docker/docker/utils" ) -func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v := &types.Version{ Version: dockerversion.VERSION, APIVersion: api.Version, @@ -31,6 +33,8 @@ func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *h BuildTime: dockerversion.BUILDTIME, } + version, _ := ctx.Value("api-version").(version.Version) + if version.GreaterThanOrEqualTo("1.19") { v.Experimental = utils.ExperimentalBuild() } @@ -42,7 +46,7 @@ func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *h return writeJSON(w, http.StatusOK, v) } -func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { info, err := s.daemon.SystemInfo() if err != nil { return err @@ -51,7 +55,7 @@ func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http return writeJSON(w, http.StatusOK, info) } -func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } diff --git a/api/server/exec.go b/api/server/exec.go index dd38c0a030..83540b6253 100644 --- a/api/server/exec.go +++ b/api/server/exec.go @@ -7,14 +7,15 @@ import ( "net/http" "strconv" + "golang.org/x/net/context" + "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/version" "github.com/docker/docker/runconfig" ) -func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter 'id'") } @@ -27,7 +28,7 @@ func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r * return writeJSON(w, http.StatusOK, eConfig) } -func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -59,7 +60,7 @@ func (s *Server) postContainerExecCreate(version version.Version, w http.Respons } // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. -func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -106,7 +107,7 @@ func (s *Server) postContainerExecStart(version version.Version, w http.Response return nil } -func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } diff --git a/api/server/image.go b/api/server/image.go index 3a751e8045..5922ba4f7f 100644 --- a/api/server/image.go +++ b/api/server/image.go @@ -8,6 +8,8 @@ import ( "net/http" "strings" + "golang.org/x/net/context" + "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" "github.com/docker/docker/builder" @@ -22,7 +24,7 @@ import ( "github.com/docker/docker/utils" ) -func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -34,6 +36,7 @@ func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *h cname := r.Form.Get("container") pause := boolValue(r, "pause") + version, _ := ctx.Value("api-version").(version.Version) if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { pause = true } @@ -64,7 +67,7 @@ func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *h } // Creates an image from Pull or from Import -func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -140,7 +143,7 @@ func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter return nil } -func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -192,7 +195,7 @@ func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, return nil } -func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -220,11 +223,11 @@ func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r return nil } -func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return s.daemon.Repositories().Load(r.Body, w) } -func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -249,7 +252,7 @@ func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r return writeJSON(w, http.StatusOK, list) } -func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -262,7 +265,7 @@ func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, return writeJSON(w, http.StatusOK, imageInspect) } -func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfigs = map[string]cliconfig.AuthConfig{} authConfigsEncoded = r.Header.Get("X-Registry-Config") @@ -280,6 +283,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht w.Header().Set("Content-Type", "application/json") + version, _ := ctx.Value("api-version").(version.Version) if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { @@ -346,7 +350,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht return nil } -func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -360,7 +364,7 @@ func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r return writeJSON(w, http.StatusOK, images) } -func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -374,7 +378,7 @@ func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter return writeJSON(w, http.StatusOK, history) } -func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -394,7 +398,7 @@ func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r return nil } -func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } diff --git a/api/server/inspect.go b/api/server/inspect.go index 41f958ebb1..d681a2a342 100644 --- a/api/server/inspect.go +++ b/api/server/inspect.go @@ -4,11 +4,13 @@ import ( "fmt" "net/http" + "golang.org/x/net/context" + "github.com/docker/docker/pkg/version" ) // getContainersByName inspects containers configuration and serializes it as json. -func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -16,6 +18,8 @@ func (s *Server) getContainersByName(version version.Version, w http.ResponseWri var json interface{} var err error + version, _ := ctx.Value("api-version").(version.Version) + switch { case version.LessThan("1.20"): json, err = s.daemon.ContainerInspectPre120(vars["name"]) diff --git a/api/server/server.go b/api/server/server.go index 1ab504da57..fa6748f4b1 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -12,12 +12,14 @@ import ( "strings" "github.com/gorilla/mux" + "golang.org/x/net/context" "github.com/Sirupsen/logrus" "github.com/docker/docker/api" "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/sockets" + "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/version" ) @@ -122,7 +124,7 @@ func (s *HTTPServer) Close() error { // HTTPAPIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints. // Any function that has the appropriate signature can be register as a API endpoint (e.g. getVersion). -type HTTPAPIFunc func(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error +type HTTPAPIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { conn, _, err := w.(http.Hijacker).Hijack() @@ -219,7 +221,7 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error { return json.NewEncoder(w).Encode(v) } -func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.WriteHeader(http.StatusOK) return nil } @@ -230,7 +232,7 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") } -func (s *Server) ping(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) ping(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { _, err := w.Write([]byte{'O', 'K'}) return err } @@ -250,6 +252,24 @@ func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) { func makeHTTPHandler(logging bool, localMethod string, localRoute string, handlerFunc HTTPAPIFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + // Define the context that we'll pass around to share info + // like the docker-request-id. + // + // The 'context' will be used for global data that should + // apply to all requests. Data that is specific to the + // immediate function being called should still be passed + // as 'args' on the function call. + + reqID := stringid.TruncateID(stringid.GenerateNonCryptoID()) + api_version := version.Version(mux.Vars(r)["version"]) + if api_version == "" { + api_version = api.Version + } + + ctx := context.Background() + ctx = context.WithValue(ctx, "docker-request-id", reqID) + ctx = context.WithValue(ctx, "api-version", api_version) + // log the request logrus.Debugf("Calling %s %s", localMethod, localRoute) @@ -270,26 +290,22 @@ func makeHTTPHandler(logging bool, localMethod string, localRoute string, handle logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) } } - version := version.Version(mux.Vars(r)["version"]) - if version == "" { - version = api.Version - } if corsHeaders != "" { writeCorsHeaders(w, r, corsHeaders) } - if version.GreaterThan(api.Version) { - http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.Version).Error(), http.StatusBadRequest) + if api_version.GreaterThan(api.Version) { + http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", api_version, api.Version).Error(), http.StatusBadRequest) return } - if version.LessThan(api.MinVersion) { + if api_version.LessThan(api.MinVersion) { http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest) return } w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")") - if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil { + if err := handlerFunc(ctx, w, r, mux.Vars(r)); err != nil { logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } diff --git a/api/server/volume.go b/api/server/volume.go index d070a33fb0..c92f0bdaab 100644 --- a/api/server/volume.go +++ b/api/server/volume.go @@ -4,11 +4,12 @@ import ( "encoding/json" "net/http" + "golang.org/x/net/context" + "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/version" ) -func (s *Server) getVolumesList(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -20,7 +21,7 @@ func (s *Server) getVolumesList(version version.Version, w http.ResponseWriter, return writeJSON(w, http.StatusOK, &types.VolumesListResponse{Volumes: volumes}) } -func (s *Server) getVolumeByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getVolumeByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -32,7 +33,7 @@ func (s *Server) getVolumeByName(version version.Version, w http.ResponseWriter, return writeJSON(w, http.StatusOK, v) } -func (s *Server) postVolumesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postVolumesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -53,7 +54,7 @@ func (s *Server) postVolumesCreate(version version.Version, w http.ResponseWrite return writeJSON(w, http.StatusCreated, volume) } -func (s *Server) deleteVolumes(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) deleteVolumes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } From f790496d8bd0930d7b1bc4adf120ae5ef169a958 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Wed, 9 Sep 2015 14:14:09 -0700 Subject: [PATCH 2/2] Move context stuff to its own package Signed-off-by: Doug Davis --- api/server/auth.go | 3 +- api/server/container.go | 7 ++--- api/server/copy.go | 3 +- api/server/daemon.go | 6 ++-- api/server/exec.go | 3 +- api/server/image.go | 8 ++---- api/server/inspect.go | 6 ++-- api/server/server.go | 18 ++++++------ api/server/volume.go | 3 +- context/context.go | 64 +++++++++++++++++++++++++++++++++++++++++ context/context_test.go | 35 ++++++++++++++++++++++ 11 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 context/context.go create mode 100644 context/context_test.go diff --git a/api/server/auth.go b/api/server/auth.go index 6562b97ed0..0373a9d2e1 100644 --- a/api/server/auth.go +++ b/api/server/auth.go @@ -4,10 +4,9 @@ import ( "encoding/json" "net/http" - "golang.org/x/net/context" - "github.com/docker/docker/api/types" "github.com/docker/docker/cliconfig" + "github.com/docker/docker/context" ) func (s *Server) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/api/server/container.go b/api/server/container.go index 36d86472f1..3af7972674 100644 --- a/api/server/container.go +++ b/api/server/container.go @@ -8,15 +8,14 @@ import ( "strings" "time" - "golang.org/x/net/context" "golang.org/x/net/websocket" "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + "github.com/docker/docker/context" "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/version" "github.com/docker/docker/runconfig" ) @@ -251,7 +250,7 @@ func (s *Server) postContainersKill(ctx context.Context, w http.ResponseWriter, // Return error that's not caused because the container is stopped. // Return error if the container is not running and the api is >= 1.20 // to keep backwards compatibility. - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() if version.GreaterThanOrEqualTo("1.20") || !isStopped { return fmt.Errorf("Cannot kill container %s: %v", name, err) } @@ -392,7 +391,7 @@ func (s *Server) postContainersCreate(ctx context.Context, w http.ResponseWriter if err != nil { return err } - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() adjustCPUShares := version.LessThan("1.19") container, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares) diff --git a/api/server/copy.go b/api/server/copy.go index 86c4a4fabf..b3d8ed320e 100644 --- a/api/server/copy.go +++ b/api/server/copy.go @@ -9,9 +9,8 @@ import ( "os" "strings" - "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/context" ) // postContainersCopy is deprecated in favor of getContainersArchive. diff --git a/api/server/daemon.go b/api/server/daemon.go index c70b8fe646..df3cba9704 100644 --- a/api/server/daemon.go +++ b/api/server/daemon.go @@ -8,17 +8,15 @@ import ( "strings" "time" - "golang.org/x/net/context" - "github.com/Sirupsen/logrus" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/autogen/dockerversion" + "github.com/docker/docker/context" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/version" "github.com/docker/docker/utils" ) @@ -33,7 +31,7 @@ func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http. BuildTime: dockerversion.BUILDTIME, } - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() if version.GreaterThanOrEqualTo("1.19") { v.Experimental = utils.ExperimentalBuild() diff --git a/api/server/exec.go b/api/server/exec.go index 83540b6253..46d46c58b7 100644 --- a/api/server/exec.go +++ b/api/server/exec.go @@ -7,10 +7,9 @@ import ( "net/http" "strconv" - "golang.org/x/net/context" - "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + "github.com/docker/docker/context" "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/runconfig" ) diff --git a/api/server/image.go b/api/server/image.go index 5922ba4f7f..6ca18be69e 100644 --- a/api/server/image.go +++ b/api/server/image.go @@ -8,18 +8,16 @@ import ( "net/http" "strings" - "golang.org/x/net/context" - "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" "github.com/docker/docker/builder" "github.com/docker/docker/cliconfig" + "github.com/docker/docker/context" "github.com/docker/docker/graph" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/ulimit" - "github.com/docker/docker/pkg/version" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" ) @@ -36,7 +34,7 @@ func (s *Server) postCommit(ctx context.Context, w http.ResponseWriter, r *http. cname := r.Form.Get("container") pause := boolValue(r, "pause") - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { pause = true } @@ -283,7 +281,7 @@ func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R w.Header().Set("Content-Type", "application/json") - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { diff --git a/api/server/inspect.go b/api/server/inspect.go index d681a2a342..782ae4865c 100644 --- a/api/server/inspect.go +++ b/api/server/inspect.go @@ -4,9 +4,7 @@ import ( "fmt" "net/http" - "golang.org/x/net/context" - - "github.com/docker/docker/pkg/version" + "github.com/docker/docker/context" ) // getContainersByName inspects containers configuration and serializes it as json. @@ -18,7 +16,7 @@ func (s *Server) getContainersByName(ctx context.Context, w http.ResponseWriter, var json interface{} var err error - version, _ := ctx.Value("api-version").(version.Version) + version := ctx.Version() switch { case version.LessThan("1.20"): diff --git a/api/server/server.go b/api/server/server.go index fa6748f4b1..f7d85138a7 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -12,11 +12,11 @@ import ( "strings" "github.com/gorilla/mux" - "golang.org/x/net/context" "github.com/Sirupsen/logrus" "github.com/docker/docker/api" "github.com/docker/docker/autogen/dockerversion" + "github.com/docker/docker/context" "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/stringid" @@ -261,14 +261,14 @@ func makeHTTPHandler(logging bool, localMethod string, localRoute string, handle // as 'args' on the function call. reqID := stringid.TruncateID(stringid.GenerateNonCryptoID()) - api_version := version.Version(mux.Vars(r)["version"]) - if api_version == "" { - api_version = api.Version + apiVersion := version.Version(mux.Vars(r)["version"]) + if apiVersion == "" { + apiVersion = api.Version } ctx := context.Background() - ctx = context.WithValue(ctx, "docker-request-id", reqID) - ctx = context.WithValue(ctx, "api-version", api_version) + ctx = context.WithValue(ctx, context.RequestID, reqID) + ctx = context.WithValue(ctx, context.APIVersion, apiVersion) // log the request logrus.Debugf("Calling %s %s", localMethod, localRoute) @@ -294,11 +294,11 @@ func makeHTTPHandler(logging bool, localMethod string, localRoute string, handle writeCorsHeaders(w, r, corsHeaders) } - if api_version.GreaterThan(api.Version) { - http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", api_version, api.Version).Error(), http.StatusBadRequest) + if apiVersion.GreaterThan(api.Version) { + http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", apiVersion, api.Version).Error(), http.StatusBadRequest) return } - if api_version.LessThan(api.MinVersion) { + if apiVersion.LessThan(api.MinVersion) { http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest) return } diff --git a/api/server/volume.go b/api/server/volume.go index c92f0bdaab..cec3b820c4 100644 --- a/api/server/volume.go +++ b/api/server/volume.go @@ -4,9 +4,8 @@ import ( "encoding/json" "net/http" - "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/context" ) func (s *Server) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/context/context.go b/context/context.go new file mode 100644 index 0000000000..3532507c3d --- /dev/null +++ b/context/context.go @@ -0,0 +1,64 @@ +package context + +import ( + "golang.org/x/net/context" + + "github.com/docker/docker/pkg/version" +) + +const ( + // RequestID is the unique ID for each http request + RequestID = "request-id" + + // APIVersion is the client's requested API version + APIVersion = "api-version" +) + +// Context is just our own wrapper for the golang 'Context' - mainly +// so we can add our over version of the funcs. +type Context struct { + context.Context +} + +// Background creates a new Context based on golang's default one. +func Background() Context { + return Context{context.Background()} +} + +// WithValue will return a Context that has this new key/value pair +// associated with it. Just uses the golang version but then wraps it. +func WithValue(ctx Context, key, value interface{}) Context { + return Context{context.WithValue(ctx, key, value)} +} + +// RequestID is a utility func to make it easier to get the +// request ID associated with this Context/request. +func (ctx Context) RequestID() string { + val := ctx.Value(RequestID) + if val == nil { + return "" + } + + id, ok := val.(string) + if !ok { + // Ideally we shouldn't panic but we also should never get here + panic("Context RequestID isn't a string") + } + return id +} + +// Version is a utility func to make it easier to get the +// API version string associated with this Context/request. +func (ctx Context) Version() version.Version { + val := ctx.Value(APIVersion) + if val == nil { + return version.Version("") + } + + ver, ok := val.(version.Version) + if !ok { + // Ideally we shouldn't panic but we also should never get here + panic("Context APIVersion isn't a version.Version") + } + return ver +} diff --git a/context/context_test.go b/context/context_test.go new file mode 100644 index 0000000000..e1ec47435b --- /dev/null +++ b/context/context_test.go @@ -0,0 +1,35 @@ +package context + +import ( + "testing" + + "github.com/docker/docker/pkg/version" +) + +func TestContext(t *testing.T) { + ctx := Background() + + // First make sure getting non-existent values doesn't break + if id := ctx.RequestID(); id != "" { + t.Fatalf("RequestID() should have been '', was: %q", id) + } + + if ver := ctx.Version(); ver != "" { + t.Fatalf("Version() should have been '', was: %q", ver) + } + + // Test basic set/get + ctx = WithValue(ctx, RequestID, "123") + if ctx.RequestID() != "123" { + t.Fatalf("RequestID() should have been '123'") + } + + // Now make sure after a 2nd set we can still get both + ctx = WithValue(ctx, APIVersion, version.Version("x.y")) + if id := ctx.RequestID(); id != "123" { + t.Fatalf("RequestID() should have been '123', was %q", id) + } + if ver := ctx.Version(); ver != "x.y" { + t.Fatalf("Version() should have been 'x.y', was %q", ver) + } +}