Separate API router from server.

Implement basic interfaces to write custom routers that can be plugged
to the server. Remove server coupling with the daemon.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2015-09-23 19:42:08 -04:00
parent 144abf2a58
commit da982cf551
32 changed files with 809 additions and 591 deletions

View file

@ -1,57 +0,0 @@
package server
import (
"fmt"
"net/http"
"path/filepath"
"strconv"
"strings"
)
func boolValue(r *http.Request, k string) bool {
s := strings.ToLower(strings.TrimSpace(r.FormValue(k)))
return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
}
// boolValueOrDefault returns the default bool passed if the query param is
// missing, otherwise it's just a proxy to boolValue above
func boolValueOrDefault(r *http.Request, k string, d bool) bool {
if _, ok := r.Form[k]; !ok {
return d
}
return boolValue(r, k)
}
func int64ValueOrZero(r *http.Request, k string) int64 {
val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
if err != nil {
return 0
}
return val
}
type archiveOptions struct {
name string
path string
}
func archiveFormValues(r *http.Request, vars map[string]string) (archiveOptions, error) {
if vars == nil {
return archiveOptions{}, fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
return archiveOptions{}, err
}
name := vars["name"]
path := filepath.FromSlash(r.Form.Get("path"))
switch {
case name == "":
return archiveOptions{}, fmt.Errorf("bad parameter: 'name' cannot be empty")
case path == "":
return archiveOptions{}, fmt.Errorf("bad parameter: 'path' cannot be empty")
}
return archiveOptions{name, path}, nil
}

View file

@ -0,0 +1,63 @@
package httputils
import (
"fmt"
"net/http"
"path/filepath"
"strconv"
"strings"
)
// BoolValue transforms a form value in different formats into a boolean type.
func BoolValue(r *http.Request, k string) bool {
s := strings.ToLower(strings.TrimSpace(r.FormValue(k)))
return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
}
// BoolValueOrDefault returns the default bool passed if the query param is
// missing, otherwise it's just a proxy to boolValue above
func BoolValueOrDefault(r *http.Request, k string, d bool) bool {
if _, ok := r.Form[k]; !ok {
return d
}
return BoolValue(r, k)
}
// Int64ValueOrZero parses a form value into a int64 type.
// It returns 0 if the parsing fails.
func Int64ValueOrZero(r *http.Request, k string) int64 {
val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
if err != nil {
return 0
}
return val
}
// ArchiveOptions stores archive information for different operations.
type ArchiveOptions struct {
Name string
Path string
}
// ArchiveFormValues parses form values and turns them into ArchiveOptions.
// It fails if the archive name and path are not in the request.
func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions, error) {
if vars == nil {
return ArchiveOptions{}, fmt.Errorf("Missing parameter")
}
if err := ParseForm(r); err != nil {
return ArchiveOptions{}, err
}
name := vars["name"]
path := filepath.FromSlash(r.Form.Get("path"))
switch {
case name == "":
return ArchiveOptions{}, fmt.Errorf("bad parameter: 'name' cannot be empty")
case path == "":
return ArchiveOptions{}, fmt.Errorf("bad parameter: 'path' cannot be empty")
}
return ArchiveOptions{name, path}, nil
}

View file

@ -1,4 +1,4 @@
package server
package httputils
import (
"net/http"
@ -26,7 +26,7 @@ func TestBoolValue(t *testing.T) {
r, _ := http.NewRequest("POST", "", nil)
r.Form = v
a := boolValue(r, "test")
a := BoolValue(r, "test")
if a != e {
t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
}
@ -35,7 +35,7 @@ func TestBoolValue(t *testing.T) {
func TestBoolValueOrDefault(t *testing.T) {
r, _ := http.NewRequest("GET", "", nil)
if !boolValueOrDefault(r, "queryparam", true) {
if !BoolValueOrDefault(r, "queryparam", true) {
t.Fatal("Expected to get true default value, got false")
}
@ -43,7 +43,7 @@ func TestBoolValueOrDefault(t *testing.T) {
v.Set("param", "")
r, _ = http.NewRequest("GET", "", nil)
r.Form = v
if boolValueOrDefault(r, "param", true) {
if BoolValueOrDefault(r, "param", true) {
t.Fatal("Expected not to get true")
}
}
@ -62,7 +62,7 @@ func TestInt64ValueOrZero(t *testing.T) {
r, _ := http.NewRequest("POST", "", nil)
r.Form = v
a := int64ValueOrZero(r, "test")
a := Int64ValueOrZero(r, "test")
if a != e {
t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
}

View file

@ -0,0 +1,180 @@
package httputils
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/api"
"github.com/docker/docker/pkg/version"
"github.com/docker/docker/utils"
)
// APIVersionKey is the client's requested API version.
const APIVersionKey = "api-version"
// APIFunc 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 APIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
// HijackConnection interrupts the http response writer to get the
// underlying connection and operate with it.
func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
return nil, nil, err
}
// Flush the options to make sure the client sets the raw mode
conn.Write([]byte{})
return conn, conn, nil
}
// CloseStreams ensures that a list for http streams are properly closed.
func CloseStreams(streams ...interface{}) {
for _, stream := range streams {
if tcpc, ok := stream.(interface {
CloseWrite() error
}); ok {
tcpc.CloseWrite()
} else if closer, ok := stream.(io.Closer); ok {
closer.Close()
}
}
}
// CheckForJSON makes sure that the request's Content-Type is application/json.
func CheckForJSON(r *http.Request) error {
ct := r.Header.Get("Content-Type")
// No Content-Type header is ok as long as there's no Body
if ct == "" {
if r.Body == nil || r.ContentLength == 0 {
return nil
}
}
// Otherwise it better be json
if api.MatchesContentType(ct, "application/json") {
return nil
}
return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
}
// ParseForm ensures the request form is parsed even with invalid content types.
// If we don't do this, POST method without Content-type (even with empty body) will fail.
func ParseForm(r *http.Request) error {
if r == nil {
return nil
}
if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
return err
}
return nil
}
// ParseMultipartForm ensure the request form is parsed, even with invalid content types.
func ParseMultipartForm(r *http.Request) error {
if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
return err
}
return nil
}
// WriteError decodes a specific docker error and sends it in the response.
func WriteError(w http.ResponseWriter, err error) {
if err == nil || w == nil {
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
return
}
statusCode := http.StatusInternalServerError
errMsg := err.Error()
// Based on the type of error we get we need to process things
// slightly differently to extract the error message.
// In the 'errcode.*' cases there are two different type of
// error that could be returned. errocode.ErrorCode is the base
// type of error object - it is just an 'int' that can then be
// used as the look-up key to find the message. errorcode.Error
// extends errorcode.Error by adding error-instance specific
// data, like 'details' or variable strings to be inserted into
// the message.
//
// Ideally, we should just be able to call err.Error() for all
// cases but the errcode package doesn't support that yet.
//
// Additionally, in both errcode cases, there might be an http
// status code associated with it, and if so use it.
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
if statusCode == 0 {
statusCode = http.StatusInternalServerError
}
logrus.WithFields(logrus.Fields{"statusCode": statusCode, "err": utils.GetErrorMessage(err)}).Error("HTTP Error")
http.Error(w, errMsg, statusCode)
}
// 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)
}
// VersionFromContext returns an API version from the context using APIVersionKey.
// It panics if the context value does not have version.Version type.
func VersionFromContext(ctx context.Context) (ver version.Version) {
if ctx == nil {
return
}
val := ctx.Value(APIVersionKey)
if val == nil {
return
}
return val.(version.Version)
}

View file

@ -7,21 +7,19 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/errors"
"github.com/docker/docker/pkg/version"
"golang.org/x/net/context"
)
// apiVersionKey is the client's requested API version.
const apiVersionKey = "api-version"
// middleware is an adapter to allow the use of ordinary functions as Docker API filters.
// Any function that has the appropriate signature can be register as a middleware.
type middleware func(handler HTTPAPIFunc) HTTPAPIFunc
type middleware func(handler httputils.APIFunc) httputils.APIFunc
// loggingMiddleware logs each request when logging is enabled.
func (s *Server) loggingMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
func (s *Server) loggingMiddleware(handler httputils.APIFunc) httputils.APIFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if s.cfg.Logging {
logrus.Infof("%s %s", r.Method, r.RequestURI)
@ -31,7 +29,7 @@ func (s *Server) loggingMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
}
// userAgentMiddleware checks the User-Agent header looking for a valid docker client spec.
func (s *Server) userAgentMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
func (s *Server) userAgentMiddleware(handler httputils.APIFunc) httputils.APIFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
dockerVersion := version.Version(s.cfg.Version)
@ -53,7 +51,7 @@ func (s *Server) userAgentMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
}
// corsMiddleware sets the CORS header expectations in the server.
func (s *Server) corsMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
func (s *Server) corsMiddleware(handler httputils.APIFunc) httputils.APIFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
// otherwise, all head values will be passed to HTTP handler
@ -70,7 +68,7 @@ func (s *Server) corsMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
}
// versionMiddleware checks the api version requirements before passing the request to the server handler.
func versionMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
func versionMiddleware(handler httputils.APIFunc) httputils.APIFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
apiVersion := version.Version(vars["version"])
if apiVersion == "" {
@ -85,7 +83,7 @@ func versionMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
}
w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
ctx = context.WithValue(ctx, apiVersionKey, apiVersion)
ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
return handler(ctx, w, r, vars)
}
}
@ -103,7 +101,8 @@ func versionMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
// )
// )
// )
func (s *Server) handleWithGlobalMiddlewares(handler HTTPAPIFunc) HTTPAPIFunc {
// )
func (s *Server) handleWithGlobalMiddlewares(handler httputils.APIFunc) httputils.APIFunc {
middlewares := []middleware{
versionMiddleware,
s.corsMiddleware,
@ -117,16 +116,3 @@ func (s *Server) handleWithGlobalMiddlewares(handler HTTPAPIFunc) HTTPAPIFunc {
}
return h
}
// versionFromContext returns an API version from the context using apiVersionKey.
// It panics if the context value does not have version.Version type.
func versionFromContext(ctx context.Context) (ver version.Version) {
if ctx == nil {
return
}
val := ctx.Value(apiVersionKey)
if val == nil {
return
}
return val.(version.Version)
}

View file

@ -6,13 +6,14 @@ import (
"testing"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/errors"
"golang.org/x/net/context"
)
func TestVersionMiddleware(t *testing.T) {
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if versionFromContext(ctx) == "" {
if httputils.VersionFromContext(ctx) == "" {
t.Fatalf("Expected version, got empty string")
}
return nil
@ -30,7 +31,7 @@ func TestVersionMiddleware(t *testing.T) {
func TestVersionMiddlewareWithErrors(t *testing.T) {
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if versionFromContext(ctx) == "" {
if httputils.VersionFromContext(ctx) == "" {
t.Fatalf("Expected version, got empty string")
}
return nil

View file

@ -1,15 +1,16 @@
package server
package local
import (
"encoding/json"
"net/http"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cliconfig"
"golang.org/x/net/context"
)
func (s *Server) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) 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()
@ -20,7 +21,7 @@ func (s *Server) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Re
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, &types.AuthResponse{
return httputils.WriteJSON(w, http.StatusOK, &types.AuthResponse{
Status: status,
})
}

View file

@ -1,4 +1,4 @@
package server
package local
import (
"fmt"
@ -11,6 +11,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/daemon"
derr "github.com/docker/docker/errors"
@ -22,14 +23,14 @@ import (
"golang.org/x/net/websocket"
)
func (s *Server) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
config := &daemon.ContainersConfig{
All: boolValue(r, "all"),
Size: boolValue(r, "size"),
All: httputils.BoolValue(r, "all"),
Size: httputils.BoolValue(r, "size"),
Since: r.Form.Get("since"),
Before: r.Form.Get("before"),
Filters: r.Form.Get("filters"),
@ -48,18 +49,18 @@ func (s *Server) getContainersJSON(ctx context.Context, w http.ResponseWriter, r
return err
}
return writeJSON(w, http.StatusOK, containers)
return httputils.WriteJSON(w, http.StatusOK, containers)
}
func (s *Server) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
return fmt.Errorf("Missing parameter")
}
stream := boolValueOrDefault(r, "stream", true)
stream := httputils.BoolValueOrDefault(r, "stream", true)
var out io.Writer
if !stream {
w.Header().Set("Content-Type", "application/json")
@ -77,14 +78,14 @@ func (s *Server) getContainersStats(ctx context.Context, w http.ResponseWriter,
Stream: stream,
OutStream: out,
Stop: closeNotifier,
Version: versionFromContext(ctx),
Version: httputils.VersionFromContext(ctx),
}
return s.daemon.ContainerStats(vars["name"], config)
}
func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -96,7 +97,7 @@ func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
// daemon is going to stream. By sending this initial HTTP 200 we can't report
// any error after the stream starts (i.e. container not found, wrong parameters)
// with the appropriate status code.
stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
if !(stdout || stderr) {
return fmt.Errorf("Bad parameters: you must choose at least one stream")
}
@ -127,8 +128,8 @@ func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
outStream.Write(nil)
logsConfig := &daemon.ContainerLogsConfig{
Follow: boolValue(r, "follow"),
Timestamps: boolValue(r, "timestamps"),
Follow: httputils.BoolValue(r, "follow"),
Timestamps: httputils.BoolValue(r, "timestamps"),
Since: since,
Tail: r.Form.Get("tail"),
UseStdout: stdout,
@ -147,7 +148,7 @@ func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
return nil
}
func (s *Server) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -155,7 +156,7 @@ func (s *Server) getContainersExport(ctx context.Context, w http.ResponseWriter,
return s.daemon.ContainerExport(vars["name"], w)
}
func (s *Server) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -168,7 +169,7 @@ func (s *Server) postContainersStart(ctx context.Context, w http.ResponseWriter,
// allow a nil body for backwards compatibility
var hostConfig *runconfig.HostConfig
if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
@ -187,8 +188,8 @@ func (s *Server) postContainersStart(ctx context.Context, w http.ResponseWriter,
return nil
}
func (s *Server) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -205,11 +206,11 @@ func (s *Server) postContainersStop(ctx context.Context, w http.ResponseWriter,
return nil
}
func (s *Server) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -231,7 +232,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 := versionFromContext(ctx)
version := httputils.VersionFromContext(ctx)
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
return fmt.Errorf("Cannot kill container %s: %v", name, err)
}
@ -241,8 +242,8 @@ func (s *Server) postContainersKill(ctx context.Context, w http.ResponseWriter,
return nil
}
func (s *Server) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -260,11 +261,11 @@ func (s *Server) postContainersRestart(ctx context.Context, w http.ResponseWrite
return nil
}
func (s *Server) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -277,11 +278,11 @@ func (s *Server) postContainersPause(ctx context.Context, w http.ResponseWriter,
return nil
}
func (s *Server) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -294,7 +295,7 @@ func (s *Server) postContainersUnpause(ctx context.Context, w http.ResponseWrite
return nil
}
func (s *Server) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -304,12 +305,12 @@ func (s *Server) postContainersWait(ctx context.Context, w http.ResponseWriter,
return err
}
return writeJSON(w, http.StatusOK, &types.ContainerWaitResponse{
return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
StatusCode: status,
})
}
func (s *Server) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -319,15 +320,15 @@ func (s *Server) getContainersChanges(ctx context.Context, w http.ResponseWriter
return err
}
return writeJSON(w, http.StatusOK, changes)
return httputils.WriteJSON(w, http.StatusOK, changes)
}
func (s *Server) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -336,11 +337,11 @@ func (s *Server) getContainersTop(ctx context.Context, w http.ResponseWriter, r
return err
}
return writeJSON(w, http.StatusOK, procList)
return httputils.WriteJSON(w, http.StatusOK, procList)
}
func (s *Server) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -356,11 +357,11 @@ func (s *Server) postContainerRename(ctx context.Context, w http.ResponseWriter,
return nil
}
func (s *Server) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
@ -370,7 +371,7 @@ func (s *Server) postContainersCreate(ctx context.Context, w http.ResponseWriter
if err != nil {
return err
}
version := versionFromContext(ctx)
version := httputils.VersionFromContext(ctx)
adjustCPUShares := version.LessThan("1.19")
ccr, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares)
@ -378,11 +379,11 @@ func (s *Server) postContainersCreate(ctx context.Context, w http.ResponseWriter
return err
}
return writeJSON(w, http.StatusCreated, ccr)
return httputils.WriteJSON(w, http.StatusCreated, ccr)
}
func (s *Server) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -391,9 +392,9 @@ func (s *Server) deleteContainers(ctx context.Context, w http.ResponseWriter, r
name := vars["name"]
config := &daemon.ContainerRmConfig{
ForceRemove: boolValue(r, "force"),
RemoveVolume: boolValue(r, "v"),
RemoveLink: boolValue(r, "link"),
ForceRemove: httputils.BoolValue(r, "force"),
RemoveVolume: httputils.BoolValue(r, "v"),
RemoveLink: httputils.BoolValue(r, "link"),
}
if err := s.daemon.ContainerRm(name, config); err != nil {
@ -409,8 +410,8 @@ func (s *Server) deleteContainers(ctx context.Context, w http.ResponseWriter, r
return nil
}
func (s *Server) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -429,8 +430,8 @@ func (s *Server) postContainersResize(ctx context.Context, w http.ResponseWriter
return s.daemon.ContainerResize(vars["name"], height, width)
}
func (s *Server) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -442,11 +443,11 @@ func (s *Server) postContainersAttach(ctx context.Context, w http.ResponseWriter
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
}
inStream, outStream, err := hijackServer(w)
inStream, outStream, err := httputils.HijackConnection(w)
if err != nil {
return err
}
defer closeStreams(inStream, outStream)
defer httputils.CloseStreams(inStream, outStream)
if _, ok := r.Header["Upgrade"]; ok {
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
@ -457,11 +458,11 @@ func (s *Server) postContainersAttach(ctx context.Context, w http.ResponseWriter
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
InStream: inStream,
OutStream: outStream,
UseStdin: boolValue(r, "stdin"),
UseStdout: boolValue(r, "stdout"),
UseStderr: boolValue(r, "stderr"),
Logs: boolValue(r, "logs"),
Stream: boolValue(r, "stream"),
UseStdin: httputils.BoolValue(r, "stdin"),
UseStdout: httputils.BoolValue(r, "stdout"),
UseStderr: httputils.BoolValue(r, "stderr"),
Logs: httputils.BoolValue(r, "logs"),
Stream: httputils.BoolValue(r, "stream"),
}
if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
@ -471,8 +472,8 @@ func (s *Server) postContainersAttach(ctx context.Context, w http.ResponseWriter
return nil
}
func (s *Server) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -491,8 +492,8 @@ func (s *Server) wsContainersAttach(ctx context.Context, w http.ResponseWriter,
InStream: ws,
OutStream: ws,
ErrStream: ws,
Logs: boolValue(r, "logs"),
Stream: boolValue(r, "stream"),
Logs: httputils.BoolValue(r, "logs"),
Stream: httputils.BoolValue(r, "stream"),
}
if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {

View file

@ -1,4 +1,4 @@
package server
package local
import (
"encoding/base64"
@ -9,17 +9,18 @@ import (
"os"
"strings"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"golang.org/x/net/context"
)
// postContainersCopy is deprecated in favor of getContainersArchive.
func (s *Server) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
@ -68,13 +69,13 @@ func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Heade
return nil
}
func (s *Server) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := archiveFormValues(r, vars)
func (s *router) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := httputils.ArchiveFormValues(r, vars)
if err != nil {
return err
}
stat, err := s.daemon.ContainerStatPath(v.name, v.path)
stat, err := s.daemon.ContainerStatPath(v.Name, v.Path)
if err != nil {
return err
}
@ -82,13 +83,13 @@ func (s *Server) headContainersArchive(ctx context.Context, w http.ResponseWrite
return setContainerPathStatHeader(stat, w.Header())
}
func (s *Server) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := archiveFormValues(r, vars)
func (s *router) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := httputils.ArchiveFormValues(r, vars)
if err != nil {
return err
}
tarArchive, stat, err := s.daemon.ContainerArchivePath(v.name, v.path)
tarArchive, stat, err := s.daemon.ContainerArchivePath(v.Name, v.Path)
if err != nil {
return err
}
@ -104,12 +105,12 @@ func (s *Server) getContainersArchive(ctx context.Context, w http.ResponseWriter
return err
}
func (s *Server) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := archiveFormValues(r, vars)
func (s *router) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v, err := httputils.ArchiveFormValues(r, vars)
if err != nil {
return err
}
noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
return s.daemon.ContainerExtractToDir(v.name, v.path, noOverwriteDirNonDir, r.Body)
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
return s.daemon.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
}

View file

@ -1,4 +1,4 @@
package server
package local
import (
"encoding/json"
@ -8,13 +8,14 @@ import (
"strconv"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/runconfig"
"golang.org/x/net/context"
)
func (s *Server) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter 'id'")
}
@ -24,14 +25,14 @@ func (s *Server) getExecByID(ctx context.Context, w http.ResponseWriter, r *http
return err
}
return writeJSON(w, http.StatusOK, eConfig)
return httputils.WriteJSON(w, http.StatusOK, eConfig)
}
func (s *Server) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
name := vars["name"]
@ -53,14 +54,14 @@ func (s *Server) postContainerExecCreate(ctx context.Context, w http.ResponseWri
return err
}
return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
ID: id,
})
}
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
func (s *Server) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
var (
@ -77,11 +78,11 @@ func (s *Server) postContainerExecStart(ctx context.Context, w http.ResponseWrit
if !execStartCheck.Detach {
var err error
// Setting up the streaming http interface.
inStream, outStream, err = hijackServer(w)
inStream, outStream, err = httputils.HijackConnection(w)
if err != nil {
return err
}
defer closeStreams(inStream, outStream)
defer httputils.CloseStreams(inStream, outStream)
if _, ok := r.Header["Upgrade"]; ok {
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
@ -106,8 +107,8 @@ func (s *Server) postContainerExecStart(ctx context.Context, w http.ResponseWrit
return nil
}
func (s *Server) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {

View file

@ -1,4 +1,4 @@
package server
package local
import (
"encoding/base64"
@ -10,6 +10,7 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/builder"
"github.com/docker/docker/cliconfig"
@ -23,19 +24,19 @@ import (
"golang.org/x/net/context"
)
func (s *Server) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
cname := r.Form.Get("container")
pause := boolValue(r, "pause")
version := versionFromContext(ctx)
pause := httputils.BoolValue(r, "pause")
version := httputils.VersionFromContext(ctx)
if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
pause = true
}
@ -60,14 +61,14 @@ func (s *Server) postCommit(ctx context.Context, w http.ResponseWriter, r *http.
return err
}
return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
ID: imgID,
})
}
// Creates an image from Pull or from Import
func (s *Server) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -142,7 +143,7 @@ func (s *Server) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
return nil
}
func (s *Server) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -153,7 +154,7 @@ func (s *Server) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
metaHeaders[k] = v
}
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
authConfig := &cliconfig.AuthConfig{}
@ -194,11 +195,11 @@ func (s *Server) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
return nil
}
func (s *Server) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -222,12 +223,12 @@ func (s *Server) getImagesGet(ctx context.Context, w http.ResponseWriter, r *htt
return nil
}
func (s *Server) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) 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(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -240,18 +241,18 @@ func (s *Server) deleteImages(ctx context.Context, w http.ResponseWriter, r *htt
return fmt.Errorf("image name cannot be blank")
}
force := boolValue(r, "force")
prune := !boolValue(r, "noprune")
force := httputils.BoolValue(r, "force")
prune := !httputils.BoolValue(r, "noprune")
list, err := s.daemon.ImageDelete(name, force, prune)
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, list)
return httputils.WriteJSON(w, http.StatusOK, list)
}
func (s *Server) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -261,10 +262,10 @@ func (s *Server) getImagesByName(ctx context.Context, w http.ResponseWriter, r *
return err
}
return writeJSON(w, http.StatusOK, imageInspect)
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
}
func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) 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")
@ -282,15 +283,15 @@ func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
w.Header().Set("Content-Type", "application/json")
version := versionFromContext(ctx)
if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
version := httputils.VersionFromContext(ctx)
if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
buildConfig.Remove = true
} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
buildConfig.Remove = true
} else {
buildConfig.Remove = boolValue(r, "rm")
buildConfig.Remove = httputils.BoolValue(r, "rm")
}
if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
buildConfig.Pull = true
}
@ -301,15 +302,15 @@ func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
buildConfig.RemoteURL = r.FormValue("remote")
buildConfig.DockerfileName = r.FormValue("dockerfile")
buildConfig.RepoName = r.FormValue("t")
buildConfig.SuppressOutput = boolValue(r, "q")
buildConfig.NoCache = boolValue(r, "nocache")
buildConfig.ForceRemove = boolValue(r, "forcerm")
buildConfig.SuppressOutput = httputils.BoolValue(r, "q")
buildConfig.NoCache = httputils.BoolValue(r, "nocache")
buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm")
buildConfig.AuthConfigs = authConfigs
buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
buildConfig.Memory = int64ValueOrZero(r, "memory")
buildConfig.CPUShares = int64ValueOrZero(r, "cpushares")
buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod")
buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota")
buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory")
buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
buildConfig.CPUSetCpus = r.FormValue("cpusetcpus")
buildConfig.CPUSetMems = r.FormValue("cpusetmems")
buildConfig.CgroupParent = r.FormValue("cgroupparent")
@ -358,21 +359,21 @@ func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
return nil
}
func (s *Server) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
// FIXME: The filter parameter could just be a match filter
images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all"))
images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, images)
return httputils.WriteJSON(w, http.StatusOK, images)
}
func (s *Server) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -383,11 +384,11 @@ func (s *Server) getImagesHistory(ctx context.Context, w http.ResponseWriter, r
return err
}
return writeJSON(w, http.StatusOK, history)
return httputils.WriteJSON(w, http.StatusOK, history)
}
func (s *Server) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if vars == nil {
@ -396,7 +397,7 @@ func (s *Server) postImagesTag(ctx context.Context, w http.ResponseWriter, r *ht
repo := r.Form.Get("repo")
tag := r.Form.Get("tag")
force := boolValue(r, "force")
force := httputils.BoolValue(r, "force")
name := vars["name"]
if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil {
return err
@ -406,8 +407,8 @@ func (s *Server) postImagesTag(ctx context.Context, w http.ResponseWriter, r *ht
return nil
}
func (s *Server) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
var (
@ -433,5 +434,5 @@ func (s *Server) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, query.Results)
return httputils.WriteJSON(w, http.StatusOK, query.Results)
}

View file

@ -1,4 +1,4 @@
package server
package local
import (
"encoding/json"
@ -10,6 +10,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/pkg/ioutils"
@ -20,7 +21,7 @@ import (
"golang.org/x/net/context"
)
func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) 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,7 +32,7 @@ func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http.
BuildTime: dockerversion.BUILDTIME,
}
version := versionFromContext(ctx)
version := httputils.VersionFromContext(ctx)
if version.GreaterThanOrEqualTo("1.19") {
v.Experimental = utils.ExperimentalBuild()
@ -41,20 +42,20 @@ func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http.
v.KernelVersion = kernelVersion.String()
}
return writeJSON(w, http.StatusOK, v)
return httputils.WriteJSON(w, http.StatusOK, v)
}
func (s *Server) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) 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
}
return writeJSON(w, http.StatusOK, info)
return httputils.WriteJSON(w, http.StatusOK, info)
}
func (s *Server) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
var since int64 = -1

View file

@ -1,14 +1,15 @@
package server
package local
import (
"fmt"
"net/http"
"github.com/docker/docker/api/server/httputils"
"golang.org/x/net/context"
)
// getContainersByName inspects containers configuration and serializes it as json.
func (s *Server) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
@ -16,7 +17,7 @@ func (s *Server) getContainersByName(ctx context.Context, w http.ResponseWriter,
var json interface{}
var err error
version := versionFromContext(ctx)
version := httputils.VersionFromContext(ctx)
switch {
case version.LessThan("1.20"):
@ -31,5 +32,5 @@ func (s *Server) getContainersByName(ctx context.Context, w http.ResponseWriter,
return err
}
return writeJSON(w, http.StatusOK, json)
return httputils.WriteJSON(w, http.StatusOK, json)
}

View file

@ -0,0 +1,161 @@
package local
import (
"net/http"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/server/httputils"
dkrouter "github.com/docker/docker/api/server/router"
"github.com/docker/docker/daemon"
"github.com/gorilla/mux"
)
// router is a docker router that talks with the local docker daemon.
type router struct {
daemon *daemon.Daemon
routes []dkrouter.Route
}
// localRoute defines an individual API route to connect with the docker daemon.
// It implements router.Route.
type localRoute struct {
method string
path string
handler httputils.APIFunc
}
// Handler returns the APIFunc to let the server wrap it in middlewares
func (l localRoute) Handler() httputils.APIFunc {
return l.handler
}
// Register adds the filtered handler to the mux.
func (l localRoute) Register(m *mux.Router, handler http.Handler) {
logrus.Debugf("Registering %s, %s", l.method, l.path)
m.Path(dkrouter.VersionMatcher + l.path).Methods(l.method).Handler(handler)
m.Path(l.path).Methods(l.method).Handler(handler)
}
// NewRoute initialies a new local route for the reouter
func NewRoute(method, path string, handler httputils.APIFunc) dkrouter.Route {
return localRoute{method, path, handler}
}
// NewGetRoute initializes a new route with the http method GET.
func NewGetRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("GET", path, handler)
}
// NewPostRoute initializes a new route with the http method POST.
func NewPostRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("POST", path, handler)
}
// NewPutRoute initializes a new route with the http method PUT.
func NewPutRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("PUT", path, handler)
}
// NewDeleteRoute initializes a new route with the http method DELETE.
func NewDeleteRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("DELETE", path, handler)
}
// NewOptionsRoute initializes a new route with the http method OPTIONS
func NewOptionsRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("OPTIONS", path, handler)
}
// NewHeadRoute initializes a new route with the http method HEAD.
func NewHeadRoute(path string, handler httputils.APIFunc) dkrouter.Route {
return NewRoute("HEAD", path, handler)
}
// NewRouter initializes a local router with a new daemon.
func NewRouter(daemon *daemon.Daemon) dkrouter.Router {
r := &router{
daemon: daemon,
}
r.initRoutes()
return r
}
// Routes returns the list of routes registered in the router.
func (r *router) Routes() []dkrouter.Route {
return r.routes
}
// initRoutes initializes the routes in this router
func (r *router) initRoutes() {
r.routes = []dkrouter.Route{
// HEAD
NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
// OPTIONS
NewOptionsRoute("/", optionsHandler),
// GET
NewGetRoute("/_ping", pingHandler),
NewGetRoute("/events", r.getEvents),
NewGetRoute("/info", r.getInfo),
NewGetRoute("/version", r.getVersion),
NewGetRoute("/images/json", r.getImagesJSON),
NewGetRoute("/images/search", r.getImagesSearch),
NewGetRoute("/images/get", r.getImagesGet),
NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
NewGetRoute("/containers/json", r.getContainersJSON),
NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
NewGetRoute("/volumes", r.getVolumesList),
NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
// POST
NewPostRoute("/auth", r.postAuth),
NewPostRoute("/commit", r.postCommit),
NewPostRoute("/build", r.postBuild),
NewPostRoute("/images/create", r.postImagesCreate),
NewPostRoute("/images/load", r.postImagesLoad),
NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
NewPostRoute("/containers/create", r.postContainersCreate),
NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
NewPostRoute("/volumes", r.postVolumesCreate),
// PUT
NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
// DELETE
NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
NewDeleteRoute("/images/{name:.*}", r.deleteImages),
NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
}
}
func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
w.WriteHeader(http.StatusOK)
return nil
}
func pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
_, err := w.Write([]byte{'O', 'K'})
return err
}

View file

@ -1,15 +1,16 @@
package server
package local
import (
"encoding/json"
"net/http"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"golang.org/x/net/context"
)
func (s *Server) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -17,11 +18,11 @@ func (s *Server) getVolumesList(ctx context.Context, w http.ResponseWriter, r *h
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, &types.VolumesListResponse{Volumes: volumes})
return httputils.WriteJSON(w, http.StatusOK, &types.VolumesListResponse{Volumes: volumes})
}
func (s *Server) getVolumeByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) getVolumeByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
@ -29,15 +30,15 @@ func (s *Server) getVolumeByName(ctx context.Context, w http.ResponseWriter, r *
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, v)
return httputils.WriteJSON(w, http.StatusOK, v)
}
func (s *Server) postVolumesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) postVolumesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := checkForJSON(r); err != nil {
if err := httputils.CheckForJSON(r); err != nil {
return err
}
@ -50,11 +51,11 @@ func (s *Server) postVolumesCreate(ctx context.Context, w http.ResponseWriter, r
if err != nil {
return err
}
return writeJSON(w, http.StatusCreated, volume)
return httputils.WriteJSON(w, http.StatusCreated, volume)
}
func (s *Server) deleteVolumes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
func (s *router) deleteVolumes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := s.daemon.VolumeRm(vars["name"]); err != nil {

View file

@ -0,0 +1,26 @@
package network
import (
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/server/router"
)
// networkRouter is a router to talk with the network controller
type networkRouter struct {
routes []router.Route
}
// Routes returns the available routes to the network controller
func (n networkRouter) Routes() []router.Route {
return n.routes
}
type networkRoute struct {
path string
handler httputils.APIFunc
}
// Handler returns the APIFunc to let the server wrap it in middlewares
func (l networkRoute) Handler() httputils.APIFunc {
return l.handler
}

View file

@ -0,0 +1,51 @@
// +build experimental
package network
import (
"net/http"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/daemon"
"github.com/docker/libnetwork/api"
"github.com/gorilla/mux"
)
var httpMethods = []string{"GET", "POST", "PUT", "DELETE"}
// NewRouter initializes a new network router
func NewRouter(d *daemon.Daemon) router.Router {
c := d.NetworkController()
if c == nil {
return networkRouter{}
}
var routes []router.Route
netHandler := api.NewHTTPHandler(c)
// TODO: libnetwork should stop hijacking request/response.
// It should define API functions to add normally to the router.
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
netHandler(w, r)
return nil
}
for _, path := range []string{"/networks", "/services", "/sandboxes"} {
routes = append(routes, networkRoute{path, handler})
}
return networkRouter{routes}
}
// Register adds the filtered handler to the mux.
func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
logrus.Debugf("Registering %s, %v", n.path, httpMethods)
subrouter := m.PathPrefix(router.VersionMatcher + n.path).Subrouter()
subrouter.Methods(httpMethods...).Handler(handler)
subrouter = m.PathPrefix(n.path).Subrouter()
subrouter.Methods(httpMethods...).Handler(handler)
}

View file

@ -0,0 +1,20 @@
// +build !experimental
package network
import (
"net/http"
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/daemon"
"github.com/gorilla/mux"
)
// NewRouter initializes a new network router
func NewRouter(d *daemon.Daemon) router.Router {
return networkRouter{}
}
// Register adds the filtered handler to the mux.
func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
}

View file

@ -0,0 +1,25 @@
package router
import (
"net/http"
"github.com/docker/docker/api/server/httputils"
"github.com/gorilla/mux"
)
// VersionMatcher defines a variable matcher to be parsed by the router
// when a request is about to be served.
const VersionMatcher = "/v{version:[0-9.]+}"
// Router defines an interface to specify a group of routes to add the the docker server.
type Router interface {
Routes() []Route
}
// Route defines an individual API route in the docker server.
type Route interface {
// Register adds the handler route to the docker mux.
Register(*mux.Router, http.Handler)
// Handler returns the raw function to create the http handler.
Handler() httputils.APIFunc
}

View file

@ -2,17 +2,17 @@ package server
import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/api/server/router/local"
"github.com/docker/docker/api/server/router/network"
"github.com/docker/docker/daemon"
"github.com/docker/docker/pkg/sockets"
"github.com/docker/docker/utils"
@ -32,21 +32,18 @@ type Config struct {
// Server contains instance details for the server
type Server struct {
daemon *daemon.Daemon
cfg *Config
router *mux.Router
start chan struct{}
servers []serverCloser
routers []router.Router
}
// New returns a new instance of the server based on the specified configuration.
func New(cfg *Config) *Server {
srv := &Server{
return &Server{
cfg: cfg,
start: make(chan struct{}),
}
srv.router = createRouter(srv)
return srv
}
// Close closes servers and thus stop receiving requests
@ -118,152 +115,6 @@ func (s *HTTPServer) Close() error {
return s.l.Close()
}
// 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(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()
if err != nil {
return nil, nil, err
}
// Flush the options to make sure the client sets the raw mode
conn.Write([]byte{})
return conn, conn, nil
}
func closeStreams(streams ...interface{}) {
for _, stream := range streams {
if tcpc, ok := stream.(interface {
CloseWrite() error
}); ok {
tcpc.CloseWrite()
} else if closer, ok := stream.(io.Closer); ok {
closer.Close()
}
}
}
// checkForJSON makes sure that the request's Content-Type is application/json.
func checkForJSON(r *http.Request) error {
ct := r.Header.Get("Content-Type")
// No Content-Type header is ok as long as there's no Body
if ct == "" {
if r.Body == nil || r.ContentLength == 0 {
return nil
}
}
// Otherwise it better be json
if api.MatchesContentType(ct, "application/json") {
return nil
}
return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
}
//If we don't do this, POST method without Content-type (even with empty body) will fail
func parseForm(r *http.Request) error {
if r == nil {
return nil
}
if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
return err
}
return nil
}
func parseMultipartForm(r *http.Request) error {
if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
return err
}
return nil
}
func httpError(w http.ResponseWriter, err error) {
if err == nil || w == nil {
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
return
}
statusCode := http.StatusInternalServerError
errMsg := err.Error()
// Based on the type of error we get we need to process things
// slightly differently to extract the error message.
// In the 'errcode.*' cases there are two different type of
// error that could be returned. errocode.ErrorCode is the base
// type of error object - it is just an 'int' that can then be
// used as the look-up key to find the message. errorcode.Error
// extends errorcode.Error by adding error-instance specific
// data, like 'details' or variable strings to be inserted into
// the message.
//
// Ideally, we should just be able to call err.Error() for all
// cases but the errcode package doesn't support that yet.
//
// Additionally, in both errcode cases, there might be an http
// status code associated with it, and if so use it.
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
if statusCode == 0 {
statusCode = http.StatusInternalServerError
}
logrus.WithFields(logrus.Fields{"statusCode": statusCode, "err": utils.GetErrorMessage(err)}).Error("HTTP Error")
http.Error(w, errMsg, statusCode)
}
// 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 (s *Server) optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
w.WriteHeader(http.StatusOK)
return nil
}
func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
@ -271,11 +122,6 @@ 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(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
_, err := w.Write([]byte{'O', 'K'})
return err
}
func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert {
logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
@ -289,10 +135,10 @@ func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
return
}
func (s *Server) makeHTTPHandler(localMethod string, localRoute string, localHandler HTTPAPIFunc) http.HandlerFunc {
func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// log the handler generation
logrus.Debugf("Calling %s %s", localMethod, localRoute)
// log the handler call
logrus.Debugf("Calling %s %s", r.Method, r.URL.Path)
// Define the context that we'll pass around to share info
// like the docker-request-id.
@ -302,108 +148,53 @@ func (s *Server) makeHTTPHandler(localMethod string, localRoute string, localHan
// immediate function being called should still be passed
// as 'args' on the function call.
ctx := context.Background()
handlerFunc := s.handleWithGlobalMiddlewares(localHandler)
handlerFunc := s.handleWithGlobalMiddlewares(handler)
if err := handlerFunc(ctx, w, r, mux.Vars(r)); err != nil {
logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, utils.GetErrorMessage(err))
httpError(w, err)
logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.URL.Path, utils.GetErrorMessage(err))
httputils.WriteError(w, err)
}
}
}
// createRouter initializes the main router the server uses.
// InitRouters initializes a list of routers for the server.
func (s *Server) InitRouters(d *daemon.Daemon) {
s.addRouter(local.NewRouter(d))
s.addRouter(network.NewRouter(d))
}
// addRouter adds a new router to the server.
func (s *Server) addRouter(r router.Router) {
s.routers = append(s.routers, r)
}
// CreateMux initializes the main router the server uses.
// we keep enableCors just for legacy usage, need to be removed in the future
func createRouter(s *Server) *mux.Router {
r := mux.NewRouter()
func (s *Server) CreateMux() *mux.Router {
m := mux.NewRouter()
if os.Getenv("DEBUG") != "" {
profilerSetup(r, "/debug/")
}
m := map[string]map[string]HTTPAPIFunc{
"HEAD": {
"/containers/{name:.*}/archive": s.headContainersArchive,
},
"GET": {
"/_ping": s.ping,
"/events": s.getEvents,
"/info": s.getInfo,
"/version": s.getVersion,
"/images/json": s.getImagesJSON,
"/images/search": s.getImagesSearch,
"/images/get": s.getImagesGet,
"/images/{name:.*}/get": s.getImagesGet,
"/images/{name:.*}/history": s.getImagesHistory,
"/images/{name:.*}/json": s.getImagesByName,
"/containers/json": s.getContainersJSON,
"/containers/{name:.*}/export": s.getContainersExport,
"/containers/{name:.*}/changes": s.getContainersChanges,
"/containers/{name:.*}/json": s.getContainersByName,
"/containers/{name:.*}/top": s.getContainersTop,
"/containers/{name:.*}/logs": s.getContainersLogs,
"/containers/{name:.*}/stats": s.getContainersStats,
"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
"/exec/{id:.*}/json": s.getExecByID,
"/containers/{name:.*}/archive": s.getContainersArchive,
"/volumes": s.getVolumesList,
"/volumes/{name:.*}": s.getVolumeByName,
},
"POST": {
"/auth": s.postAuth,
"/commit": s.postCommit,
"/build": s.postBuild,
"/images/create": s.postImagesCreate,
"/images/load": s.postImagesLoad,
"/images/{name:.*}/push": s.postImagesPush,
"/images/{name:.*}/tag": s.postImagesTag,
"/containers/create": s.postContainersCreate,
"/containers/{name:.*}/kill": s.postContainersKill,
"/containers/{name:.*}/pause": s.postContainersPause,
"/containers/{name:.*}/unpause": s.postContainersUnpause,
"/containers/{name:.*}/restart": s.postContainersRestart,
"/containers/{name:.*}/start": s.postContainersStart,
"/containers/{name:.*}/stop": s.postContainersStop,
"/containers/{name:.*}/wait": s.postContainersWait,
"/containers/{name:.*}/resize": s.postContainersResize,
"/containers/{name:.*}/attach": s.postContainersAttach,
"/containers/{name:.*}/copy": s.postContainersCopy,
"/containers/{name:.*}/exec": s.postContainerExecCreate,
"/exec/{name:.*}/start": s.postContainerExecStart,
"/exec/{name:.*}/resize": s.postContainerExecResize,
"/containers/{name:.*}/rename": s.postContainerRename,
"/volumes": s.postVolumesCreate,
},
"PUT": {
"/containers/{name:.*}/archive": s.putContainersArchive,
},
"DELETE": {
"/containers/{name:.*}": s.deleteContainers,
"/images/{name:.*}": s.deleteImages,
"/volumes/{name:.*}": s.deleteVolumes,
},
"OPTIONS": {
"": s.optionsHandler,
},
profilerSetup(m, "/debug/")
}
for method, routes := range m {
for route, fct := range routes {
logrus.Debugf("Registering %s, %s", method, route)
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
localMethod := method
// build the handler function
f := s.makeHTTPHandler(localMethod, localRoute, localFct)
// add the new route
if localRoute == "" {
r.Methods(localMethod).HandlerFunc(f)
} else {
r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
}
logrus.Debugf("Registering routers")
for _, router := range s.routers {
for _, r := range router.Routes() {
f := s.makeHTTPHandler(r.Handler())
r.Register(m, f)
}
}
return r
return m
}
// AcceptConnections allows clients to connect to the API server.
// Referenced Daemon is notified about this server, and waits for the
// daemon acknowledgement before the incoming connections are accepted.
func (s *Server) AcceptConnections() {
// close the lock so the listeners start accepting connections
select {
case <-s.start:
default:
close(s.start)
}
}

View file

@ -1,22 +0,0 @@
// +build experimental,!windows
package server
func (s *Server) registerSubRouter() {
httpHandler := s.daemon.NetworkAPIRouter()
subrouter := s.router.PathPrefix("/v{version:[0-9.]+}/networks").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
subrouter = s.router.PathPrefix("/networks").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
subrouter = s.router.PathPrefix("/v{version:[0-9.]+}/services").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
subrouter = s.router.PathPrefix("/services").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
subrouter = s.router.PathPrefix("/v{version:[0-9.]+}/sandboxes").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
subrouter = s.router.PathPrefix("/sandboxes").Subrouter()
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
}

View file

@ -1,6 +0,0 @@
// +build !experimental windows
package server
func (s *Server) registerSubRouter() {
}

View file

@ -5,6 +5,8 @@ import (
"net/http/httptest"
"testing"
"github.com/docker/docker/api/server/httputils"
"golang.org/x/net/context"
)
@ -19,7 +21,7 @@ func TestMiddlewares(t *testing.T) {
ctx := context.Background()
localHandler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if versionFromContext(ctx) == "" {
if httputils.VersionFromContext(ctx) == "" {
t.Fatalf("Expected version, got empty string")
}
return nil

View file

@ -8,12 +8,10 @@ import (
"net/http"
"strconv"
"github.com/docker/docker/daemon"
"github.com/docker/docker/pkg/sockets"
"github.com/docker/libnetwork/portallocator"
systemdActivation "github.com/coreos/go-systemd/activation"
systemdDaemon "github.com/coreos/go-systemd/daemon"
)
// newServer sets up the required serverClosers and does protocol specific checking.
@ -52,7 +50,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
res = append(res, &HTTPServer{
&http.Server{
Addr: addr,
Handler: s.router,
Handler: s.CreateMux(),
},
l,
})
@ -60,22 +58,6 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
return res, nil
}
// AcceptConnections allows clients to connect to the API server.
// Referenced Daemon is notified about this server, and waits for the
// daemon acknowledgement before the incoming connections are accepted.
func (s *Server) AcceptConnections(d *daemon.Daemon) {
// Tell the init daemon we are accepting requests
s.daemon = d
s.registerSubRouter()
go systemdDaemon.SdNotify("READY=1")
// close the lock so the listeners start accepting connections
select {
case <-s.start:
default:
close(s.start)
}
}
func allocateDaemonPort(addr string) error {
host, port, err := net.SplitHostPort(addr)
if err != nil {

View file

@ -6,8 +6,6 @@ import (
"errors"
"net"
"net/http"
"github.com/docker/docker/daemon"
)
// NewServer sets up the required Server and does protocol specific checking.
@ -32,7 +30,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
res = append(res, &HTTPServer{
&http.Server{
Addr: addr,
Handler: s.router,
Handler: s.CreateMux(),
},
l,
})
@ -41,18 +39,6 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
}
// AcceptConnections allows router to start listening for the incoming requests.
func (s *Server) AcceptConnections(d *daemon.Daemon) {
s.daemon = d
s.registerSubRouter()
// close the lock so the listeners start accepting connections
select {
case <-s.start:
default:
close(s.start)
}
}
func allocateDaemonPort(addr string) error {
return nil
}

View file

@ -1131,6 +1131,11 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
return verifyPlatformContainerSettings(daemon, hostConfig, config)
}
// NetworkController exposes the libnetwork interface to manage networks.
func (daemon *Daemon) NetworkController() libnetwork.NetworkController {
return daemon.netController
}
func configureVolumes(config *Config) (*store.VolumeStore, error) {
volumesDriver, err := local.New(config.Root)
if err != nil {

View file

@ -5,7 +5,6 @@ package daemon
import (
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"
@ -22,7 +21,6 @@ import (
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
"github.com/docker/libnetwork"
nwapi "github.com/docker/libnetwork/api"
nwconfig "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
@ -489,12 +487,6 @@ func setupInitLayer(initLayer string) error {
return nil
}
// NetworkAPIRouter implements a feature for server-experimental,
// directly calling into libnetwork.
func (daemon *Daemon) NetworkAPIRouter() func(w http.ResponseWriter, req *http.Request) {
return nwapi.NewHTTPHandler(daemon.netController)
}
// registerLinks writes the links to a file.
func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.HostConfig) error {
if hostConfig == nil || hostConfig.Links == nil {

View file

@ -224,21 +224,6 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
serverConfig.TLSConfig = tlsConfig
}
api := apiserver.New(serverConfig)
// The serve API routine never exits unless an error occurs
// We need to start it as a goroutine and wait on it so
// daemon doesn't exit
serveAPIWait := make(chan error)
go func() {
if err := api.ServeAPI(commonFlags.Hosts); err != nil {
logrus.Errorf("ServeAPI error: %v", err)
serveAPIWait <- err
return
}
serveAPIWait <- nil
}()
if err := migrateKey(); err != nil {
logrus.Fatal(err)
}
@ -264,6 +249,22 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
"graphdriver": d.GraphDriver().String(),
}).Info("Docker daemon")
api := apiserver.New(serverConfig)
api.InitRouters(d)
// The serve API routine never exits unless an error occurs
// We need to start it as a goroutine and wait on it so
// daemon doesn't exit
serveAPIWait := make(chan error)
go func() {
if err := api.ServeAPI(commonFlags.Hosts); err != nil {
logrus.Errorf("ServeAPI error: %v", err)
serveAPIWait <- err
return
}
serveAPIWait <- nil
}()
signal.Trap(func() {
api.Close()
<-serveAPIWait
@ -277,7 +278,8 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
// after the daemon is done setting up we can tell the api to start
// accepting connections with specified daemon
api.AcceptConnections(d)
notifySystem()
api.AcceptConnections()
// Daemon is fully initialized and handling API traffic
// Wait for serve API to complete

7
docker/daemon_freebsd.go Normal file
View file

@ -0,0 +1,7 @@
// +build daemon
package docker
// notifySystem sends a message to the host when the server is ready to be used
func notifySystem() {
}

View file

@ -3,5 +3,12 @@
package main
import (
systemdDaemon "github.com/coreos/go-systemd/daemon"
_ "github.com/docker/docker/daemon/execdriver/lxc"
)
// notifySystem sends a message to the host when the server is ready to be used
func notifySystem() {
// Tell the init daemon we are accepting requests
go systemdDaemon.SdNotify("READY=1")
}

View file

@ -10,3 +10,7 @@ var daemonCli cli.Handler
// TODO: remove once `-d` is retired
func handleGlobalDaemonFlag() {}
// notifySystem sends a message to the host when the server is ready to be used
func notifySystem() {
}

View file

@ -27,3 +27,7 @@ func setDefaultUmask() error {
func getDaemonConfDir() string {
return os.Getenv("PROGRAMDATA") + `\docker\config`
}
// notifySystem sends a message to the host when the server is ready to be used
func notifySystem() {
}