浏览代码

Merge pull request #28489 from vieux/1.13.0-rc2-cherrypicks

1.13.0-rc2 cherry-picks : part 2
Brian Goff 8 年之前
父节点
当前提交
f7ae8204cb
共有 100 个文件被更改,包括 997 次插入484 次删除
  1. 17 14
      api/server/httputils/errors.go
  2. 3 3
      api/server/router/checkpoint/checkpoint.go
  3. 67 0
      api/server/router/experimental.go
  4. 2 2
      api/server/router/swarm/backend.go
  5. 3 14
      api/server/router/swarm/cluster.go
  6. 5 6
      api/server/router/swarm/cluster_routes.go
  7. 1 1
      api/server/server.go
  8. 36 19
      api/swagger.yaml
  9. 3 1
      api/types/client.go
  10. 12 0
      api/types/service_update_response.go
  11. 1 1
      api/types/swarm/service.go
  12. 1 0
      api/types/versions/v1p24/types.go
  13. 1 1
      api/types/volume.go
  14. 1 1
      api/types/volume/volumes_create.go
  15. 1 1
      builder/dockerfile/evaluator_windows.go
  16. 2 5
      cli/command/checkpoint/cmd.go
  17. 8 0
      cli/command/cli.go
  18. 1 4
      cli/command/container/cmd.go
  19. 16 18
      cli/command/container/list.go
  20. 51 0
      cli/command/container/ps_test.go
  21. 2 2
      cli/command/formatter/container_test.go
  22. 6 0
      cli/command/formatter/service.go
  23. 1 4
      cli/command/image/cmd.go
  24. 4 2
      cli/command/image/load.go
  25. 2 2
      cli/command/image/tag.go
  26. 1 4
      cli/command/network/cmd.go
  27. 1 4
      cli/command/node/cmd.go
  28. 1 4
      cli/command/plugin/cmd.go
  29. 1 5
      cli/command/secret/cmd.go
  30. 1 4
      cli/command/service/cmd.go
  31. 4 0
      cli/command/service/create.go
  32. 5 1
      cli/command/service/scale.go
  33. 5 1
      cli/command/service/update.go
  34. 2 5
      cli/command/stack/cmd.go
  35. 8 3
      cli/command/stack/deploy.go
  36. 1 1
      cli/command/stack/list.go
  37. 1 4
      cli/command/swarm/cmd.go
  38. 1 4
      cli/command/system/cmd.go
  39. 1 4
      cli/command/volume/cmd.go
  40. 1 1
      client/interface.go
  41. 9 3
      client/plugin_install.go
  42. 9 2
      client/service_update.go
  43. 3 3
      client/service_update_test.go
  44. 31 6
      cmd/docker/docker.go
  45. 16 8
      cmd/dockerd/daemon.go
  46. 0 15
      cmd/dockerd/routes_experimental.go
  47. 1 4
      container/health.go
  48. 4 1
      contrib/check-config.sh
  49. 4 2
      contrib/completion/zsh/_docker
  50. 66 37
      daemon/cluster/cluster.go
  51. 2 6
      daemon/commit.go
  52. 0 8
      daemon/create_windows.go
  53. 5 6
      daemon/daemon.go
  54. 8 3
      daemon/daemon_unix.go
  55. 3 1
      daemon/daemon_windows.go
  56. 30 1
      daemon/debugtrap.go
  57. 2 1
      daemon/graphdriver/plugin.go
  58. 2 4
      daemon/graphdriver/windows/windows.go
  59. 22 8
      daemon/health.go
  60. 1 1
      daemon/health_test.go
  61. 1 0
      daemon/image_pull.go
  62. 1 0
      daemon/info_unix.go
  63. 1 0
      daemon/info_windows.go
  64. 2 0
      docs/extend/config.md
  65. 2 2
      docs/extend/index.md
  66. 3 0
      docs/reference/api/docker_remote_api.md
  67. 6 6
      docs/reference/api/docker_remote_api_v1.18.md
  68. 6 6
      docs/reference/api/docker_remote_api_v1.19.md
  69. 7 7
      docs/reference/api/docker_remote_api_v1.20.md
  70. 9 9
      docs/reference/api/docker_remote_api_v1.21.md
  71. 12 9
      docs/reference/api/docker_remote_api_v1.22.md
  72. 13 10
      docs/reference/api/docker_remote_api_v1.23.md
  73. 23 20
      docs/reference/api/docker_remote_api_v1.24.md
  74. 50 36
      docs/reference/api/docker_remote_api_v1.25.md
  75. 19 0
      docs/reference/commandline/images.md
  76. 50 27
      docs/reference/commandline/info.md
  77. 7 4
      docs/reference/commandline/stats.md
  78. 2 2
      docs/reference/commandline/tag.md
  79. 3 3
      hack/dockerfile/binaries-commits
  80. 2 1
      hack/generate-swagger-api.sh
  81. 1 1
      hack/validate/vet
  82. 4 1
      image/tarexport/load.go
  83. 4 2
      image/tarexport/save.go
  84. 1 1
      integration-cli/daemon.go
  85. 23 27
      integration-cli/docker_cli_build_test.go
  86. 1 1
      integration-cli/docker_cli_help_test.go
  87. 16 0
      integration-cli/docker_cli_swarm_test.go
  88. 1 1
      libcontainerd/remote_unix.go
  89. 52 27
      man/docker-info.1.md
  90. 1 1
      man/docker-run.1.md
  91. 2 0
      man/docker-stats.1.md
  92. 2 2
      man/docker-tag.1.md
  93. 1 0
      opts/port.go
  94. 8 3
      pkg/archive/archive.go
  95. 1 1
      pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go
  96. 1 1
      pkg/discovery/backends.go
  97. 4 4
      pkg/ioutils/multireader.go
  98. 21 0
      pkg/ioutils/multireader_test.go
  99. 29 0
      pkg/system/filesys.go
  100. 106 3
      pkg/system/filesys_windows.go

+ 17 - 14
api/server/httputils/errors.go

@@ -28,7 +28,7 @@ type inputValidationError interface {
 	IsValidationError() bool
 	IsValidationError() bool
 }
 }
 
 
-// GetHTTPErrorStatusCode retrieve status code from error message
+// GetHTTPErrorStatusCode retrieves status code from error message
 func GetHTTPErrorStatusCode(err error) int {
 func GetHTTPErrorStatusCode(err error) int {
 	if err == nil {
 	if err == nil {
 		logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
 		logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
@@ -49,20 +49,23 @@ func GetHTTPErrorStatusCode(err error) int {
 		// If we need to differentiate between different possible error types,
 		// If we need to differentiate between different possible error types,
 		// we should create appropriate error types that implement the httpStatusError interface.
 		// we should create appropriate error types that implement the httpStatusError interface.
 		errStr := strings.ToLower(errMsg)
 		errStr := strings.ToLower(errMsg)
-		for keyword, status := range map[string]int{
-			"not found":             http.StatusNotFound,
-			"no such":               http.StatusNotFound,
-			"bad parameter":         http.StatusBadRequest,
-			"no command":            http.StatusBadRequest,
-			"conflict":              http.StatusConflict,
-			"impossible":            http.StatusNotAcceptable,
-			"wrong login/password":  http.StatusUnauthorized,
-			"unauthorized":          http.StatusUnauthorized,
-			"hasn't been activated": http.StatusForbidden,
-			"this node":             http.StatusNotAcceptable,
+		for _, status := range []struct {
+			keyword string
+			code    int
+		}{
+			{"not found", http.StatusNotFound},
+			{"no such", http.StatusNotFound},
+			{"bad parameter", http.StatusBadRequest},
+			{"no command", http.StatusBadRequest},
+			{"conflict", http.StatusConflict},
+			{"impossible", http.StatusNotAcceptable},
+			{"wrong login/password", http.StatusUnauthorized},
+			{"unauthorized", http.StatusUnauthorized},
+			{"hasn't been activated", http.StatusForbidden},
+			{"this node", http.StatusServiceUnavailable},
 		} {
 		} {
-			if strings.Contains(errStr, keyword) {
-				statusCode = status
+			if strings.Contains(errStr, status.keyword) {
+				statusCode = status.code
 				break
 				break
 			}
 			}
 		}
 		}

+ 3 - 3
api/server/router/checkpoint/checkpoint.go

@@ -29,8 +29,8 @@ func (r *checkpointRouter) Routes() []router.Route {
 
 
 func (r *checkpointRouter) initRoutes() {
 func (r *checkpointRouter) initRoutes() {
 	r.routes = []router.Route{
 	r.routes = []router.Route{
-		router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints),
-		router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint),
-		router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint),
+		router.Experimental(router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints)),
+		router.Experimental(router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint)),
+		router.Experimental(router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint)),
 	}
 	}
 }
 }

+ 67 - 0
api/server/router/experimental.go

@@ -0,0 +1,67 @@
+package router
+
+import (
+	"errors"
+	"net/http"
+
+	"golang.org/x/net/context"
+
+	apierrors "github.com/docker/docker/api/errors"
+	"github.com/docker/docker/api/server/httputils"
+)
+
+var (
+	errExperimentalFeature = errors.New("This experimental feature is disabled by default. Start the Docker daemon with --experimental in order to enable it.")
+)
+
+// ExperimentalRoute defines an experimental API route that can be enabled or disabled.
+type ExperimentalRoute interface {
+	Route
+
+	Enable()
+	Disable()
+}
+
+// experimentalRoute defines an experimental API route that can be enabled or disabled.
+// It implements ExperimentalRoute
+type experimentalRoute struct {
+	local   Route
+	handler httputils.APIFunc
+}
+
+// Enable enables this experimental route
+func (r *experimentalRoute) Enable() {
+	r.handler = r.local.Handler()
+}
+
+// Disable disables the experimental route
+func (r *experimentalRoute) Disable() {
+	r.handler = experimentalHandler
+}
+
+func experimentalHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	return apierrors.NewErrorWithStatusCode(errExperimentalFeature, http.StatusNotImplemented)
+}
+
+// Handler returns returns the APIFunc to let the server wrap it in middlewares.
+func (r *experimentalRoute) Handler() httputils.APIFunc {
+	return r.handler
+}
+
+// Method returns the http method that the route responds to.
+func (r *experimentalRoute) Method() string {
+	return r.local.Method()
+}
+
+// Path returns the subpath where the route responds to.
+func (r *experimentalRoute) Path() string {
+	return r.local.Path()
+}
+
+// Experimental will mark a route as experimental.
+func Experimental(r Route) Route {
+	return &experimentalRoute{
+		local:   r,
+		handler: experimentalHandler,
+	}
+}

+ 2 - 2
api/server/router/swarm/backend.go

@@ -18,8 +18,8 @@ type Backend interface {
 	UnlockSwarm(req types.UnlockRequest) error
 	UnlockSwarm(req types.UnlockRequest) error
 	GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
 	GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
 	GetService(string) (types.Service, error)
 	GetService(string) (types.Service, error)
-	CreateService(types.ServiceSpec, string) (string, error)
-	UpdateService(string, uint64, types.ServiceSpec, string, string) error
+	CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
+	UpdateService(string, uint64, types.ServiceSpec, string, string) (*basictypes.ServiceUpdateResponse, error)
 	RemoveService(string) error
 	RemoveService(string) error
 	ServiceLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
 	ServiceLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
 	GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
 	GetNodes(basictypes.NodeListOptions) ([]types.Node, error)

+ 3 - 14
api/server/router/swarm/cluster.go

@@ -1,9 +1,6 @@
 package swarm
 package swarm
 
 
-import (
-	"github.com/docker/docker/api/server/router"
-	"github.com/docker/docker/daemon"
-)
+import "github.com/docker/docker/api/server/router"
 
 
 // swarmRouter is a router to talk with the build controller
 // swarmRouter is a router to talk with the build controller
 type swarmRouter struct {
 type swarmRouter struct {
@@ -12,14 +9,11 @@ type swarmRouter struct {
 }
 }
 
 
 // NewRouter initializes a new build router
 // NewRouter initializes a new build router
-func NewRouter(d *daemon.Daemon, b Backend) router.Router {
+func NewRouter(b Backend) router.Router {
 	r := &swarmRouter{
 	r := &swarmRouter{
 		backend: b,
 		backend: b,
 	}
 	}
 	r.initRoutes()
 	r.initRoutes()
-	if d.HasExperimental() {
-		r.addExperimentalRoutes()
-	}
 	return r
 	return r
 }
 }
 
 
@@ -28,12 +22,6 @@ func (sr *swarmRouter) Routes() []router.Route {
 	return sr.routes
 	return sr.routes
 }
 }
 
 
-func (sr *swarmRouter) addExperimentalRoutes() {
-	sr.routes = append(sr.routes,
-		router.Cancellable(router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs)),
-	)
-}
-
 func (sr *swarmRouter) initRoutes() {
 func (sr *swarmRouter) initRoutes() {
 	sr.routes = []router.Route{
 	sr.routes = []router.Route{
 		router.NewPostRoute("/swarm/init", sr.initCluster),
 		router.NewPostRoute("/swarm/init", sr.initCluster),
@@ -48,6 +36,7 @@ func (sr *swarmRouter) initRoutes() {
 		router.NewPostRoute("/services/create", sr.createService),
 		router.NewPostRoute("/services/create", sr.createService),
 		router.NewPostRoute("/services/{id}/update", sr.updateService),
 		router.NewPostRoute("/services/{id}/update", sr.updateService),
 		router.NewDeleteRoute("/services/{id}", sr.removeService),
 		router.NewDeleteRoute("/services/{id}", sr.removeService),
+		router.Experimental(router.Cancellable(router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs))),
 		router.NewGetRoute("/nodes", sr.getNodes),
 		router.NewGetRoute("/nodes", sr.getNodes),
 		router.NewGetRoute("/nodes/{id}", sr.getNode),
 		router.NewGetRoute("/nodes/{id}", sr.getNode),
 		router.NewDeleteRoute("/nodes/{id}", sr.removeNode),
 		router.NewDeleteRoute("/nodes/{id}", sr.removeNode),

+ 5 - 6
api/server/router/swarm/cluster_routes.go

@@ -166,15 +166,13 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
 	// Get returns "" if the header does not exist
 	// Get returns "" if the header does not exist
 	encodedAuth := r.Header.Get("X-Registry-Auth")
 	encodedAuth := r.Header.Get("X-Registry-Auth")
 
 
-	id, err := sr.backend.CreateService(service, encodedAuth)
+	resp, err := sr.backend.CreateService(service, encodedAuth)
 	if err != nil {
 	if err != nil {
 		logrus.Errorf("Error creating service %s: %v", service.Name, err)
 		logrus.Errorf("Error creating service %s: %v", service.Name, err)
 		return err
 		return err
 	}
 	}
 
 
-	return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ServiceCreateResponse{
-		ID: id,
-	})
+	return httputils.WriteJSON(w, http.StatusCreated, resp)
 }
 }
 
 
 func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
@@ -194,11 +192,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
 
 
 	registryAuthFrom := r.URL.Query().Get("registryAuthFrom")
 	registryAuthFrom := r.URL.Query().Get("registryAuthFrom")
 
 
-	if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth, registryAuthFrom); err != nil {
+	resp, err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth, registryAuthFrom)
+	if err != nil {
 		logrus.Errorf("Error updating service %s: %v", vars["id"], err)
 		logrus.Errorf("Error updating service %s: %v", vars["id"], err)
 		return err
 		return err
 	}
 	}
-	return nil
+	return httputils.WriteJSON(w, http.StatusOK, resp)
 }
 }
 
 
 func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 1 - 1
api/server/server.go

@@ -102,7 +102,7 @@ func (s *Server) serveAPI() error {
 }
 }
 
 
 // HTTPServer contains an instance of http server and the listener.
 // HTTPServer contains an instance of http server and the listener.
-// srv *http.Server, contains configuration to create a http server and a mux router with all api end points.
+// srv *http.Server, contains configuration to create an http server and a mux router with all api end points.
 // l   net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
 // l   net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
 type HTTPServer struct {
 type HTTPServer struct {
 	srv *http.Server
 	srv *http.Server

+ 36 - 19
api/swagger.yaml

@@ -2218,6 +2218,16 @@ definitions:
       Deleted:
       Deleted:
         description: "The image ID of an image that was deleted"
         description: "The image ID of an image that was deleted"
         type: "string"
         type: "string"
+  ServiceUpdateResponse:
+    type: "object"
+    properties:
+      Warnings:
+        description: "Optional warning messages"
+        type: "array"
+        items:
+          type: "string"
+    example:
+      Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
   ContainerSummary:
   ContainerSummary:
     type: "array"
     type: "array"
     items:
     items:
@@ -6731,14 +6741,14 @@ paths:
           description: "bad parameter"
           description: "bad parameter"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
-        406:
-          description: "node is already part of a swarm"
-          schema:
-            $ref: "#/definitions/ErrorResponse"
         500:
         500:
           description: "server error"
           description: "server error"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
+        503:
+          description: "node is already part of a swarm"
+          schema:
+            $ref: "#/definitions/ErrorResponse"
       parameters:
       parameters:
         - name: "body"
         - name: "body"
           in: "body"
           in: "body"
@@ -6773,14 +6783,14 @@ paths:
       responses:
       responses:
         200:
         200:
           description: "no error"
           description: "no error"
-        406:
-          description: "node is not part of a swarm"
-          schema:
-            $ref: "#/definitions/ErrorResponse"
         500:
         500:
           description: "server error"
           description: "server error"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
+        503:
+          description: "node is not part of a swarm"
+          schema:
+            $ref: "#/definitions/ErrorResponse"
       parameters:
       parameters:
         - name: "force"
         - name: "force"
           description: "Force leave swarm, even if this is the last manager or that it will break the cluster."
           description: "Force leave swarm, even if this is the last manager or that it will break the cluster."
@@ -6800,14 +6810,14 @@ paths:
           description: "bad parameter"
           description: "bad parameter"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
-        406:
-          description: "node is not part of a swarm"
-          schema:
-            $ref: "#/definitions/ErrorResponse"
         500:
         500:
           description: "server error"
           description: "server error"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
+        503:
+          description: "node is not part of a swarm"
+          schema:
+            $ref: "#/definitions/ErrorResponse"
       parameters:
       parameters:
         - name: "body"
         - name: "body"
           in: "body"
           in: "body"
@@ -6875,12 +6885,12 @@ paths:
               ID:
               ID:
                 description: "The ID of the created service."
                 description: "The ID of the created service."
                 type: "string"
                 type: "string"
+              Warning:
+                description: "Optional warning message"
+                type: "string"
             example:
             example:
               ID: "ak7w3gjqoa3kuz8xcpnyy0pvl"
               ID: "ak7w3gjqoa3kuz8xcpnyy0pvl"
-        406:
-          description: "server error or node is not part of a swarm"
-          schema:
-            $ref: "#/definitions/ErrorResponse"
+              Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
         409:
         409:
           description: "name conflicts with an existing service"
           description: "name conflicts with an existing service"
           schema:
           schema:
@@ -6889,6 +6899,10 @@ paths:
           description: "server error"
           description: "server error"
           schema:
           schema:
             $ref: "#/definitions/ErrorResponse"
             $ref: "#/definitions/ErrorResponse"
+        503:
+          description: "server error or node is not part of a swarm"
+          schema:
+            $ref: "#/definitions/ErrorResponse"
       parameters:
       parameters:
         - name: "body"
         - name: "body"
           in: "body"
           in: "body"
@@ -6998,10 +7012,14 @@ paths:
   /services/{id}/update:
   /services/{id}/update:
     post:
     post:
       summary: "Update a service"
       summary: "Update a service"
-      operationId: "PostServicesUpdate"
+      operationId: "ServiceUpdate"
+      consumes: ["application/json"]
+      produces: ["application/json"]
       responses:
       responses:
         200:
         200:
           description: "no error"
           description: "no error"
+          schema:
+            $ref: "#/definitions/ImageDeleteResponse"
         404:
         404:
           description: "no such service"
           description: "no such service"
           schema:
           schema:
@@ -7065,8 +7083,7 @@ paths:
           description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
           description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
           type: "string"
           type: "string"
 
 
-      tags:
-        - "Services"
+      tags: [Service]
   /tasks:
   /tasks:
     get:
     get:
       summary: "List tasks"
       summary: "List tasks"

+ 3 - 1
api/types/client.go

@@ -285,10 +285,12 @@ type ServiceCreateOptions struct {
 }
 }
 
 
 // ServiceCreateResponse contains the information returned to a client
 // ServiceCreateResponse contains the information returned to a client
-// on the  creation of a new service.
+// on the creation of a new service.
 type ServiceCreateResponse struct {
 type ServiceCreateResponse struct {
 	// ID is the ID of the created service.
 	// ID is the ID of the created service.
 	ID string
 	ID string
+	// Warnings is a set of non-fatal warning messages to pass on to the user.
+	Warnings []string `json:",omitempty"`
 }
 }
 
 
 // Values for RegistryAuthFrom in ServiceUpdateOptions
 // Values for RegistryAuthFrom in ServiceUpdateOptions

+ 12 - 0
api/types/service_update_response.go

@@ -0,0 +1,12 @@
+package types
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+// ServiceUpdateResponse service update response
+// swagger:model ServiceUpdateResponse
+type ServiceUpdateResponse struct {
+
+	// Optional warning messages
+	Warnings []string `json:"Warnings"`
+}

+ 1 - 1
api/types/swarm/service.go

@@ -74,7 +74,7 @@ const (
 type UpdateConfig struct {
 type UpdateConfig struct {
 	// Maximum number of tasks to be updated in one iteration.
 	// Maximum number of tasks to be updated in one iteration.
 	// 0 means unlimited parallelism.
 	// 0 means unlimited parallelism.
-	Parallelism uint64 `json:",omitempty"`
+	Parallelism uint64
 
 
 	// Amount of time between updates.
 	// Amount of time between updates.
 	Delay time.Duration `json:",omitempty"`
 	Delay time.Duration `json:",omitempty"`

+ 1 - 0
api/types/versions/v1p24/types.go

@@ -3,6 +3,7 @@ package v1p24
 
 
 import "github.com/docker/docker/api/types"
 import "github.com/docker/docker/api/types"
 
 
+// Info is a backcompatibility struct for the API 1.24
 type Info struct {
 type Info struct {
 	*types.InfoBase
 	*types.InfoBase
 	ExecutionDriver string
 	ExecutionDriver string

+ 1 - 1
api/types/volume.go

@@ -11,7 +11,7 @@ type Volume struct {
 	// Required: true
 	// Required: true
 	Driver string `json:"Driver"`
 	Driver string `json:"Driver"`
 
 
-	// A mapping of abitrary key/value data set on this volume.
+	// User-defined key/value metadata.
 	// Required: true
 	// Required: true
 	Labels map[string]string `json:"Labels"`
 	Labels map[string]string `json:"Labels"`
 
 

+ 1 - 1
api/types/volume/volumes_create.go

@@ -19,7 +19,7 @@ type VolumesCreateBody struct {
 	// Required: true
 	// Required: true
 	DriverOpts map[string]string `json:"DriverOpts"`
 	DriverOpts map[string]string `json:"DriverOpts"`
 
 
-	// A mapping of arbitrary key/value data to set on the volume.
+	// User-defined key/value metadata.
 	// Required: true
 	// Required: true
 	Labels map[string]string `json:"Labels"`
 	Labels map[string]string `json:"Labels"`
 
 

+ 1 - 1
builder/dockerfile/evaluator_windows.go

@@ -6,7 +6,7 @@ import "fmt"
 // a command not supported on the platform.
 // a command not supported on the platform.
 func platformSupports(command string) error {
 func platformSupports(command string) error {
 	switch command {
 	switch command {
-	case "user", "stopsignal":
+	case "stopsignal":
 		return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
 		return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
 	}
 	}
 	return nil
 	return nil

+ 2 - 5
cli/command/checkpoint/cmd.go

@@ -12,11 +12,8 @@ func NewCheckpointCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "checkpoint",
 		Use:   "checkpoint",
 		Short: "Manage checkpoints",
 		Short: "Manage checkpoints",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
-		Tags: map[string]string{"experimental": "", "version": "1.25"},
+		RunE:  dockerCli.ShowHelp,
+		Tags:  map[string]string{"experimental": "", "version": "1.25"},
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newCreateCommand(dockerCli),
 		newCreateCommand(dockerCli),

+ 8 - 0
cli/command/cli.go

@@ -20,6 +20,7 @@ import (
 	dopts "github.com/docker/docker/opts"
 	dopts "github.com/docker/docker/opts"
 	"github.com/docker/go-connections/sockets"
 	"github.com/docker/go-connections/sockets"
 	"github.com/docker/go-connections/tlsconfig"
 	"github.com/docker/go-connections/tlsconfig"
+	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -73,6 +74,13 @@ func (cli *DockerCli) In() *InStream {
 	return cli.in
 	return cli.in
 }
 }
 
 
+// ShowHelp shows the command help.
+func (cli *DockerCli) ShowHelp(cmd *cobra.Command, args []string) error {
+	cmd.SetOutput(cli.err)
+	cmd.HelpFunc()(cmd, args)
+	return nil
+}
+
 // ConfigFile returns the ConfigFile
 // ConfigFile returns the ConfigFile
 func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
 func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
 	return cli.configFile
 	return cli.configFile

+ 1 - 4
cli/command/container/cmd.go

@@ -13,10 +13,7 @@ func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "container",
 		Use:   "container",
 		Short: "Manage containers",
 		Short: "Manage containers",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		NewAttachCommand(dockerCli),
 		NewAttachCommand(dockerCli),

+ 16 - 18
cli/command/container/list.go

@@ -59,20 +59,18 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
 	return &cmd
 	return &cmd
 }
 }
 
 
-type preProcessor struct {
-	types.Container
-	opts *types.ContainerListOptions
-
-	// Fields that need to exist so the template doesn't error out
-	// These are needed since they are available on the final object but are not
-	// fields in types.Container
-	// TODO(cpuguy83): this seems rather broken
-	Networks, CreatedAt, RunningFor bool
-}
-
-// Size sets the size option when called by a template execution.
-func (p *preProcessor) Size() bool {
-	p.opts.Size = true
+// listOptionsProcessor is used to set any container list options which may only
+// be embedded in the format template.
+// This is passed directly into tmpl.Execute in order to allow the preprocessor
+// to set any list options that were not provided by flags (e.g. `.Size`).
+// It is using a `map[string]bool` so that unknown fields passed into the
+// template format do not cause errors. These errors will get picked up when
+// running through the actual template processor.
+type listOptionsProcessor map[string]bool
+
+// Size sets the size of the map when called by a template execution.
+func (o listOptionsProcessor) Size() bool {
+	o["size"] = true
 	return true
 	return true
 }
 }
 
 
@@ -88,20 +86,20 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
 		options.Limit = 1
 		options.Limit = 1
 	}
 	}
 
 
-	// Currently only used with Size, so we can determine if the user
-	// put {{.Size}} in their format.
-	pre := &preProcessor{opts: options}
 	tmpl, err := templates.Parse(opts.format)
 	tmpl, err := templates.Parse(opts.format)
 
 
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	optionsProcessor := listOptionsProcessor{}
 	// This shouldn't error out but swallowing the error makes it harder
 	// This shouldn't error out but swallowing the error makes it harder
 	// to track down if preProcessor issues come up. Ref #24696
 	// to track down if preProcessor issues come up. Ref #24696
-	if err := tmpl.Execute(ioutil.Discard, pre); err != nil {
+	if err := tmpl.Execute(ioutil.Discard, optionsProcessor); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+	// At the moment all we need is to capture .Size for preprocessor
+	options.Size = opts.size || optionsProcessor["size"]
 
 
 	return options, nil
 	return options, nil
 }
 }

+ 51 - 0
cli/command/container/ps_test.go

@@ -46,6 +46,57 @@ func TestBuildContainerListOptions(t *testing.T) {
 			expectedLimit:   1,
 			expectedLimit:   1,
 			expectedFilters: make(map[string]string),
 			expectedFilters: make(map[string]string),
 		},
 		},
+		{
+			psOpts: &psOptions{
+				all:    true,
+				size:   false,
+				last:   5,
+				filter: filters,
+				// With .Size, size should be true
+				format: "{{.Size}}",
+			},
+			expectedAll:   true,
+			expectedSize:  true,
+			expectedLimit: 5,
+			expectedFilters: map[string]string{
+				"foo": "bar",
+				"baz": "foo",
+			},
+		},
+		{
+			psOpts: &psOptions{
+				all:    true,
+				size:   false,
+				last:   5,
+				filter: filters,
+				// With .Size, size should be true
+				format: "{{.Size}} {{.CreatedAt}} {{.Networks}}",
+			},
+			expectedAll:   true,
+			expectedSize:  true,
+			expectedLimit: 5,
+			expectedFilters: map[string]string{
+				"foo": "bar",
+				"baz": "foo",
+			},
+		},
+		{
+			psOpts: &psOptions{
+				all:    true,
+				size:   false,
+				last:   5,
+				filter: filters,
+				// Without .Size, size should be false
+				format: "{{.CreatedAt}} {{.Networks}}",
+			},
+			expectedAll:   true,
+			expectedSize:  false,
+			expectedLimit: 5,
+			expectedFilters: map[string]string{
+				"foo": "bar",
+				"baz": "foo",
+			},
+		},
 	}
 	}
 
 
 	for _, c := range contexts {
 	for _, c := range contexts {

+ 2 - 2
cli/command/formatter/container_test.go

@@ -372,7 +372,7 @@ func TestContainerContextWriteJSONField(t *testing.T) {
 }
 }
 
 
 func TestContainerBackCompat(t *testing.T) {
 func TestContainerBackCompat(t *testing.T) {
-	containers := []types.Container{types.Container{ID: "brewhaha"}}
+	containers := []types.Container{{ID: "brewhaha"}}
 	cases := []string{
 	cases := []string{
 		"ID",
 		"ID",
 		"Names",
 		"Names",
@@ -390,7 +390,7 @@ func TestContainerBackCompat(t *testing.T) {
 	for _, c := range cases {
 	for _, c := range cases {
 		ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
 		ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
 		if err := ContainerWrite(ctx, containers); err != nil {
 		if err := ContainerWrite(ctx, containers); err != nil {
-			t.Log("could not render template for field '%s': %v", c, err)
+			t.Logf("could not render template for field '%s': %v", c, err)
 			t.Fail()
 			t.Fail()
 		}
 		}
 		buf.Reset()
 		buf.Reset()

+ 6 - 0
cli/command/formatter/service.go

@@ -263,6 +263,9 @@ func (ctx *serviceInspectContext) HasResources() bool {
 }
 }
 
 
 func (ctx *serviceInspectContext) HasResourceReservations() bool {
 func (ctx *serviceInspectContext) HasResourceReservations() bool {
+	if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Reservations == nil {
+		return false
+	}
 	return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
 	return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
 }
 }
 
 
@@ -281,6 +284,9 @@ func (ctx *serviceInspectContext) ResourceReservationMemory() string {
 }
 }
 
 
 func (ctx *serviceInspectContext) HasResourceLimits() bool {
 func (ctx *serviceInspectContext) HasResourceLimits() bool {
+	if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Limits == nil {
+		return false
+	}
 	return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
 	return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
 }
 }
 
 

+ 1 - 4
cli/command/image/cmd.go

@@ -13,10 +13,7 @@ func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "image",
 		Use:   "image",
 		Short: "Manage images",
 		Short: "Manage images",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		NewBuildCommand(dockerCli),
 		NewBuildCommand(dockerCli),

+ 4 - 2
cli/command/image/load.go

@@ -3,13 +3,13 @@ package image
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"os"
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/pkg/system"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
@@ -43,7 +43,9 @@ func runLoad(dockerCli *command.DockerCli, opts loadOptions) error {
 
 
 	var input io.Reader = dockerCli.In()
 	var input io.Reader = dockerCli.In()
 	if opts.input != "" {
 	if opts.input != "" {
-		file, err := os.Open(opts.input)
+		// We use system.OpenSequential to use sequential file access on Windows, avoiding
+		// depleting the standby list un-necessarily. On Linux, this equates to a regular os.Open.
+		file, err := system.OpenSequential(opts.input)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}

+ 2 - 2
cli/command/image/tag.go

@@ -18,8 +18,8 @@ func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command {
 	var opts tagOptions
 	var opts tagOptions
 
 
 	cmd := &cobra.Command{
 	cmd := &cobra.Command{
-		Use:   "tag IMAGE[:TAG] IMAGE[:TAG]",
-		Short: "Tag an image into a repository",
+		Use:   "tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]",
+		Short: "Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE",
 		Args:  cli.ExactArgs(2),
 		Args:  cli.ExactArgs(2),
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			opts.image = args[0]
 			opts.image = args[0]

+ 1 - 4
cli/command/network/cmd.go

@@ -13,10 +13,7 @@ func NewNetworkCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "network",
 		Use:   "network",
 		Short: "Manage networks",
 		Short: "Manage networks",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newConnectCommand(dockerCli),
 		newConnectCommand(dockerCli),

+ 1 - 4
cli/command/node/cmd.go

@@ -14,10 +14,7 @@ func NewNodeCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "node",
 		Use:   "node",
 		Short: "Manage Swarm nodes",
 		Short: "Manage Swarm nodes",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newDemoteCommand(dockerCli),
 		newDemoteCommand(dockerCli),

+ 1 - 4
cli/command/plugin/cmd.go

@@ -12,10 +12,7 @@ func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "plugin",
 		Use:   "plugin",
 		Short: "Manage plugins",
 		Short: "Manage plugins",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 
 
 	cmd.AddCommand(
 	cmd.AddCommand(

+ 1 - 5
cli/command/secret/cmd.go

@@ -1,8 +1,6 @@
 package secret
 package secret
 
 
 import (
 import (
-	"fmt"
-
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 
 
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
@@ -15,9 +13,7 @@ func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "secret",
 		Use:   "secret",
 		Short: "Manage Docker secrets",
 		Short: "Manage Docker secrets",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newSecretListCommand(dockerCli),
 		newSecretListCommand(dockerCli),

+ 1 - 4
cli/command/service/cmd.go

@@ -13,10 +13,7 @@ func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "service",
 		Use:   "service",
 		Short: "Manage services",
 		Short: "Manage services",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newCreateCommand(dockerCli),
 		newCreateCommand(dockerCli),

+ 4 - 0
cli/command/service/create.go

@@ -90,6 +90,10 @@ func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error {
 		return err
 		return err
 	}
 	}
 
 
+	for _, warning := range response.Warnings {
+		fmt.Fprintln(dockerCli.Err(), warning)
+	}
+
 	fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
 	fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
 	return nil
 	return nil
 }
 }

+ 5 - 1
cli/command/service/scale.go

@@ -82,11 +82,15 @@ func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint6
 
 
 	serviceMode.Replicated.Replicas = &scale
 	serviceMode.Replicated.Replicas = &scale
 
 
-	err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	response, err := client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	for _, warning := range response.Warnings {
+		fmt.Fprintln(dockerCli.Err(), warning)
+	}
+
 	fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
 	fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
 	return nil
 	return nil
 }
 }

+ 5 - 1
cli/command/service/update.go

@@ -133,11 +133,15 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str
 		updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
 		updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
 	}
 	}
 
 
-	err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
+	response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	for _, warning := range response.Warnings {
+		fmt.Fprintln(dockerCli.Err(), warning)
+	}
+
 	fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
 	fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
 	return nil
 	return nil
 }
 }

+ 2 - 5
cli/command/stack/cmd.go

@@ -12,11 +12,8 @@ func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "stack",
 		Use:   "stack",
 		Short: "Manage Docker stacks",
 		Short: "Manage Docker stacks",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
-		Tags: map[string]string{"experimental": "", "version": "1.25"},
+		RunE:  dockerCli.ShowHelp,
+		Tags:  map[string]string{"experimental": "", "version": "1.25"},
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newDeployCommand(dockerCli),
 		newDeployCommand(dockerCli),

+ 8 - 3
cli/command/stack/deploy.go

@@ -408,15 +408,20 @@ func deployServices(
 			if sendAuth {
 			if sendAuth {
 				updateOpts.EncodedRegistryAuth = encodedAuth
 				updateOpts.EncodedRegistryAuth = encodedAuth
 			}
 			}
-			if err := apiClient.ServiceUpdate(
+			response, err := apiClient.ServiceUpdate(
 				ctx,
 				ctx,
 				service.ID,
 				service.ID,
 				service.Version,
 				service.Version,
 				serviceSpec,
 				serviceSpec,
 				updateOpts,
 				updateOpts,
-			); err != nil {
+			)
+			if err != nil {
 				return err
 				return err
 			}
 			}
+
+			for _, warning := range response.Warnings {
+				fmt.Fprintln(dockerCli.Err(), warning)
+			}
 		} else {
 		} else {
 			fmt.Fprintf(out, "Creating service %s\n", name)
 			fmt.Fprintf(out, "Creating service %s\n", name)
 
 
@@ -526,7 +531,7 @@ func convertService(
 func convertExtraHosts(extraHosts map[string]string) []string {
 func convertExtraHosts(extraHosts map[string]string) []string {
 	hosts := []string{}
 	hosts := []string{}
 	for host, ip := range extraHosts {
 	for host, ip := range extraHosts {
-		hosts = append(hosts, fmt.Sprintf("%s %s", host, ip))
+		hosts = append(hosts, fmt.Sprintf("%s %s", ip, host))
 	}
 	}
 	return hosts
 	return hosts
 }
 }

+ 1 - 1
cli/command/stack/list.go

@@ -72,7 +72,7 @@ func printTable(out io.Writer, stacks []*stack) {
 
 
 type stack struct {
 type stack struct {
 	// Name is the name of the stack
 	// Name is the name of the stack
-	Name     string
+	Name string
 	// Services is the number of the services
 	// Services is the number of the services
 	Services int
 	Services int
 }
 }

+ 1 - 4
cli/command/swarm/cmd.go

@@ -13,10 +13,7 @@ func NewSwarmCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "swarm",
 		Use:   "swarm",
 		Short: "Manage Swarm",
 		Short: "Manage Swarm",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newInitCommand(dockerCli),
 		newInitCommand(dockerCli),

+ 1 - 4
cli/command/system/cmd.go

@@ -13,10 +13,7 @@ func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Use:   "system",
 		Use:   "system",
 		Short: "Manage Docker",
 		Short: "Manage Docker",
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		NewEventsCommand(dockerCli),
 		NewEventsCommand(dockerCli),

+ 1 - 4
cli/command/volume/cmd.go

@@ -14,10 +14,7 @@ func NewVolumeCommand(dockerCli *command.DockerCli) *cobra.Command {
 		Short: "Manage volumes",
 		Short: "Manage volumes",
 		Long:  volumeDescription,
 		Long:  volumeDescription,
 		Args:  cli.NoArgs,
 		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-		},
+		RunE:  dockerCli.ShowHelp,
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newCreateCommand(dockerCli),
 		newCreateCommand(dockerCli),

+ 1 - 1
client/interface.go

@@ -124,7 +124,7 @@ type ServiceAPIClient interface {
 	ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
 	ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
 	ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
 	ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
 	ServiceRemove(ctx context.Context, serviceID string) error
 	ServiceRemove(ctx context.Context, serviceID string) error
-	ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) error
+	ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
 	ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error)
 	ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error)
 	TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
 	TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
 	TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
 	TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)

+ 9 - 3
client/plugin_install.go

@@ -10,7 +10,7 @@ import (
 )
 )
 
 
 // PluginInstall installs a plugin
 // PluginInstall installs a plugin
-func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error {
+func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (err error) {
 	// FIXME(vdemeester) name is a ref, we might want to parse/validate it here.
 	// FIXME(vdemeester) name is a ref, we might want to parse/validate it here.
 	query := url.Values{}
 	query := url.Values{}
 	query.Set("name", name)
 	query.Set("name", name)
@@ -27,6 +27,14 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
 		ensureReaderClosed(resp)
 		ensureReaderClosed(resp)
 		return err
 		return err
 	}
 	}
+
+	defer func() {
+		if err != nil {
+			delResp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
+			ensureReaderClosed(delResp)
+		}
+	}()
+
 	var privileges types.PluginPrivileges
 	var privileges types.PluginPrivileges
 	if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
 	if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
 		ensureReaderClosed(resp)
 		ensureReaderClosed(resp)
@@ -40,8 +48,6 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
 			return err
 			return err
 		}
 		}
 		if !accept {
 		if !accept {
-			resp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
-			ensureReaderClosed(resp)
 			return pluginPermissionDenied{name}
 			return pluginPermissionDenied{name}
 		}
 		}
 	}
 	}

+ 9 - 2
client/service_update.go

@@ -1,6 +1,7 @@
 package client
 package client
 
 
 import (
 import (
+	"encoding/json"
 	"net/url"
 	"net/url"
 	"strconv"
 	"strconv"
 
 
@@ -10,7 +11,7 @@ import (
 )
 )
 
 
 // ServiceUpdate updates a Service.
 // ServiceUpdate updates a Service.
-func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) error {
+func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
 	var (
 	var (
 		headers map[string][]string
 		headers map[string][]string
 		query   = url.Values{}
 		query   = url.Values{}
@@ -28,7 +29,13 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
 
 
 	query.Set("version", strconv.FormatUint(version.Index, 10))
 	query.Set("version", strconv.FormatUint(version.Index, 10))
 
 
+	var response types.ServiceUpdateResponse
 	resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
 	resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
+	if err != nil {
+		return response, err
+	}
+
+	err = json.NewDecoder(resp.body).Decode(&response)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)
-	return err
+	return response, err
 }
 }

+ 3 - 3
client/service_update_test.go

@@ -19,7 +19,7 @@ func TestServiceUpdateError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
+	_, err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -64,12 +64,12 @@ func TestServiceUpdate(t *testing.T) {
 				}
 				}
 				return &http.Response{
 				return &http.Response{
 					StatusCode: http.StatusOK,
 					StatusCode: http.StatusOK,
-					Body:       ioutil.NopCloser(bytes.NewReader([]byte("body"))),
+					Body:       ioutil.NopCloser(bytes.NewReader([]byte("{}"))),
 				}, nil
 				}, nil
 			}),
 			}),
 		}
 		}
 
 
-		err := client.ServiceUpdate(context.Background(), "service_id", updateCase.swarmVersion, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
+		_, err := client.ServiceUpdate(context.Background(), "service_id", updateCase.swarmVersion, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}

+ 31 - 6
cmd/docker/docker.go

@@ -1,6 +1,7 @@
 package main
 package main
 
 
 import (
 import (
+	"errors"
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 
 
@@ -34,27 +35,37 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
 				showVersion()
 				showVersion()
 				return nil
 				return nil
 			}
 			}
-			cmd.SetOutput(dockerCli.Err())
-			cmd.HelpFunc()(cmd, args)
-			return nil
+			return dockerCli.ShowHelp(cmd, args)
 		},
 		},
 		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
 		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			// daemon command is special, we redirect directly to another binary
+			if cmd.Name() == "daemon" {
+				return nil
+			}
 			// flags must be the top-level command flags, not cmd.Flags()
 			// flags must be the top-level command flags, not cmd.Flags()
 			opts.Common.SetDefaultOptions(flags)
 			opts.Common.SetDefaultOptions(flags)
 			dockerPreRun(opts)
 			dockerPreRun(opts)
-			return dockerCli.Initialize(opts)
+			if err := dockerCli.Initialize(opts); err != nil {
+				return err
+			}
+			return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
 		},
 		},
 	}
 	}
 	cli.SetupRootCommand(cmd)
 	cli.SetupRootCommand(cmd)
 
 
 	cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
 	cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
-		if dockerCli.Client() == nil {
+		if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed.
 			// flags must be the top-level command flags, not cmd.Flags()
 			// flags must be the top-level command flags, not cmd.Flags()
 			opts.Common.SetDefaultOptions(flags)
 			opts.Common.SetDefaultOptions(flags)
 			dockerPreRun(opts)
 			dockerPreRun(opts)
 			dockerCli.Initialize(opts)
 			dockerCli.Initialize(opts)
 		}
 		}
 
 
+		if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
+			ccmd.Println(err)
+			return
+		}
+
 		hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
 		hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
 
 
 		if err := ccmd.Help(); err != nil {
 		if err := ccmd.Help(); err != nil {
@@ -79,7 +90,7 @@ func noArgs(cmd *cobra.Command, args []string) error {
 		return nil
 		return nil
 	}
 	}
 	return fmt.Errorf(
 	return fmt.Errorf(
-		"docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0])
+		"docker: '%s' is not a docker command.\nSee 'docker --help'", args[0])
 }
 }
 
 
 func main() {
 func main() {
@@ -153,3 +164,17 @@ func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperi
 		}
 		}
 	}
 	}
 }
 }
+
+func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error {
+	if !hasExperimental {
+		if _, ok := cmd.Tags["experimental"]; ok {
+			return errors.New("only supported with experimental daemon")
+		}
+	}
+
+	if cmdVersion, ok := cmd.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
+		return fmt.Errorf("only supported with daemon version >= %s", cmdVersion)
+	}
+
+	return nil
+}

+ 16 - 8
cmd/dockerd/daemon.go

@@ -17,6 +17,7 @@ import (
 	"github.com/docker/docker/api/server/middleware"
 	"github.com/docker/docker/api/server/middleware"
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/api/server/router/build"
 	"github.com/docker/docker/api/server/router/build"
+	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
 	"github.com/docker/docker/api/server/router/container"
 	"github.com/docker/docker/api/server/router/container"
 	"github.com/docker/docker/api/server/router/image"
 	"github.com/docker/docker/api/server/router/image"
 	"github.com/docker/docker/api/server/router/network"
 	"github.com/docker/docker/api/server/router/network"
@@ -461,25 +462,32 @@ func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) {
 func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
 func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
 	decoder := runconfig.ContainerDecoder{}
 	decoder := runconfig.ContainerDecoder{}
 
 
-	routers := []router.Router{}
-
-	// we need to add the checkpoint router before the container router or the DELETE gets masked
-	routers = addExperimentalRouters(routers, d, decoder)
-
-	routers = append(routers, []router.Router{
+	routers := []router.Router{
+		// we need to add the checkpoint router before the container router or the DELETE gets masked
+		checkpointrouter.NewRouter(d, decoder),
 		container.NewRouter(d, decoder),
 		container.NewRouter(d, decoder),
 		image.NewRouter(d, decoder),
 		image.NewRouter(d, decoder),
 		systemrouter.NewRouter(d, c),
 		systemrouter.NewRouter(d, c),
 		volume.NewRouter(d),
 		volume.NewRouter(d),
 		build.NewRouter(dockerfile.NewBuildManager(d)),
 		build.NewRouter(dockerfile.NewBuildManager(d)),
-		swarmrouter.NewRouter(d, c),
+		swarmrouter.NewRouter(c),
 		pluginrouter.NewRouter(plugin.GetManager()),
 		pluginrouter.NewRouter(plugin.GetManager()),
-	}...)
+	}
 
 
 	if d.NetworkControllerEnabled() {
 	if d.NetworkControllerEnabled() {
 		routers = append(routers, network.NewRouter(d, c))
 		routers = append(routers, network.NewRouter(d, c))
 	}
 	}
 
 
+	if d.HasExperimental() {
+		for _, r := range routers {
+			for _, route := range r.Routes() {
+				if experimental, ok := route.(router.ExperimentalRoute); ok {
+					experimental.Enable()
+				}
+			}
+		}
+	}
+
 	s.InitRouter(utils.IsDebugEnabled(), routers...)
 	s.InitRouter(utils.IsDebugEnabled(), routers...)
 }
 }
 
 

+ 0 - 15
cmd/dockerd/routes_experimental.go

@@ -1,15 +0,0 @@
-package main
-
-import (
-	"github.com/docker/docker/api/server/httputils"
-	"github.com/docker/docker/api/server/router"
-	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
-	"github.com/docker/docker/daemon"
-)
-
-func addExperimentalRouters(routers []router.Router, d *daemon.Daemon, decoder httputils.ContainerDecoder) []router.Router {
-	if !d.HasExperimental() {
-		return []router.Router{}
-	}
-	return append(routers, checkpointrouter.NewRouter(d, decoder))
-}

+ 1 - 4
container/health.go

@@ -42,10 +42,7 @@ func (s *Health) OpenMonitorChannel() chan struct{} {
 func (s *Health) CloseMonitorChannel() {
 func (s *Health) CloseMonitorChannel() {
 	if s.stop != nil {
 	if s.stop != nil {
 		logrus.Debug("CloseMonitorChannel: waiting for probe to stop")
 		logrus.Debug("CloseMonitorChannel: waiting for probe to stop")
-		// This channel does not buffer. Once the write succeeds, the monitor
-		// has read the stop request and will not make any further updates
-		// to c.State.Health.
-		s.stop <- struct{}{}
+		close(s.stop)
 		s.stop = nil
 		s.stop = nil
 		logrus.Debug("CloseMonitorChannel done")
 		logrus.Debug("CloseMonitorChannel done")
 	}
 	}

+ 4 - 1
contrib/check-config.sh

@@ -188,7 +188,6 @@ fi
 
 
 flags=(
 flags=(
 	NAMESPACES {NET,PID,IPC,UTS}_NS
 	NAMESPACES {NET,PID,IPC,UTS}_NS
-	DEVPTS_MULTIPLE_INSTANCES
 	CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
 	CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
 	KEYS
 	KEYS
 	VETH BRIDGE BRIDGE_NETFILTER
 	VETH BRIDGE BRIDGE_NETFILTER
@@ -200,6 +199,10 @@ flags=(
 	POSIX_MQUEUE
 	POSIX_MQUEUE
 )
 )
 check_flags "${flags[@]}"
 check_flags "${flags[@]}"
+if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -lt 8 ]; then
+        check_flags DEVPTS_MULTIPLE_INSTANCES
+fi
+
 echo
 echo
 
 
 echo 'Optional Features:'
 echo 'Optional Features:'

+ 4 - 2
contrib/completion/zsh/_docker

@@ -1961,7 +1961,7 @@ __docker_swarm_subcommand() {
 __docker_system_commands() {
 __docker_system_commands() {
     local -a _docker_system_subcommands
     local -a _docker_system_subcommands
     _docker_system_subcommands=(
     _docker_system_subcommands=(
-        "df:Show docker disk usage"
+        "df:Show docker filesystem usage"
         "events:Get real time events from the server"
         "events:Get real time events from the server"
         "info:Display system-wide information"
         "info:Display system-wide information"
         "prune:Remove unused data"
         "prune:Remove unused data"
@@ -1978,7 +1978,9 @@ __docker_system_subcommand() {
 
 
     case "$words[1]" in
     case "$words[1]" in
         (df)
         (df)
-            # @TODO
+            _arguments $(__docker_arguments) \
+                $opts_help \
+                "($help -v --verbose)"{-v,--verbose}"[Show detailed information on space usage]" && ret=0
             ;;
             ;;
         (events)
         (events)
             _arguments $(__docker_arguments) \
             _arguments $(__docker_arguments) \

+ 66 - 37
daemon/cluster/cluster.go

@@ -16,6 +16,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	distreference "github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	apierrors "github.com/docker/docker/api/errors"
 	apitypes "github.com/docker/docker/api/types"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
@@ -69,7 +70,7 @@ var ErrSwarmJoinTimeoutReached = fmt.Errorf("Timeout was reached before node was
 // ErrSwarmLocked is returned if the swarm is encrypted and needs a key to unlock it.
 // ErrSwarmLocked is returned if the swarm is encrypted and needs a key to unlock it.
 var ErrSwarmLocked = fmt.Errorf("Swarm is encrypted and needs to be unlocked before it can be used. Please use \"docker swarm unlock\" to unlock it.")
 var ErrSwarmLocked = fmt.Errorf("Swarm is encrypted and needs to be unlocked before it can be used. Please use \"docker swarm unlock\" to unlock it.")
 
 
-// ErrSwarmCertificatesExipred is returned if docker was not started for the whole validity period and they had no chance to renew automatically.
+// ErrSwarmCertificatesExpired is returned if docker was not started for the whole validity period and they had no chance to renew automatically.
 var ErrSwarmCertificatesExpired = errors.New("Swarm certificates have expired. To replace them, leave the swarm and join again.")
 var ErrSwarmCertificatesExpired = errors.New("Swarm certificates have expired. To replace them, leave the swarm and join again.")
 
 
 // NetworkSubnetsProvider exposes functions for retrieving the subnets
 // NetworkSubnetsProvider exposes functions for retrieving the subnets
@@ -587,6 +588,15 @@ func (c *Cluster) GetUnlockKey() (string, error) {
 
 
 // UnlockSwarm provides a key to decrypt data that is encrypted at rest.
 // UnlockSwarm provides a key to decrypt data that is encrypted at rest.
 func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
 func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
+	c.RLock()
+	if !c.isActiveManager() {
+		if err := c.errNoManager(); err != ErrSwarmLocked {
+			c.RUnlock()
+			return err
+		}
+	}
+	c.RUnlock()
+
 	key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
 	key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -1008,16 +1018,25 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
 
 
 // imageWithDigestString takes an image such as name or name:tag
 // imageWithDigestString takes an image such as name or name:tag
 // and returns the image pinned to a digest, such as name@sha256:34234...
 // and returns the image pinned to a digest, such as name@sha256:34234...
+// Due to the difference between the docker/docker/reference, and the
+// docker/distribution/reference packages, we're parsing the image twice.
+// As the two packages converge, this function should be simplified.
+// TODO(nishanttotla): After the packages converge, the function must
+// convert distreference.Named -> distreference.Canonical, and the logic simplified.
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
-	ref, err := reference.ParseNamed(image)
+	ref, err := distreference.ParseNamed(image)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
 	// only query registry if not a canonical reference (i.e. with digest)
 	// only query registry if not a canonical reference (i.e. with digest)
-	if _, ok := ref.(reference.Canonical); !ok {
-		ref = reference.WithDefaultTag(ref)
-
-		namedTaggedRef, ok := ref.(reference.NamedTagged)
+	if _, ok := ref.(distreference.Canonical); !ok {
+		// create a docker/docker/reference Named object because GetRepository needs it
+		dockerRef, err := reference.ParseNamed(image)
+		if err != nil {
+			return "", err
+		}
+		dockerRef = reference.WithDefaultTag(dockerRef)
+		namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
 		if !ok {
 		if !ok {
 			return "", fmt.Errorf("unable to cast image to NamedTagged reference object")
 			return "", fmt.Errorf("unable to cast image to NamedTagged reference object")
 		}
 		}
@@ -1031,28 +1050,23 @@ func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authC
 			return "", err
 			return "", err
 		}
 		}
 
 
-		// TODO(nishanttotla): Currently, the service would lose the tag while calling WithDigest
-		// To prevent this, we create the image string manually, which is a bad idea in general
-		// This will be fixed when https://github.com/docker/distribution/pull/2044 is vendored
-		// namedDigestedRef, err := reference.WithDigest(ref, dscrptr.Digest)
-		// if err != nil {
-		// 	return "", err
-		// }
-		// return namedDigestedRef.String(), nil
-		return image + "@" + dscrptr.Digest.String(), nil
-	} else {
-		// reference already contains a digest, so just return it
-		return ref.String(), nil
+		namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
+		if err != nil {
+			return "", err
+		}
+		return namedDigestedRef.String(), nil
 	}
 	}
+	// reference already contains a digest, so just return it
+	return ref.String(), nil
 }
 }
 
 
 // CreateService creates a new service in a managed swarm cluster.
 // CreateService creates a new service in a managed swarm cluster.
-func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string, error) {
+func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apitypes.ServiceCreateResponse, error) {
 	c.RLock()
 	c.RLock()
 	defer c.RUnlock()
 	defer c.RUnlock()
 
 
 	if !c.isActiveManager() {
 	if !c.isActiveManager() {
-		return "", c.errNoManager()
+		return nil, c.errNoManager()
 	}
 	}
 
 
 	ctx, cancel := c.getRequestContext()
 	ctx, cancel := c.getRequestContext()
@@ -1060,17 +1074,17 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
 
 
 	err := c.populateNetworkID(ctx, c.client, &s)
 	err := c.populateNetworkID(ctx, c.client, &s)
 	if err != nil {
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	}
 
 
 	serviceSpec, err := convert.ServiceSpecToGRPC(s)
 	serviceSpec, err := convert.ServiceSpecToGRPC(s)
 	if err != nil {
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	}
 
 
 	ctnr := serviceSpec.Task.GetContainer()
 	ctnr := serviceSpec.Task.GetContainer()
 	if ctnr == nil {
 	if ctnr == nil {
-		return "", fmt.Errorf("service does not use container tasks")
+		return nil, fmt.Errorf("service does not use container tasks")
 	}
 	}
 
 
 	if encodedAuth != "" {
 	if encodedAuth != "" {
@@ -1084,11 +1098,15 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
 			logrus.Warnf("invalid authconfig: %v", err)
 			logrus.Warnf("invalid authconfig: %v", err)
 		}
 		}
 	}
 	}
+
+	resp := &apitypes.ServiceCreateResponse{}
+
 	// pin image by digest
 	// pin image by digest
 	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
 	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
 		digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig)
 		digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig)
 		if err != nil {
 		if err != nil {
 			logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error())
 			logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error())
+			resp.Warnings = append(resp.Warnings, fmt.Sprintf("unable to pin image %s to digest: %s", ctnr.Image, err.Error()))
 		} else {
 		} else {
 			logrus.Debugf("pinning image %s by digest: %s", ctnr.Image, digestImage)
 			logrus.Debugf("pinning image %s by digest: %s", ctnr.Image, digestImage)
 			ctnr.Image = digestImage
 			ctnr.Image = digestImage
@@ -1097,10 +1115,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
 
 
 	r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
 	r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
 	if err != nil {
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	}
 
 
-	return r.Service.ID, nil
+	resp.ID = r.Service.ID
+	return resp, nil
 }
 }
 
 
 // GetService returns a service based on an ID or name.
 // GetService returns a service based on an ID or name.
@@ -1123,12 +1142,12 @@ func (c *Cluster) GetService(input string) (types.Service, error) {
 }
 }
 
 
 // UpdateService updates existing service to match new properties.
 // UpdateService updates existing service to match new properties.
-func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, encodedAuth string, registryAuthFrom string) error {
+func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, encodedAuth string, registryAuthFrom string) (*apitypes.ServiceUpdateResponse, error) {
 	c.RLock()
 	c.RLock()
 	defer c.RUnlock()
 	defer c.RUnlock()
 
 
 	if !c.isActiveManager() {
 	if !c.isActiveManager() {
-		return c.errNoManager()
+		return nil, c.errNoManager()
 	}
 	}
 
 
 	ctx, cancel := c.getRequestContext()
 	ctx, cancel := c.getRequestContext()
@@ -1136,22 +1155,22 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 
 
 	err := c.populateNetworkID(ctx, c.client, &spec)
 	err := c.populateNetworkID(ctx, c.client, &spec)
 	if err != nil {
 	if err != nil {
-		return err
+		return nil, err
 	}
 	}
 
 
 	serviceSpec, err := convert.ServiceSpecToGRPC(spec)
 	serviceSpec, err := convert.ServiceSpecToGRPC(spec)
 	if err != nil {
 	if err != nil {
-		return err
+		return nil, err
 	}
 	}
 
 
 	currentService, err := getService(ctx, c.client, serviceIDOrName)
 	currentService, err := getService(ctx, c.client, serviceIDOrName)
 	if err != nil {
 	if err != nil {
-		return err
+		return nil, err
 	}
 	}
 
 
 	newCtnr := serviceSpec.Task.GetContainer()
 	newCtnr := serviceSpec.Task.GetContainer()
 	if newCtnr == nil {
 	if newCtnr == nil {
-		return fmt.Errorf("service does not use container tasks")
+		return nil, fmt.Errorf("service does not use container tasks")
 	}
 	}
 
 
 	if encodedAuth != "" {
 	if encodedAuth != "" {
@@ -1165,14 +1184,14 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 			ctnr = currentService.Spec.Task.GetContainer()
 			ctnr = currentService.Spec.Task.GetContainer()
 		case apitypes.RegistryAuthFromPreviousSpec:
 		case apitypes.RegistryAuthFromPreviousSpec:
 			if currentService.PreviousSpec == nil {
 			if currentService.PreviousSpec == nil {
-				return fmt.Errorf("service does not have a previous spec")
+				return nil, fmt.Errorf("service does not have a previous spec")
 			}
 			}
 			ctnr = currentService.PreviousSpec.Task.GetContainer()
 			ctnr = currentService.PreviousSpec.Task.GetContainer()
 		default:
 		default:
-			return fmt.Errorf("unsupported registryAuthFromValue")
+			return nil, fmt.Errorf("unsupported registryAuthFromValue")
 		}
 		}
 		if ctnr == nil {
 		if ctnr == nil {
-			return fmt.Errorf("service does not use container tasks")
+			return nil, fmt.Errorf("service does not use container tasks")
 		}
 		}
 		newCtnr.PullOptions = ctnr.PullOptions
 		newCtnr.PullOptions = ctnr.PullOptions
 		// update encodedAuth so it can be used to pin image by digest
 		// update encodedAuth so it can be used to pin image by digest
@@ -1188,11 +1207,15 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 			logrus.Warnf("invalid authconfig: %v", err)
 			logrus.Warnf("invalid authconfig: %v", err)
 		}
 		}
 	}
 	}
+
+	resp := &apitypes.ServiceUpdateResponse{}
+
 	// pin image by digest
 	// pin image by digest
 	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
 	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
 		digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig)
 		digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig)
 		if err != nil {
 		if err != nil {
 			logrus.Warnf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error())
 			logrus.Warnf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error())
+			resp.Warnings = append(resp.Warnings, fmt.Sprintf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error()))
 		} else if newCtnr.Image != digestImage {
 		} else if newCtnr.Image != digestImage {
 			logrus.Debugf("pinning image %s by digest: %s", newCtnr.Image, digestImage)
 			logrus.Debugf("pinning image %s by digest: %s", newCtnr.Image, digestImage)
 			newCtnr.Image = digestImage
 			newCtnr.Image = digestImage
@@ -1209,7 +1232,8 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 			},
 			},
 		},
 		},
 	)
 	)
-	return err
+
+	return resp, err
 }
 }
 
 
 // RemoveService removes a service from a managed swarm cluster.
 // RemoveService removes a service from a managed swarm cluster.
@@ -1369,7 +1393,7 @@ func (c *Cluster) GetNode(input string) (types.Node, error) {
 }
 }
 
 
 // UpdateNode updates existing nodes properties.
 // UpdateNode updates existing nodes properties.
-func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec) error {
+func (c *Cluster) UpdateNode(input string, version uint64, spec types.NodeSpec) error {
 	c.RLock()
 	c.RLock()
 	defer c.RUnlock()
 	defer c.RUnlock()
 
 
@@ -1385,10 +1409,15 @@ func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec)
 	ctx, cancel := c.getRequestContext()
 	ctx, cancel := c.getRequestContext()
 	defer cancel()
 	defer cancel()
 
 
+	currentNode, err := getNode(ctx, c.client, input)
+	if err != nil {
+		return err
+	}
+
 	_, err = c.client.UpdateNode(
 	_, err = c.client.UpdateNode(
 		ctx,
 		ctx,
 		&swarmapi.UpdateNodeRequest{
 		&swarmapi.UpdateNodeRequest{
-			NodeID: nodeID,
+			NodeID: currentNode.ID,
 			Spec:   &nodeSpec,
 			Spec:   &nodeSpec,
 			NodeVersion: &swarmapi.Version{
 			NodeVersion: &swarmapi.Version{
 				Index: version,
 				Index: version,

+ 2 - 6
daemon/commit.go

@@ -17,7 +17,6 @@ import (
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/reference"
-	"github.com/docker/go-connections/nat"
 )
 )
 
 
 // merge merges two Config, the image container configuration (defaults values),
 // merge merges two Config, the image container configuration (defaults values),
@@ -32,9 +31,6 @@ func merge(userConf, imageConf *containertypes.Config) error {
 	if len(userConf.ExposedPorts) == 0 {
 	if len(userConf.ExposedPorts) == 0 {
 		userConf.ExposedPorts = imageConf.ExposedPorts
 		userConf.ExposedPorts = imageConf.ExposedPorts
 	} else if imageConf.ExposedPorts != nil {
 	} else if imageConf.ExposedPorts != nil {
-		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(nat.PortSet)
-		}
 		for port := range imageConf.ExposedPorts {
 		for port := range imageConf.ExposedPorts {
 			if _, exists := userConf.ExposedPorts[port]; !exists {
 			if _, exists := userConf.ExposedPorts[port]; !exists {
 				userConf.ExposedPorts[port] = struct{}{}
 				userConf.ExposedPorts[port] = struct{}{}
@@ -244,8 +240,8 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 	}
 	}
 
 
 	attributes := map[string]string{
 	attributes := map[string]string{
-		"comment": c.Comment,
-		"imageID": id.String(),
+		"comment":  c.Comment,
+		"imageID":  id.String(),
 		"imageRef": imageRef,
 		"imageRef": imageRef,
 	}
 	}
 	daemon.LogContainerEventWithAttributes(container, "commit", attributes)
 	daemon.LogContainerEventWithAttributes(container, "commit", attributes)

+ 0 - 8
daemon/create_windows.go

@@ -16,14 +16,6 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
 		hostConfig.Isolation = daemon.defaultIsolation
 		hostConfig.Isolation = daemon.defaultIsolation
 	}
 	}
 
 
-	if err := daemon.Mount(container); err != nil {
-		return nil
-	}
-	defer daemon.Unmount(container)
-	if err := container.SetupWorkingDirectory(0, 0); err != nil {
-		return err
-	}
-
 	for spec := range config.Volumes {
 	for spec := range config.Volumes {
 
 
 		mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
 		mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)

+ 5 - 6
daemon/daemon.go

@@ -550,7 +550,12 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		driverName = config.GraphDriver
 		driverName = config.GraphDriver
 	}
 	}
 
 
+	d.RegistryService = registryService
 	d.PluginStore = pluginstore.NewStore(config.Root)
 	d.PluginStore = pluginstore.NewStore(config.Root)
+	// Plugin system initialization should happen before restore. Do not change order.
+	if err := d.pluginInit(config, containerdRemote); err != nil {
+		return nil, err
+	}
 
 
 	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
 	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
 		StorePath:                 config.Root,
 		StorePath:                 config.Root,
@@ -649,7 +654,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		Type:   config.LogConfig.Type,
 		Type:   config.LogConfig.Type,
 		Config: config.LogConfig.Config,
 		Config: config.LogConfig.Config,
 	}
 	}
-	d.RegistryService = registryService
 	d.EventsService = eventsService
 	d.EventsService = eventsService
 	d.volumes = volStore
 	d.volumes = volStore
 	d.root = config.Root
 	d.root = config.Root
@@ -668,11 +672,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	// Plugin system initialization should happen before restore. Do not change order.
-	if err := d.pluginInit(config, containerdRemote); err != nil {
-		return nil, err
-	}
-
 	if err := d.restore(); err != nil {
 	if err := d.restore(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 8 - 3
daemon/daemon_unix.go

@@ -112,9 +112,7 @@ func getCPUResources(config containertypes.Resources) *specs.CPU {
 	}
 	}
 
 
 	if config.NanoCPUs > 0 {
 	if config.NanoCPUs > 0 {
-		// Use the default setting of 100ms, as is specified in:
 		// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
 		// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
-		//    	cpu.cfs_period_us=100ms
 		period := uint64(100 * time.Millisecond / time.Microsecond)
 		period := uint64(100 * time.Millisecond / time.Microsecond)
 		quota := uint64(config.NanoCPUs) * period / 1e9
 		quota := uint64(config.NanoCPUs) * period / 1e9
 		cpu.Period = &period
 		cpu.Period = &period
@@ -361,8 +359,15 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
 	if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
 	if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
 		return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
 		return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
 	}
 	}
+	// The highest precision we could get on Linux is 0.001, by setting
+	//   cpu.cfs_period_us=1000ms
+	//   cpu.cfs_quota=1ms
+	// See the following link for details:
+	// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
+	// Here we don't set the lower limit and it is up to the underlying platform (e.g., Linux) to return an error.
+	// The error message is 0.01 so that this is consistent with Windows
 	if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
 	if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
-		return warnings, fmt.Errorf("Range of Nano CPUs is from 1 to %d", int64(sysinfo.NumCPU())*1e9)
+		return warnings, fmt.Errorf("Range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
 	}
 	}
 
 
 	if resources.CPUShares > 0 && !sysInfo.CPUShares {
 	if resources.CPUShares > 0 && !sysInfo.CPUShares {

+ 3 - 1
daemon/daemon_windows.go

@@ -129,8 +129,10 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool
 	if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
 	if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
 		return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set")
 		return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set")
 	}
 	}
+	// The precision we could get is 0.01, because on Windows we have to convert to CPUPercent.
+	// We don't set the lower limit here and it is up to the underlying platform (e.g., Windows) to return an error.
 	if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
 	if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
-		return warnings, fmt.Errorf("range of Nano CPUs is from 1 to %d", int64(sysinfo.NumCPU())*1e9)
+		return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
 	}
 	}
 
 
 	if len(resources.BlkioDeviceReadBps) > 0 {
 	if len(resources.BlkioDeviceReadBps) > 0 {

+ 30 - 1
daemon/debugtrap.go

@@ -27,7 +27,36 @@ func (d *Daemon) dumpDaemon(dir string) (string, error) {
 		return "", errors.Wrap(err, "failed to open file to write the daemon datastructure dump")
 		return "", errors.Wrap(err, "failed to open file to write the daemon datastructure dump")
 	}
 	}
 	defer f.Close()
 	defer f.Close()
-	spew.Fdump(f, d) // Does not return an error
+
+	dump := struct {
+		containers      interface{}
+		names           interface{}
+		links           interface{}
+		execs           interface{}
+		volumes         interface{}
+		images          interface{}
+		layers          interface{}
+		imageReferences interface{}
+		downloads       interface{}
+		uploads         interface{}
+		registry        interface{}
+		plugins         interface{}
+	}{
+		containers:      d.containers,
+		execs:           d.execCommands,
+		volumes:         d.volumes,
+		images:          d.imageStore,
+		layers:          d.layerStore,
+		imageReferences: d.referenceStore,
+		downloads:       d.downloadManager,
+		uploads:         d.uploadManager,
+		registry:        d.RegistryService,
+		plugins:         d.PluginStore,
+		names:           d.nameIndex,
+		links:           d.linkIndex,
+	}
+
+	spew.Fdump(f, dump) // Does not return an error
 	f.Sync()
 	f.Sync()
 	return path, nil
 	return path, nil
 }
 }

+ 2 - 1
daemon/graphdriver/plugin.go

@@ -3,6 +3,7 @@ package graphdriver
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"path/filepath"
 
 
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
 )
 )
@@ -26,5 +27,5 @@ func lookupPlugin(name, home string, opts []string, pg plugingetter.PluginGetter
 
 
 func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
 func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
 	proxy := &graphDriverProxy{name, c}
 	proxy := &graphDriverProxy{name, c}
-	return proxy, proxy.Init(home, opts)
+	return proxy, proxy.Init(filepath.Join(home, name), opts)
 }
 }

+ 2 - 4
daemon/graphdriver/windows/windows.go

@@ -165,18 +165,16 @@ func (d *Driver) Exists(id string) bool {
 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
 	if opts != nil {
 	if opts != nil {
 		return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
 		return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
-	} else {
-		return d.create(id, parent, "", false, nil)
 	}
 	}
+	return d.create(id, parent, "", false, nil)
 }
 }
 
 
 // Create creates a new read-only layer with the given id.
 // Create creates a new read-only layer with the given id.
 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
 	if opts != nil {
 	if opts != nil {
 		return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
 		return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
-	} else {
-		return d.create(id, parent, "", true, nil)
 	}
 	}
+	return d.create(id, parent, "", true, nil)
 }
 }
 
 
 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {

+ 22 - 8
daemon/health.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/exec"
 	"github.com/docker/docker/daemon/exec"
@@ -63,11 +64,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, container *container.Cont
 
 
 	cmdSlice := strslice.StrSlice(container.Config.Healthcheck.Test)[1:]
 	cmdSlice := strslice.StrSlice(container.Config.Healthcheck.Test)[1:]
 	if p.shell {
 	if p.shell {
-		if runtime.GOOS != "windows" {
-			cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
-		} else {
-			cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...)
-		}
+		cmdSlice = append(getShell(container.Config), cmdSlice...)
 	}
 	}
 	entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice)
 	entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice)
 	execConfig := exec.NewConfig()
 	execConfig := exec.NewConfig()
@@ -107,10 +104,17 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, container *container.Cont
 }
 }
 
 
 // Update the container's Status.Health struct based on the latest probe's result.
 // Update the container's Status.Health struct based on the latest probe's result.
-func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult) {
+func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult, done chan struct{}) {
 	c.Lock()
 	c.Lock()
 	defer c.Unlock()
 	defer c.Unlock()
 
 
+	// probe may have been cancelled while waiting on lock. Ignore result then
+	select {
+	case <-done:
+		return
+	default:
+	}
+
 	retries := c.Config.Healthcheck.Retries
 	retries := c.Config.Healthcheck.Retries
 	if retries <= 0 {
 	if retries <= 0 {
 		retries = defaultProbeRetries
 		retries = defaultProbeRetries
@@ -183,7 +187,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
 				cancelProbe()
 				cancelProbe()
 				return
 				return
 			case result := <-results:
 			case result := <-results:
-				handleProbeResult(d, c, result)
+				handleProbeResult(d, c, result, stop)
 				// Stop timeout
 				// Stop timeout
 				cancelProbe()
 				cancelProbe()
 			case <-ctx.Done():
 			case <-ctx.Done():
@@ -193,7 +197,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
 					Output:   fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout),
 					Output:   fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout),
 					Start:    startTime,
 					Start:    startTime,
 					End:      time.Now(),
 					End:      time.Now(),
-				})
+				}, stop)
 				cancelProbe()
 				cancelProbe()
 				// Wait for probe to exit (it might take a while to respond to the TERM
 				// Wait for probe to exit (it might take a while to respond to the TERM
 				// signal and we don't want dying probes to pile up).
 				// signal and we don't want dying probes to pile up).
@@ -325,3 +329,13 @@ func min(x, y int) int {
 	}
 	}
 	return y
 	return y
 }
 }
+
+func getShell(config *containertypes.Config) []string {
+	if len(config.Shell) != 0 {
+		return config.Shell
+	}
+	if runtime.GOOS != "windows" {
+		return []string{"/bin/sh", "-c"}
+	}
+	return []string{"cmd", "/S", "/C"}
+}

+ 1 - 1
daemon/health_test.go

@@ -80,7 +80,7 @@ func TestHealthStates(t *testing.T) {
 			Start:    startTime,
 			Start:    startTime,
 			End:      startTime,
 			End:      startTime,
 			ExitCode: exitCode,
 			ExitCode: exitCode,
-		})
+		}, nil)
 	}
 	}
 
 
 	// starting -> failed -> success -> failed
 	// starting -> failed -> success -> failed

+ 1 - 0
daemon/image_pull.go

@@ -106,6 +106,7 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
 	return err
 	return err
 }
 }
 
 
+// GetRepository returns a repository from the registry.
 func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagged, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
 func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagged, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
 	// get repository info
 	// get repository info
 	repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
 	repoInfo, err := daemon.RegistryService.ResolveRepository(ref)

+ 1 - 0
daemon/info_unix.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
 )
 )
 
 
+// FillPlatformInfo fills the platform related info.
 func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
 func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
 	v.MemoryLimit = sysInfo.MemoryLimit
 	v.MemoryLimit = sysInfo.MemoryLimit
 	v.SwapLimit = sysInfo.SwapLimit
 	v.SwapLimit = sysInfo.SwapLimit

+ 1 - 0
daemon/info_windows.go

@@ -5,5 +5,6 @@ import (
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
 )
 )
 
 
+// FillPlatformInfo fills the platform related info.
 func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
 func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
 }
 }

+ 2 - 0
docs/extend/config.md

@@ -55,6 +55,8 @@ Config provides the base accessible fields for working with V0 plugin format
 
 
       	- **docker.volumedriver/1.0**
       	- **docker.volumedriver/1.0**
 
 
+      	- **docker.authz/1.0**
+
     - **`socket`** *string*
     - **`socket`** *string*
 
 
       socket is the name of the socket the engine should use to communicate with the plugins.
       socket is the name of the socket the engine should use to communicate with the plugins.

+ 2 - 2
docs/extend/index.md

@@ -199,8 +199,8 @@ drwx------ 19 root root 4096 Aug  8 17:56 rootfs
 The `rootfs` directory represents the root filesystem of the plugin. In this
 The `rootfs` directory represents the root filesystem of the plugin. In this
 example, it was created from a Dockerfile:
 example, it was created from a Dockerfile:
 
 
->**Note:** The `/run/docker/plugins` directory is mandatory for docker to communicate with
-the plugin.
+>**Note:** The `/run/docker/plugins` directory is mandatory inside of the
+plugin's filesystem for docker to communicate with the plugin.
 
 
 ```bash
 ```bash
 $ git clone https://github.com/vieux/docker-volume-sshfs
 $ git clone https://github.com/vieux/docker-volume-sshfs

+ 3 - 0
docs/reference/api/docker_remote_api.md

@@ -165,6 +165,7 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /containers/create` now takes `StopTimeout` field.
 * `POST /containers/create` now takes `StopTimeout` field.
 * `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
 * `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
 * `POST /services/(id or name)/update` now accepts a `ForceUpdate` parameter inside the `TaskTemplate`, which causes the service to be updated even if there are no changes which would ordinarily trigger an update.
 * `POST /services/(id or name)/update` now accepts a `ForceUpdate` parameter inside the `TaskTemplate`, which causes the service to be updated even if there are no changes which would ordinarily trigger an update.
+* `POST /services/create` and `POST /services/(id or name)/update` now return a `Warnings` array.
 * `GET /networks/(name)` now returns field `Created` in response to show network created time.
 * `GET /networks/(name)` now returns field `Created` in response to show network created time.
 * `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
 * `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
 * `GET /volumes`, `GET /volumes/(name)`, and `POST /volumes/create` now return the `Options` field which holds the driver specific options to use for when creating the volume.
 * `GET /volumes`, `GET /volumes/(name)`, and `POST /volumes/create` now return the `Options` field which holds the driver specific options to use for when creating the volume.
@@ -193,6 +194,8 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /plugins/(plugin name)/push` push a plugin.
 * `POST /plugins/(plugin name)/push` push a plugin.
 * `POST /plugins/create?name=(plugin name)` create a plugin.
 * `POST /plugins/create?name=(plugin name)` create a plugin.
 * `DELETE /plugins/(plugin name)` delete a plugin.
 * `DELETE /plugins/(plugin name)` delete a plugin.
+* `POST /node/(id or name)/update` now accepts both `id` or `name` to identify the node to update.
+* `GET /images/json` now support a `reference` filter.
 
 
 
 
 ### v1.24 API changes
 ### v1.24 API changes

+ 6 - 6
docs/reference/api/docker_remote_api_v1.18.md

@@ -1044,7 +1044,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.18/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1462,7 +1462,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.18/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1765,7 +1765,7 @@ Docker images report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.18/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1807,7 +1807,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.18/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1836,7 +1836,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.18/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1859,7 +1859,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.18/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body

+ 6 - 6
docs/reference/api/docker_remote_api_v1.19.md

@@ -1083,7 +1083,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.19/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1528,7 +1528,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.19/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1845,7 +1845,7 @@ Docker images report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.19/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1887,7 +1887,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.19/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1916,7 +1916,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.19/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1939,7 +1939,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.19/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body

+ 7 - 7
docs/reference/api/docker_remote_api_v1.20.md

@@ -1090,7 +1090,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.20/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1235,7 +1235,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.20/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -1682,7 +1682,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.20/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2000,7 +2000,7 @@ Docker images report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.20/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2042,7 +2042,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.20/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2071,7 +2071,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.20/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2094,7 +2094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.20/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body

+ 9 - 9
docs/reference/api/docker_remote_api_v1.21.md

@@ -1173,7 +1173,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.21/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1318,7 +1318,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.21/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -1835,7 +1835,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.21/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2155,7 +2155,7 @@ Docker images report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.21/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2198,7 +2198,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.21/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2227,7 +2227,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.21/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2250,7 +2250,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.21/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body
@@ -2637,7 +2637,7 @@ Instruct the driver to remove the volume (`name`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /volumes/tardis HTTP/1.1
+    DELETE /v1.21/volumes/tardis HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2912,7 +2912,7 @@ Instruct the driver to remove the network (`id`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
+    DELETE /v1.21/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 

+ 12 - 9
docs/reference/api/docker_remote_api_v1.22.md

@@ -257,6 +257,7 @@ Create a container
            "StopSignal": "SIGTERM",
            "StopSignal": "SIGTERM",
            "HostConfig": {
            "HostConfig": {
              "Binds": ["/tmp:/tmp"],
              "Binds": ["/tmp:/tmp"],
+             "Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
              "Links": ["redis3:redis"],
              "Links": ["redis3:redis"],
              "Memory": 0,
              "Memory": 0,
              "MemorySwap": 0,
              "MemorySwap": 0,
@@ -364,6 +365,8 @@ Create a container
              _absolute_ path.
              _absolute_ path.
            + `volume-name:container-dest:ro` to mount the volume read-only
            + `volume-name:container-dest:ro` to mount the volume read-only
              inside the container.  `container-dest` must be an _absolute_ path.
              inside the container.  `container-dest` must be an _absolute_ path.
+    -   **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
+          mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
     -   **Links** - A list of links for the container. Each link entry should be
     -   **Links** - A list of links for the container. Each link entry should be
           in the form of `container_name:alias`.
           in the form of `container_name:alias`.
     -   **Memory** - Memory limit in bytes.
     -   **Memory** - Memory limit in bytes.
@@ -1349,7 +1352,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.22/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1494,7 +1497,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.22/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -2049,7 +2052,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.22/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2394,7 +2397,7 @@ Docker networks report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.22/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2586,7 +2589,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.22/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2615,7 +2618,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.22/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2638,7 +2641,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.22/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body
@@ -2947,7 +2950,7 @@ Instruct the driver to remove the volume (`name`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /volumes/tardis HTTP/1.1
+    DELETE /v1.22/volumes/tardis HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3247,7 +3250,7 @@ Instruct the driver to remove the network (`id`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
+    DELETE /v1.22/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 

+ 13 - 10
docs/reference/api/docker_remote_api_v1.23.md

@@ -281,6 +281,7 @@ Create a container
            "StopSignal": "SIGTERM",
            "StopSignal": "SIGTERM",
            "HostConfig": {
            "HostConfig": {
              "Binds": ["/tmp:/tmp"],
              "Binds": ["/tmp:/tmp"],
+             "Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
              "Links": ["redis3:redis"],
              "Links": ["redis3:redis"],
              "Memory": 0,
              "Memory": 0,
              "MemorySwap": 0,
              "MemorySwap": 0,
@@ -389,6 +390,8 @@ Create a container
              _absolute_ path.
              _absolute_ path.
            + `volume-name:container-dest:ro` to mount the volume read-only
            + `volume-name:container-dest:ro` to mount the volume read-only
              inside the container.  `container-dest` must be an _absolute_ path.
              inside the container.  `container-dest` must be an _absolute_ path.
+    -   **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
+          mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
     -   **Links** - A list of links for the container. Each link entry should be
     -   **Links** - A list of links for the container. Each link entry should be
           in the form of `container_name:alias`.
           in the form of `container_name:alias`.
     -   **Memory** - Memory limit in bytes.
     -   **Memory** - Memory limit in bytes.
@@ -1384,7 +1387,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.23/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1529,7 +1532,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.23/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -2092,7 +2095,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.23/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2444,7 +2447,7 @@ Docker networks report the following events:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.23/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2636,7 +2639,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.23/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2665,7 +2668,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.23/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2688,7 +2691,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.23/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body
@@ -3005,7 +3008,7 @@ Return low-level information on the volume `name`
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /volumes/tardis
+    GET /v1.23/volumes/tardis
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3036,7 +3039,7 @@ Instruct the driver to remove the volume (`name`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /volumes/tardis HTTP/1.1
+    DELETE /v1.23/volumes/tardis HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3364,7 +3367,7 @@ Instruct the driver to remove the network (`id`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
+    DELETE /v1.23/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 

+ 23 - 20
docs/reference/api/docker_remote_api_v1.24.md

@@ -290,6 +290,7 @@ Create a container
            "StopSignal": "SIGTERM",
            "StopSignal": "SIGTERM",
            "HostConfig": {
            "HostConfig": {
              "Binds": ["/tmp:/tmp"],
              "Binds": ["/tmp:/tmp"],
+             "Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
              "Links": ["redis3:redis"],
              "Links": ["redis3:redis"],
              "Memory": 0,
              "Memory": 0,
              "MemorySwap": 0,
              "MemorySwap": 0,
@@ -404,6 +405,8 @@ Create a container
              _absolute_ path.
              _absolute_ path.
            + `volume-name:container-dest:ro` to mount the volume read-only
            + `volume-name:container-dest:ro` to mount the volume read-only
              inside the container.  `container-dest` must be an _absolute_ path.
              inside the container.  `container-dest` must be an _absolute_ path.
+    -   **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
+          mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
     -   **Links** - A list of links for the container. Each link entry should be
     -   **Links** - A list of links for the container. Each link entry should be
           in the form of `container_name:alias`.
           in the form of `container_name:alias`.
     -   **Memory** - Memory limit in bytes.
     -   **Memory** - Memory limit in bytes.
@@ -1411,7 +1414,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.24/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1524,7 +1527,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.24/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -2088,7 +2091,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.24/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2453,13 +2456,13 @@ Docker daemon report the following event:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.24/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
     HTTP/1.1 200 OK
     HTTP/1.1 200 OK
     Content-Type: application/json
     Content-Type: application/json
-    Server: Docker/1.11.0 (linux)
+    Server: Docker/1.12.0 (linux)
     Date: Fri, 29 Apr 2016 15:18:06 GMT
     Date: Fri, 29 Apr 2016 15:18:06 GMT
     Transfer-Encoding: chunked
     Transfer-Encoding: chunked
 
 
@@ -2646,7 +2649,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.24/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2675,7 +2678,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.24/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2698,7 +2701,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.24/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body
@@ -3030,7 +3033,7 @@ Return low-level information on the volume `name`
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /volumes/tardis
+    GET /v1.24/volumes/tardis
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3082,7 +3085,7 @@ Instruct the driver to remove the volume (`name`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /volumes/tardis HTTP/1.1
+    DELETE /v1.24/volumes/tardis HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3414,7 +3417,7 @@ Instruct the driver to remove the network (`id`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
+    DELETE /v1.24/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3818,7 +3821,7 @@ Removes a plugin
 **Example request**:
 **Example request**:
 
 
 ```
 ```
-DELETE /plugins/tiborvass/no-remove:latest HTTP/1.1
+DELETE /v1.24/plugins/tiborvass/no-remove:latest HTTP/1.1
 ```
 ```
 
 
 The `:latest` tag is optional, and is used as default if omitted.
 The `:latest` tag is optional, and is used as default if omitted.
@@ -3912,7 +3915,7 @@ List nodes
             "MemoryBytes": 8272408576
             "MemoryBytes": 8272408576
           },
           },
           "Engine": {
           "Engine": {
-            "EngineVersion": "1.12.0-dev",
+            "EngineVersion": "1.12.0",
             "Labels": {
             "Labels": {
                 "foo": "bar",
                 "foo": "bar",
             }
             }
@@ -3965,7 +3968,7 @@ List nodes
 ### Inspect a node
 ### Inspect a node
 
 
 
 
-`GET /nodes/<id>`
+`GET /nodes/(id or name)`
 
 
 Return low-level information on the node `id`
 Return low-level information on the node `id`
 
 
@@ -4004,7 +4007,7 @@ Return low-level information on the node `id`
           "MemoryBytes": 8272408576
           "MemoryBytes": 8272408576
         },
         },
         "Engine": {
         "Engine": {
-          "EngineVersion": "1.12.0-dev",
+          "EngineVersion": "1.12.0",
           "Labels": {
           "Labels": {
               "foo": "bar",
               "foo": "bar",
           }
           }
@@ -4047,13 +4050,13 @@ Return low-level information on the node `id`
 ### Remove a node
 ### Remove a node
 
 
 
 
-`DELETE /nodes/(id)`
+`DELETE /nodes/(id or name)`
 
 
-Remove a node [`id`] from the swarm.
+Remove a node from the swarm.
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
+    DELETE /v1.24/nodes/24ifsmvkjbyhk HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -4077,7 +4080,7 @@ Remove a node [`id`] from the swarm.
 
 
 `POST /nodes/(id)/update`
 `POST /nodes/(id)/update`
 
 
-Update the node `id`.
+Update a node.
 
 
 The payload of the `POST` request is the new `NodeSpec` and
 The payload of the `POST` request is the new `NodeSpec` and
 overrides the current `NodeSpec` for the specified node.
 overrides the current `NodeSpec` for the specified node.
@@ -4693,7 +4696,7 @@ Stop and remove the service `id`
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /services/16253994b7c4 HTTP/1.1
+    DELETE /v1.24/services/16253994b7c4 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 

+ 50 - 36
docs/reference/api/docker_remote_api_v1.25.md

@@ -295,6 +295,7 @@ Create a container
            "StopTimeout": 10,
            "StopTimeout": 10,
            "HostConfig": {
            "HostConfig": {
              "Binds": ["/tmp:/tmp"],
              "Binds": ["/tmp:/tmp"],
+             "Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
              "Links": ["redis3:redis"],
              "Links": ["redis3:redis"],
              "Memory": 0,
              "Memory": 0,
              "MemorySwap": 0,
              "MemorySwap": 0,
@@ -418,6 +419,8 @@ Create a container
              _absolute_ path.
              _absolute_ path.
            + `volume-name:container-dest:ro` to mount the volume read-only
            + `volume-name:container-dest:ro` to mount the volume read-only
              inside the container.  `container-dest` must be an _absolute_ path.
              inside the container.  `container-dest` must be an _absolute_ path.
+    -   **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
+          mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
     -   **Links** - A list of links for the container. Each link entry should be
     -   **Links** - A list of links for the container. Each link entry should be
           in the form of `container_name:alias`.
           in the form of `container_name:alias`.
     -   **Memory** - Memory limit in bytes.
     -   **Memory** - Memory limit in bytes.
@@ -1467,7 +1470,7 @@ Remove the container `id` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /containers/16253994b7c4?v=1 HTTP/1.1
+    DELETE /v1.25/containers/16253994b7c4?v=1 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -1580,7 +1583,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
 
 
 **Example request**:
 **Example request**:
 
 
-    PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
+    PUT /v1.25/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     {% raw %}
     {% raw %}
@@ -2272,7 +2275,7 @@ Remove the image `name` from the filesystem
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /images/test HTTP/1.1
+    DELETE /v1.25/images/test HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -2610,7 +2613,7 @@ Display system-wide information
         "Name": "WIN-V0V70C0LU5P",
         "Name": "WIN-V0V70C0LU5P",
         "Labels": null,
         "Labels": null,
         "ExperimentalBuild": false,
         "ExperimentalBuild": false,
-        "ServerVersion": "1.13.0-dev",
+        "ServerVersion": "1.13.0",
         "ClusterStore": "",
         "ClusterStore": "",
         "ClusterAdvertise": "",
         "ClusterAdvertise": "",
         "SecurityOptions": null,
         "SecurityOptions": null,
@@ -2898,13 +2901,13 @@ Docker daemon report the following event:
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /events?since=1374067924
+    GET /v1.25/events?since=1374067924
 
 
 **Example response**:
 **Example response**:
 
 
     HTTP/1.1 200 OK
     HTTP/1.1 200 OK
     Content-Type: application/json
     Content-Type: application/json
-    Server: Docker/1.11.0 (linux)
+    Server: Docker/1.13.0 (linux)
     Date: Fri, 29 Apr 2016 15:18:06 GMT
     Date: Fri, 29 Apr 2016 15:18:06 GMT
     Transfer-Encoding: chunked
     Transfer-Encoding: chunked
 
 
@@ -3091,7 +3094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/ubuntu/get
+    GET /v1.25/images/ubuntu/get
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3120,7 +3123,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
+    GET /v1.25/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3143,7 +3146,7 @@ See the [image tarball format](#image-tarball-format) for more details.
 
 
 **Example request**
 **Example request**
 
 
-    POST /images/load
+    POST /v1.25/images/load
     Content-Type: application/x-tar
     Content-Type: application/x-tar
 
 
     Tarball in body
     Tarball in body
@@ -3490,7 +3493,7 @@ Return low-level information on the volume `name`
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /volumes/tardis
+    GET /v1.25/volumes/tardis
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3547,7 +3550,7 @@ Instruct the driver to remove the volume (`name`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /volumes/tardis HTTP/1.1
+    DELETE /v1.25/volumes/tardis HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -3920,7 +3923,7 @@ Instruct the driver to remove the network (`id`).
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
+    DELETE /v1.25/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -4296,7 +4299,7 @@ Content-Type: application/json
 **Example request**:
 **Example request**:
 
 
 
 
-    POST /plugins/tiborvass/no-remove/set
+    POST /v1.25/plugins/tiborvass/no-remove/set
     Content-Type: application/json
     Content-Type: application/json
 
 
     ["DEBUG=1"]
     ["DEBUG=1"]
@@ -4375,7 +4378,7 @@ Removes a plugin
 **Example request**:
 **Example request**:
 
 
 ```
 ```
-DELETE /plugins/tiborvass/no-remove:latest HTTP/1.1
+DELETE /v1.25/plugins/tiborvass/no-remove:latest HTTP/1.1
 ```
 ```
 
 
 The `:latest` tag is optional, and is used as default if omitted.
 The `:latest` tag is optional, and is used as default if omitted.
@@ -4510,7 +4513,7 @@ List nodes
             "MemoryBytes": 8272408576
             "MemoryBytes": 8272408576
           },
           },
           "Engine": {
           "Engine": {
-            "EngineVersion": "1.12.0-dev",
+            "EngineVersion": "1.13.0",
             "Labels": {
             "Labels": {
                 "foo": "bar",
                 "foo": "bar",
             }
             }
@@ -4564,7 +4567,7 @@ List nodes
 ### Inspect a node
 ### Inspect a node
 
 
 
 
-`GET /nodes/<id>`
+`GET /nodes/(id or name)`
 
 
 Return low-level information on the node `id`
 Return low-level information on the node `id`
 
 
@@ -4603,7 +4606,7 @@ Return low-level information on the node `id`
           "MemoryBytes": 8272408576
           "MemoryBytes": 8272408576
         },
         },
         "Engine": {
         "Engine": {
-          "EngineVersion": "1.12.0-dev",
+          "EngineVersion": "1.13.0",
           "Labels": {
           "Labels": {
               "foo": "bar",
               "foo": "bar",
           }
           }
@@ -4647,13 +4650,13 @@ Return low-level information on the node `id`
 ### Remove a node
 ### Remove a node
 
 
 
 
-`DELETE /nodes/(id)`
+`DELETE /nodes/(id or name)`
 
 
-Remove a node [`id`] from the swarm.
+Remove a node from the swarm.
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
+    DELETE /v1.25/nodes/24ifsmvkjbyhk HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -4675,9 +4678,9 @@ Remove a node [`id`] from the swarm.
 ### Update a node
 ### Update a node
 
 
 
 
-`POST /nodes/(id)/update`
+`POST /nodes/(id or name)/update`
 
 
-Update the node `id`.
+Update a node.
 
 
 The payload of the `POST` request is the new `NodeSpec` and
 The payload of the `POST` request is the new `NodeSpec` and
 overrides the current `NodeSpec` for the specified node.
 overrides the current `NodeSpec` for the specified node.
@@ -4813,7 +4816,7 @@ Initialize a new swarm. The body of the HTTP response includes the node ID.
     Content-Length: 28
     Content-Length: 28
     Content-Type: application/json
     Content-Type: application/json
     Date: Thu, 01 Sep 2016 21:49:13 GMT
     Date: Thu, 01 Sep 2016 21:49:13 GMT
-    Server: Docker/1.12.0 (linux)
+    Server: Docker/1.13.0 (linux)
 
 
     "7v2t30z9blmxuhnyo6s4cpenp"
     "7v2t30z9blmxuhnyo6s4cpenp"
 
 
@@ -4821,7 +4824,7 @@ Initialize a new swarm. The body of the HTTP response includes the node ID.
 
 
 - **200** – no error
 - **200** – no error
 - **400** – bad parameter
 - **400** – bad parameter
-- **406** – node is already part of a swarm
+- **503** – node is already part of a swarm
 
 
 JSON Parameters:
 JSON Parameters:
 
 
@@ -4890,7 +4893,7 @@ Join an existing swarm
 
 
 - **200** – no error
 - **200** – no error
 - **400** – bad parameter
 - **400** – bad parameter
-- **406** – node is already part of a swarm
+- **503** – node is already part of a swarm
 
 
 JSON Parameters:
 JSON Parameters:
 
 
@@ -4928,7 +4931,7 @@ Leave a swarm
 **Status codes**:
 **Status codes**:
 
 
 - **200** – no error
 - **200** – no error
-- **406** – node is not part of a swarm
+- **503** – node is not part of a swarm
 
 
 ### Retrieve the swarm's unlock key
 ### Retrieve the swarm's unlock key
 
 
@@ -5024,7 +5027,7 @@ Update a swarm
 
 
 - **200** – no error
 - **200** – no error
 - **400** – bad parameter
 - **400** – bad parameter
-- **406** – node is not part of a swarm
+- **503** – node is not part of a swarm
 
 
 JSON Parameters:
 JSON Parameters:
 
 
@@ -5262,14 +5265,15 @@ image](#create-an-image) section for more details.
     Content-Type: application/json
     Content-Type: application/json
 
 
     {
     {
-      "ID":"ak7w3gjqoa3kuz8xcpnyy0pvl"
+      "ID": "ak7w3gjqoa3kuz8xcpnyy0pvl",
+      "Warnings": ["unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"]
     }
     }
 
 
 **Status codes**:
 **Status codes**:
 
 
 - **201** – no error
 - **201** – no error
-- **406** – server error or node is not part of a swarm
 - **409** – name conflicts with an existing object
 - **409** – name conflicts with an existing object
+- **503** – server error or node is not part of a swarm
 
 
 **JSON Parameters**:
 **JSON Parameters**:
 
 
@@ -5367,7 +5371,7 @@ Stop and remove the service `id`
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /services/16253994b7c4 HTTP/1.1
+    DELETE /v1.25/services/16253994b7c4 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -5628,6 +5632,16 @@ image](#create-an-image) section for more details.
 -   **404** – no such service
 -   **404** – no such service
 -   **500** – server error
 -   **500** – server error
 
 
+**Example response**:
+
+    HTTP/1.1 200 OK
+    Content-Type: application/json
+
+    {
+      "Warnings": ["unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"]
+    }
+
+
 ### Get service logs
 ### Get service logs
 
 
 `GET /services/(id or name)/logs`
 `GET /services/(id or name)/logs`
@@ -5639,7 +5653,7 @@ Get `stdout` and `stderr` logs from the service ``id``
 
 
 **Example request**:
 **Example request**:
 
 
-     GET /services/4fa6e0f0c678/logs?stderr=1&stdout=1&timestamps=1&follow=1&tail=10&since=1428990821 HTTP/1.1
+     GET /v1.25/services/4fa6e0f0c678/logs?stderr=1&stdout=1&timestamps=1&follow=1&tail=10&since=1428990821 HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -5988,7 +6002,7 @@ List secrets
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /secrets HTTP/1.1
+    GET /v1.25/secrets HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -6026,7 +6040,7 @@ Create a secret
 
 
 **Example request**:
 **Example request**:
 
 
-    POST /secrets/create HTTP/1.1
+    POST /v1.25/secrets/create HTTP/1.1
     Content-Type: application/json
     Content-Type: application/json
 
 
     {
     {
@@ -6049,8 +6063,8 @@ Create a secret
 **Status codes**:
 **Status codes**:
 
 
 - **201** – no error
 - **201** – no error
-- **406** – server error or node is not part of a swarm
 - **409** – name conflicts with an existing object
 - **409** – name conflicts with an existing object
+- **503** – server error or node is not part of a swarm
 
 
 **JSON Parameters**:
 **JSON Parameters**:
 
 
@@ -6066,7 +6080,7 @@ Get details on the secret `id`
 
 
 **Example request**:
 **Example request**:
 
 
-    GET /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
+    GET /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 
@@ -6099,7 +6113,7 @@ Remove the secret `id` from the secret store
 
 
 **Example request**:
 **Example request**:
 
 
-    DELETE /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
+    DELETE /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
 
 
 **Example response**:
 **Example response**:
 
 

+ 19 - 0
docs/reference/commandline/images.md

@@ -28,6 +28,7 @@ Options:
                         - label=<key> or label=<key>=<value>
                         - label=<key> or label=<key>=<value>
                         - before=(<image-name>[:tag]|<image-id>|<image@digest>)
                         - before=(<image-name>[:tag]|<image-id>|<image@digest>)
                         - since=(<image-name>[:tag]|<image-id>|<image@digest>)
                         - since=(<image-name>[:tag]|<image-id>|<image@digest>)
+                        - reference=(pattern of an image reference)
       --format string   Pretty-print images using a Go template
       --format string   Pretty-print images using a Go template
       --help            Print usage
       --help            Print usage
       --no-trunc        Don't truncate output
       --no-trunc        Don't truncate output
@@ -229,6 +230,24 @@ Filtering with `since` would give:
     image1              latest              eeae25ada2aa        4 minutes ago        188.3 MB
     image1              latest              eeae25ada2aa        4 minutes ago        188.3 MB
     image2              latest              dea752e4e117        9 minutes ago        188.3 MB
     image2              latest              dea752e4e117        9 minutes ago        188.3 MB
 
 
+#### Reference
+
+The `reference` filter shows only images whose reference matches
+the specified pattern.
+
+    $ docker images
+    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
+    busybox             latest              e02e811dd08f        5 weeks ago         1.09 MB
+    busybox             uclibc              e02e811dd08f        5 weeks ago         1.09 MB
+    busybox             musl                733eb3059dce        5 weeks ago         1.21 MB
+    busybox             glibc               21c16b6787c6        5 weeks ago         4.19 MB
+
+Filtering with `reference` would give:
+
+    $ docker images --filter=reference='busy*:*libc'
+    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
+    busybox             uclibc              e02e811dd08f        5 weeks ago         1.09 MB
+    busybox             glibc               21c16b6787c6        5 weeks ago         4.19 MB
 
 
 ## Formatting
 ## Formatting
 
 

+ 50 - 27
docs/reference/commandline/info.md

@@ -47,7 +47,7 @@ available on the volume where `/var/lib/docker` is mounted.
 
 
 ## Display Docker system information
 ## Display Docker system information
 
 
-Here is a sample output for a daemon running on Ubuntu, using the overlay
+Here is a sample output for a daemon running on Ubuntu, using the overlay2
 storage driver and a node that is part of a 2-node swarm:
 storage driver and a node that is part of a 2-node swarm:
 
 
     $ docker -D info
     $ docker -D info
@@ -56,49 +56,72 @@ storage driver and a node that is part of a 2-node swarm:
      Paused: 1
      Paused: 1
      Stopped: 10
      Stopped: 10
     Images: 52
     Images: 52
-    Server Version: 1.12.0-dev
-    Storage Driver: overlay
+    Server Version: 1.13.0
+    Storage Driver: overlay2
      Backing Filesystem: extfs
      Backing Filesystem: extfs
+     Supports d_type: true
+     Native Overlay Diff: false
     Logging Driver: json-file
     Logging Driver: json-file
     Cgroup Driver: cgroupfs
     Cgroup Driver: cgroupfs
     Plugins:
     Plugins:
      Volume: local
      Volume: local
-     Network: bridge null host overlay
-    Swarm:
-     NodeID: 0gac67oclbxq7
+     Network: bridge host macvlan null overlay
+    Swarm: active
+     NodeID: rdjq45w1op418waxlairloqbm
      Is Manager: true
      Is Manager: true
-     Managers: 2
+     ClusterID: te8kdyw33n36fqiz74bfjeixd
+     Managers: 1
      Nodes: 2
      Nodes: 2
-    Runtimes: default
-    Default Runtime: default
-    Security Options: apparmor seccomp
-    Kernel Version: 4.4.0-21-generic
-    Operating System: Ubuntu 16.04 LTS
+     Orchestration:
+      Task History Retention Limit: 5
+     Raft:
+      Snapshot Interval: 10000
+      Number of Old Snapshots to Retain: 0
+      Heartbeat Tick: 1
+      Election Tick: 3
+     Dispatcher:
+      Heartbeat Period: 5 seconds
+     CA Configuration:
+      Expiry Duration: 3 months
+     Node Address: 172.16.66.128 172.16.66.129
+     Manager Addresses:
+      172.16.66.128:2477
+    Runtimes: runc
+    Default Runtime: runc
+    Init Binary: docker-init
+    containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
+    runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
+    init version: N/A (expected: v0.13.0)
+    Security Options:
+     apparmor
+     seccomp
+      Profile: default
+    Kernel Version: 4.4.0-31-generic
+    Operating System: Ubuntu 16.04.1 LTS
     OSType: linux
     OSType: linux
     Architecture: x86_64
     Architecture: x86_64
-    CPUs: 24
-    Total Memory: 62.86 GiB
-    Name: docker
-    ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
+    CPUs: 2
+    Total Memory: 1.937 GiB
+    Name: ubuntu
+    ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
     Docker Root Dir: /var/lib/docker
     Docker Root Dir: /var/lib/docker
-    Debug mode (client): true
-    Debug mode (server): true
-     File Descriptors: 59
-     Goroutines: 159
-     System Time: 2016-04-26T10:04:06.14689342-04:00
+    Debug Mode (client): true
+    Debug Mode (server): true
+     File Descriptors: 30
+     Goroutines: 123
+     System Time: 2016-11-12T17:24:37.955404361-08:00
      EventsListeners: 0
      EventsListeners: 0
-    Http Proxy: http://test:test@localhost:8080
-    Https Proxy: https://test:test@localhost:8080
+    Http Proxy: http://proxy.example.com:80/
     No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
     No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
-    Username: svendowideit
     Registry: https://index.docker.io/v1/
     Registry: https://index.docker.io/v1/
     WARNING: No swap limit support
     WARNING: No swap limit support
     Labels:
     Labels:
      storage=ssd
      storage=ssd
      staging=true
      staging=true
-    Insecure registries:
-     myinsecurehost:5000
+    Experimental: false
+    Insecure Registries:
      127.0.0.0/8
      127.0.0.0/8
+    Live Restore Enabled: false
 
 
 The global `-D` option tells all `docker` commands to output debug information.
 The global `-D` option tells all `docker` commands to output debug information.
 
 
@@ -168,7 +191,7 @@ Here is a sample output for a daemon running on Windows Server 2016:
      Paused: 0
      Paused: 0
      Stopped: 1
      Stopped: 1
     Images: 17
     Images: 17
-    Server Version: 1.13.0-dev
+    Server Version: 1.13.0
     Storage Driver: windowsfilter
     Storage Driver: windowsfilter
      Windows:
      Windows:
     Logging Driver: json-file
     Logging Driver: json-file

+ 7 - 4
docs/reference/commandline/stats.md

@@ -21,9 +21,10 @@ Usage:  docker stats [OPTIONS] [CONTAINER...]
 Display a live stream of container(s) resource usage statistics
 Display a live stream of container(s) resource usage statistics
 
 
 Options:
 Options:
-  -a, --all         Show all containers (default shows just running)
-      --help        Print usage
-      --no-stream   Disable streaming stats and only pull the first result
+  -a, --all             Show all containers (default shows just running)
+      --format string   Pretty-print images using a Go template
+      --help            Print usage
+      --no-stream       Disable streaming stats and only pull the first result
 ```
 ```
 
 
 The `docker stats` command returns a live data stream for running containers. To limit data to one or more specific containers, specify a list of container names or ids separated by a space. You can specify a stopped container but stopped containers do not return any data.
 The `docker stats` command returns a live data stream for running containers. To limit data to one or more specific containers, specify a list of container names or ids separated by a space. You can specify a stopped container but stopped containers do not return any data.
@@ -77,7 +78,9 @@ Valid placeholders for the Go template are listed below:
 
 
 Placeholder  | Description
 Placeholder  | Description
 ------------ | --------------------------------------------
 ------------ | --------------------------------------------
-`.Container` | Container name or ID
+`.Container` | Container name or ID (user input)
+`.Name`      | Container name
+`.ID`        | Container ID
 `.CPUPerc`   | CPU percentage
 `.CPUPerc`   | CPU percentage
 `.MemUsage`  | Memory usage
 `.MemUsage`  | Memory usage
 `.NetIO`     | Network IO
 `.NetIO`     | Network IO

+ 2 - 2
docs/reference/commandline/tag.md

@@ -16,9 +16,9 @@ keywords: "tag, name, image"
 # tag
 # tag
 
 
 ```markdown
 ```markdown
-Usage:  docker tag IMAGE[:TAG] IMAGE[:TAG]
+Usage:  docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
 
 
-Tag an image into a repository
+Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
 
 
 Options:
 Options:
       --help   Print usage
       --help   Print usage

+ 3 - 3
hack/dockerfile/binaries-commits

@@ -1,8 +1,8 @@
 #!/bin/sh
 #!/bin/sh
 
 
 TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
 TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
-RUNC_COMMIT=ac031b5bf1cc92239461125f4c1ffb760522bbf2
-CONTAINERD_COMMIT=8517738ba4b82aff5662c97ca4627e7e4d03b531
-TINI_COMMIT=v0.13.0
+RUNC_COMMIT=51371867a01c467f08af739783b8beafc15
+CONTAINERD_COMMIT=03e5862ec0d8d3b3f750e19fca3ee367e13c090e
+TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
 LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
 LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
 VNDR_COMMIT=f56bd4504b4fad07a357913687fb652ee54bb3b0
 VNDR_COMMIT=f56bd4504b4fad07a357913687fb652ee54bb3b0

+ 2 - 1
hack/generate-swagger-api.sh

@@ -8,7 +8,8 @@ swagger generate model -f api/swagger.yaml \
     -n ImageSummary \
     -n ImageSummary \
     -n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \
     -n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \
     -n ErrorResponse \
     -n ErrorResponse \
-    -n IdResponse
+    -n IdResponse \
+    -n ServiceUpdateResponse
 
 
 swagger generate operation -f api/swagger.yaml \
 swagger generate operation -f api/swagger.yaml \
     -t api -a types -m types -C api/swagger-gen.yaml \
     -t api -a types -m types -C api/swagger-gen.yaml \

+ 1 - 1
hack/validate/vet

@@ -4,7 +4,7 @@ export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 source "${SCRIPTDIR}/.validate"
 source "${SCRIPTDIR}/.validate"
 
 
 IFS=$'\n'
 IFS=$'\n'
-files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) )
+files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' | grep -v '^api/types/container/' || true) )
 unset IFS
 unset IFS
 
 
 errors=()
 errors=()

+ 4 - 1
image/tarexport/load.go

@@ -21,6 +21,7 @@ import (
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/symlink"
+	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/reference"
 )
 )
 
 
@@ -164,7 +165,9 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
 }
 }
 
 
 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
-	rawTar, err := os.Open(filename)
+	// We use system.OpenSequential to use sequential file access on Windows, avoiding
+	// depleting the standby list. On Linux, this equates to a regular os.Open.
+	rawTar, err := system.OpenSequential(filename)
 	if err != nil {
 	if err != nil {
 		logrus.Debugf("Error reading embedded tar: %v", err)
 		logrus.Debugf("Error reading embedded tar: %v", err)
 		return nil, err
 		return nil, err

+ 4 - 2
image/tarexport/save.go

@@ -315,8 +315,10 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
 		}
 		}
 		os.Symlink(relPath, layerPath)
 		os.Symlink(relPath, layerPath)
 	} else {
 	} else {
-
-		tarFile, err := os.Create(layerPath)
+		// Use system.CreateSequential rather than os.Create. This ensures sequential
+		// file access on Windows to avoid eating into MM standby list.
+		// On Linux, this equates to a regular os.Create.
+		tarFile, err := system.CreateSequential(layerPath)
 		if err != nil {
 		if err != nil {
 			return distribution.Descriptor{}, err
 			return distribution.Descriptor{}, err
 		}
 		}

+ 1 - 1
integration-cli/daemon.go

@@ -495,7 +495,7 @@ func (d *Daemon) SockRequest(method, endpoint string, data interface{}) (int, []
 	return res.StatusCode, b, err
 	return res.StatusCode, b, err
 }
 }
 
 
-// SockRequestRaw executes a socket request on a daemon and returns a http
+// SockRequestRaw executes a socket request on a daemon and returns an http
 // response and a reader for the output data.
 // response and a reader for the output data.
 func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
 func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
 	return sockRequestRawToDaemon(method, endpoint, data, ct, d.sock())
 	return sockRequestRawToDaemon(method, endpoint, data, ct, d.sock())

+ 23 - 27
integration-cli/docker_cli_build_test.go

@@ -1865,6 +1865,10 @@ func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) {
 func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
 func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
 	testRequires(c, DaemonIsWindows)
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildwindowsaddcopypathprocessing"
 	name := "testbuildwindowsaddcopypathprocessing"
+	// TODO Windows (@jhowardmsft). Needs a follow-up PR to 22181 to
+	// support backslash such as .\\ being equivalent to ./ and c:\\ being
+	// equivalent to c:/. This is not currently (nor ever has been) supported
+	// by docker on the Windows platform.
 	dockerfile := `
 	dockerfile := `
 		FROM busybox
 		FROM busybox
 			# No trailing slash on COPY/ADD
 			# No trailing slash on COPY/ADD
@@ -1874,8 +1878,8 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
 			WORKDIR /wc2
 			WORKDIR /wc2
 			ADD wc2 c:/wc2
 			ADD wc2 c:/wc2
 			WORKDIR c:/
 			WORKDIR c:/
-			RUN sh -c "[ $(cat c:/wc1/wc1) = 'hellowc1' ]"
-			RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]"
+			RUN sh -c "[ $(cat c:/wc1) = 'hellowc1' ]"
+			RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"
 
 
 			# Trailing slash on COPY/ADD, Windows-style path.
 			# Trailing slash on COPY/ADD, Windows-style path.
 			WORKDIR /wd1
 			WORKDIR /wd1
@@ -7172,31 +7176,6 @@ RUN echo vegeta
 	c.Assert(out, checker.Contains, "Step 3/3 : RUN echo vegeta")
 	c.Assert(out, checker.Contains, "Step 3/3 : RUN echo vegeta")
 }
 }
 
 
-// Verifies if COPY file . when WORKDIR is set to a non-existing directory,
-// the directory is created and the file is copied into the directory,
-// as opposed to the file being copied as a file with the name of the
-// directory. Fix for 27545 (found on Windows, but regression good for Linux too)
-func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) {
-	name := "testbuildcopyfiledotwithworkdir"
-
-	ctx, err := fakeContext(`FROM busybox
-WORKDIR /foo
-COPY file .
-RUN ["cat", "/foo/file"]
-`,
-		map[string]string{})
-	if err != nil {
-		c.Fatal(err)
-	}
-	defer ctx.Close()
-	if err := ctx.Add("file", "content"); err != nil {
-		c.Fatal(err)
-	}
-	if _, err = buildImageFromContext(name, ctx, true); err != nil {
-		c.Fatal(err)
-	}
-}
-
 func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
 func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
 	testRequires(c, ExperimentalDaemon)
 	testRequires(c, ExperimentalDaemon)
 	dockerFile := `
 	dockerFile := `
@@ -7287,3 +7266,20 @@ func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
 	_, err := buildImage("testopaquedirectory", dockerFile, false)
 	_, err := buildImage("testopaquedirectory", dockerFile, false)
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 }
 }
+
+// Windows test for USER in dockerfile
+func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
+	testRequires(c, DaemonIsWindows)
+	name := "testbuildwindowsuser"
+	_, out, err := buildImageWithOut(name,
+		`FROM `+WindowsBaseImage+`
+		RUN net user user /add
+		USER user
+		RUN set username
+		`,
+		true)
+	if err != nil {
+		c.Fatal(err)
+	}
+	c.Assert(strings.ToLower(out), checker.Contains, "username=user")
+}

+ 1 - 1
integration-cli/docker_cli_help_test.go

@@ -196,7 +196,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	stdout, stderr, _, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, _, err = runCommandWithStdoutStderr(cmd)
 	c.Assert(err, checker.NotNil)
 	c.Assert(err, checker.NotNil)
 	c.Assert(stdout, checker.Equals, "")
 	c.Assert(stdout, checker.Equals, "")
-	c.Assert(stderr, checker.Equals, "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n", check.Commentf("Unexcepted output for 'docker badCmd'\n"))
+	c.Assert(stderr, checker.Equals, "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'\n", check.Commentf("Unexcepted output for 'docker badCmd'\n"))
 }
 }
 
 
 func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) error {
 func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) error {

+ 16 - 0
integration-cli/docker_cli_swarm_test.go

@@ -1072,3 +1072,19 @@ func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput)
 	c.Assert(out, checker.Contains, expectedOutput)
 }
 }
+
+func (s *DockerSwarmSuite) TestSwarmServiceInspectPretty(c *check.C) {
+	d := s.AddDaemon(c, true, true)
+
+	name := "top"
+	out, err := d.Cmd("service", "create", "--name", name, "--limit-cpu=0.5", "busybox", "top")
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+
+	expectedOutput := `
+Resources:
+ Limits:
+  CPU:		0.5`
+	out, err = d.Cmd("service", "inspect", "--pretty", name)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, expectedOutput, check.Commentf(out))
+}

+ 1 - 1
libcontainerd/remote_unix.go

@@ -67,7 +67,7 @@ type remote struct {
 func New(stateDir string, options ...RemoteOption) (_ Remote, err error) {
 func New(stateDir string, options ...RemoteOption) (_ Remote, err error) {
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
-			err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specificed the correct address. Got error: %v", err)
+			err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specified the correct address. Got error: %v", err)
 		}
 		}
 	}()
 	}()
 	r := &remote{
 	r := &remote{

+ 52 - 27
man/docker-info.1.md

@@ -39,7 +39,7 @@ available on the volume where `/var/lib/docker` is mounted.
 
 
 ## Display Docker system information
 ## Display Docker system information
 
 
-Here is a sample output for a daemon running on Ubuntu, using the overlay
+Here is a sample output for a daemon running on Ubuntu, using the overlay2
 storage driver:
 storage driver:
 
 
     $ docker -D info
     $ docker -D info
@@ -48,49 +48,74 @@ storage driver:
      Paused: 1
      Paused: 1
      Stopped: 10
      Stopped: 10
     Images: 52
     Images: 52
-    Server Version: 1.12.0-dev
-    Storage Driver: overlay
+    Server Version: 1.13.0
+    Storage Driver: overlay2
      Backing Filesystem: extfs
      Backing Filesystem: extfs
+     Supports d_type: true
+     Native Overlay Diff: false
     Logging Driver: json-file
     Logging Driver: json-file
     Cgroup Driver: cgroupfs
     Cgroup Driver: cgroupfs
     Plugins:
     Plugins:
      Volume: local
      Volume: local
-     Network: bridge null host overlay
-    Swarm: 
-     NodeID: 0gac67oclbxq7
-     IsManager: YES
-     Managers: 2
+     Network: bridge host macvlan null overlay
+    Swarm: active
+     NodeID: rdjq45w1op418waxlairloqbm
+     Is Manager: true
+     ClusterID: te8kdyw33n36fqiz74bfjeixd
+     Managers: 1
      Nodes: 2
      Nodes: 2
-    Runtimes: default
-    Default Runtime: default
-    Security Options: apparmor seccomp
-    Kernel Version: 4.4.0-21-generic
-    Operating System: Ubuntu 16.04 LTS
+     Orchestration:
+      Task History Retention Limit: 5
+     Raft:
+      Snapshot Interval: 10000
+      Number of Old Snapshots to Retain: 0
+      Heartbeat Tick: 1
+      Election Tick: 3
+     Dispatcher:
+      Heartbeat Period: 5 seconds
+     CA Configuration:
+      Expiry Duration: 3 months
+     Node Address: 172.16.66.128 172.16.66.129
+     Manager Addresses:
+      172.16.66.128:2477
+    Runtimes: runc
+    Default Runtime: runc
+    Init Binary: docker-init
+    containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
+    runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
+    init version: N/A (expected: v0.13.0)
+    Security Options:
+     apparmor
+     seccomp
+      Profile: default
+    Kernel Version: 4.4.0-31-generic
+    Operating System: Ubuntu 16.04.1 LTS
     OSType: linux
     OSType: linux
     Architecture: x86_64
     Architecture: x86_64
-    CPUs: 24
-    Total Memory: 62.86 GiB
-    Name: docker
-    ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
+    CPUs: 2
+    Total Memory: 1.937 GiB
+    Name: ubuntu
+    ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
     Docker Root Dir: /var/lib/docker
     Docker Root Dir: /var/lib/docker
-    Debug mode (client): true
-    Debug mode (server): true
-     File Descriptors: 59
-     Goroutines: 159
-     System Time: 2016-04-26T10:04:06.14689342-04:00
+    Debug Mode (client): true
+    Debug Mode (server): true
+     File Descriptors: 30
+     Goroutines: 123
+     System Time: 2016-11-12T17:24:37.955404361-08:00
      EventsListeners: 0
      EventsListeners: 0
-    Http Proxy: http://test:test@localhost:8080
-    Https Proxy: https://test:test@localhost:8080
+    Http Proxy: http://proxy.example.com:80/
     No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
     No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
-    Username: svendowideit
     Registry: https://index.docker.io/v1/
     Registry: https://index.docker.io/v1/
     WARNING: No swap limit support
     WARNING: No swap limit support
     Labels:
     Labels:
      storage=ssd
      storage=ssd
      staging=true
      staging=true
-    Insecure registries:
-     myinsecurehost:5000
+    Experimental: false
+    Insecure Registries:
      127.0.0.0/8
      127.0.0.0/8
+    Live Restore Enabled: false
+
+
 
 
 The global `-D` option tells all `docker` commands to output debug information.
 The global `-D` option tells all `docker` commands to output debug information.
 
 

+ 1 - 1
man/docker-run.1.md

@@ -907,7 +907,7 @@ should fix the problem.
 ## Mapping Ports for External Usage
 ## Mapping Ports for External Usage
 
 
 The exposed port of an application can be mapped to a host port using the **-p**
 The exposed port of an application can be mapped to a host port using the **-p**
-flag. For example, a httpd port 80 can be mapped to the host port 8080 using the
+flag. For example, an httpd port 80 can be mapped to the host port 8080 using the
 following:
 following:
 
 
     # docker run -p 8080:80 -d -i -t fedora/httpd
     # docker run -p 8080:80 -d -i -t fedora/httpd

+ 2 - 0
man/docker-stats.1.md

@@ -30,6 +30,8 @@ Display a live stream of one or more containers' resource usage statistics
    Pretty-print containers statistics using a Go template.
    Pretty-print containers statistics using a Go template.
    Valid placeholders:
    Valid placeholders:
       .Container - Container name or ID.
       .Container - Container name or ID.
+      .Name - Container name.
+      .ID - Container ID.
       .CPUPerc - CPU percentage.
       .CPUPerc - CPU percentage.
       .MemUsage - Memory usage.
       .MemUsage - Memory usage.
       .NetIO - Network IO.
       .NetIO - Network IO.

+ 2 - 2
man/docker-tag.1.md

@@ -2,12 +2,12 @@
 % Docker Community
 % Docker Community
 % JUNE 2014
 % JUNE 2014
 # NAME
 # NAME
-docker-tag - Tag an image into a repository
+docker-tag - Create a tag `TARGET_IMAGE` that refers to `SOURCE_IMAGE`
 
 
 # SYNOPSIS
 # SYNOPSIS
 **docker tag**
 **docker tag**
 [**--help**]
 [**--help**]
-NAME[:TAG] NAME[:TAG]
+SOURCE_NAME[:TAG] TARGET_NAME[:TAG]
 
 
 # DESCRIPTION
 # DESCRIPTION
 Assigns a new alias to an image in a registry. An alias refers to the
 Assigns a new alias to an image in a registry. An alias refers to the

+ 1 - 0
opts/port.go

@@ -16,6 +16,7 @@ const (
 	portOptMode          = "mode"
 	portOptMode          = "mode"
 )
 )
 
 
+// PortOpt represents a port config in swarm mode.
 type PortOpt struct {
 type PortOpt struct {
 	ports []swarm.PortConfig
 	ports []swarm.PortConfig
 }
 }

+ 8 - 3
pkg/archive/archive.go

@@ -374,7 +374,10 @@ func (ta *tarAppender) addTarFile(path, name string) error {
 	}
 	}
 
 
 	if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
 	if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
-		file, err := os.Open(path)
+		// We use system.OpenSequential to ensure we use sequential file
+		// access on Windows to avoid depleting the standby list.
+		// On Linux, this equates to a regular os.Open.
+		file, err := system.OpenSequential(path)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -412,8 +415,10 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
 		}
 		}
 
 
 	case tar.TypeReg, tar.TypeRegA:
 	case tar.TypeReg, tar.TypeRegA:
-		// Source is regular file
-		file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
+		// Source is regular file. We use system.OpenFileSequential to use sequential
+		// file access to avoid depleting the standby list on Windows.
+		// On Linux, this equates to a regular os.OpenFile
+		file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}

+ 1 - 1
pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go

@@ -2,7 +2,7 @@
 
 
 package devicemapper
 package devicemapper
 
 
-// LibraryDeferredRemovalsupport is not supported when statically linked.
+// LibraryDeferredRemovalSupport is not supported when statically linked.
 const LibraryDeferredRemovalSupport = false
 const LibraryDeferredRemovalSupport = false
 
 
 func dmTaskDeferredRemoveFct(task *cdmTask) int {
 func dmTaskDeferredRemoveFct(task *cdmTask) int {

+ 1 - 1
pkg/discovery/backends.go

@@ -86,7 +86,7 @@ func ParseAdvertise(advertise string) (string, error) {
 		break
 		break
 	}
 	}
 	if addr == "" {
 	if addr == "" {
-		return "", fmt.Errorf("couldnt find a valid ip-address in interface %s", advertise)
+		return "", fmt.Errorf("could not find a valid ip-address in interface %s", advertise)
 	}
 	}
 
 
 	addr = net.JoinHostPort(addr, port)
 	addr = net.JoinHostPort(addr, port)

+ 4 - 4
pkg/ioutils/multireader.go

@@ -155,18 +155,18 @@ func (r *multiReadSeeker) Read(b []byte) (int, error) {
 		r.pos = &pos{0, 0}
 		r.pos = &pos{0, 0}
 	}
 	}
 
 
-	bCap := int64(cap(b))
+	bLen := int64(len(b))
 	buf := bytes.NewBuffer(nil)
 	buf := bytes.NewBuffer(nil)
 	var rdr io.ReadSeeker
 	var rdr io.ReadSeeker
 
 
 	for _, rdr = range r.readers[r.pos.idx:] {
 	for _, rdr = range r.readers[r.pos.idx:] {
-		readBytes, err := io.CopyN(buf, rdr, bCap)
+		readBytes, err := io.CopyN(buf, rdr, bLen)
 		if err != nil && err != io.EOF {
 		if err != nil && err != io.EOF {
 			return -1, err
 			return -1, err
 		}
 		}
-		bCap -= readBytes
+		bLen -= readBytes
 
 
-		if bCap == 0 {
+		if bLen == 0 {
 			break
 			break
 		}
 		}
 	}
 	}

+ 21 - 0
pkg/ioutils/multireader_test.go

@@ -2,6 +2,7 @@ package ioutils
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"encoding/binary"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -188,3 +189,23 @@ func TestMultiReadSeekerCurAfterSet(t *testing.T) {
 		t.Fatalf("reader size does not match, got %d, expected %d", size, mid+18)
 		t.Fatalf("reader size does not match, got %d, expected %d", size, mid+18)
 	}
 	}
 }
 }
+
+func TestMultiReadSeekerSmallReads(t *testing.T) {
+	readers := []io.ReadSeeker{}
+	for i := 0; i < 10; i++ {
+		integer := make([]byte, 4, 4)
+		binary.BigEndian.PutUint32(integer, uint32(i))
+		readers = append(readers, bytes.NewReader(integer))
+	}
+
+	reader := MultiReadSeeker(readers...)
+	for i := 0; i < 10; i++ {
+		var integer uint32
+		if err := binary.Read(reader, binary.BigEndian, &integer); err != nil {
+			t.Fatalf("Read from NewMultiReadSeeker failed: %v", err)
+		}
+		if uint32(i) != integer {
+			t.Fatalf("Read wrong value from NewMultiReadSeeker: %d != %d", i, integer)
+		}
+	}
+}

+ 29 - 0
pkg/system/filesys.go

@@ -23,3 +23,32 @@ func MkdirAll(path string, perm os.FileMode) error {
 func IsAbs(path string) bool {
 func IsAbs(path string) bool {
 	return filepath.IsAbs(path)
 	return filepath.IsAbs(path)
 }
 }
+
+// The functions below here are wrappers for the equivalents in the os package.
+// They are passthrough on Unix platforms, and only relevant on Windows.
+
+// CreateSequential creates the named file with mode 0666 (before umask), truncating
+// it if it already exists. If successful, methods on the returned
+// File can be used for I/O; the associated file descriptor has mode
+// O_RDWR.
+// If there is an error, it will be of type *PathError.
+func CreateSequential(name string) (*os.File, error) {
+	return os.Create(name)
+}
+
+// OpenSequential opens the named file for reading. If successful, methods on
+// the returned file can be used for reading; the associated file
+// descriptor has mode O_RDONLY.
+// If there is an error, it will be of type *PathError.
+func OpenSequential(name string) (*os.File, error) {
+	return os.Open(name)
+}
+
+// OpenFileSequential is the generalized open call; most users will use Open
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
+// methods on the returned File can be used for I/O.
+// If there is an error, it will be of type *PathError.
+func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
+	return os.OpenFile(name, flag, perm)
+}

+ 106 - 3
pkg/system/filesys_windows.go

@@ -98,7 +98,7 @@ func mkdirWithACL(name string) error {
 	sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
 	sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
 	sd, err := winio.SddlToSecurityDescriptor(sddl)
 	sd, err := winio.SddlToSecurityDescriptor(sddl)
 	if err != nil {
 	if err != nil {
-		return &os.PathError{"mkdir", name, err}
+		return &os.PathError{Op: "mkdir", Path: name, Err: err}
 	}
 	}
 	sa.Length = uint32(unsafe.Sizeof(sa))
 	sa.Length = uint32(unsafe.Sizeof(sa))
 	sa.InheritHandle = 1
 	sa.InheritHandle = 1
@@ -106,12 +106,12 @@ func mkdirWithACL(name string) error {
 
 
 	namep, err := syscall.UTF16PtrFromString(name)
 	namep, err := syscall.UTF16PtrFromString(name)
 	if err != nil {
 	if err != nil {
-		return &os.PathError{"mkdir", name, err}
+		return &os.PathError{Op: "mkdir", Path: name, Err: err}
 	}
 	}
 
 
 	e := syscall.CreateDirectory(namep, &sa)
 	e := syscall.CreateDirectory(namep, &sa)
 	if e != nil {
 	if e != nil {
-		return &os.PathError{"mkdir", name, e}
+		return &os.PathError{Op: "mkdir", Path: name, Err: e}
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -131,3 +131,106 @@ func IsAbs(path string) bool {
 	}
 	}
 	return true
 	return true
 }
 }
+
+// The origin of the functions below here are the golang OS and syscall packages,
+// slightly modified to only cope with files, not directories due to the
+// specific use case.
+//
+// The alteration is to allow a file on Windows to be opened with
+// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating
+// the standby list, particularly when accessing large files such as layer.tar.
+
+// CreateSequential creates the named file with mode 0666 (before umask), truncating
+// it if it already exists. If successful, methods on the returned
+// File can be used for I/O; the associated file descriptor has mode
+// O_RDWR.
+// If there is an error, it will be of type *PathError.
+func CreateSequential(name string) (*os.File, error) {
+	return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0)
+}
+
+// OpenSequential opens the named file for reading. If successful, methods on
+// the returned file can be used for reading; the associated file
+// descriptor has mode O_RDONLY.
+// If there is an error, it will be of type *PathError.
+func OpenSequential(name string) (*os.File, error) {
+	return OpenFileSequential(name, os.O_RDONLY, 0)
+}
+
+// OpenFileSequential is the generalized open call; most users will use Open
+// or Create instead.
+// If there is an error, it will be of type *PathError.
+func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) {
+	if name == "" {
+		return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
+	}
+	r, errf := syscallOpenFileSequential(name, flag, 0)
+	if errf == nil {
+		return r, nil
+	}
+	return nil, &os.PathError{Op: "open", Path: name, Err: errf}
+}
+
+func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
+	r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0)
+	if e != nil {
+		return nil, e
+	}
+	return os.NewFile(uintptr(r), name), nil
+}
+
+func makeInheritSa() *syscall.SecurityAttributes {
+	var sa syscall.SecurityAttributes
+	sa.Length = uint32(unsafe.Sizeof(sa))
+	sa.InheritHandle = 1
+	return &sa
+}
+
+func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) {
+	if len(path) == 0 {
+		return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
+	}
+	pathp, err := syscall.UTF16PtrFromString(path)
+	if err != nil {
+		return syscall.InvalidHandle, err
+	}
+	var access uint32
+	switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
+	case syscall.O_RDONLY:
+		access = syscall.GENERIC_READ
+	case syscall.O_WRONLY:
+		access = syscall.GENERIC_WRITE
+	case syscall.O_RDWR:
+		access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
+	}
+	if mode&syscall.O_CREAT != 0 {
+		access |= syscall.GENERIC_WRITE
+	}
+	if mode&syscall.O_APPEND != 0 {
+		access &^= syscall.GENERIC_WRITE
+		access |= syscall.FILE_APPEND_DATA
+	}
+	sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
+	var sa *syscall.SecurityAttributes
+	if mode&syscall.O_CLOEXEC == 0 {
+		sa = makeInheritSa()
+	}
+	var createmode uint32
+	switch {
+	case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
+		createmode = syscall.CREATE_NEW
+	case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
+		createmode = syscall.CREATE_ALWAYS
+	case mode&syscall.O_CREAT == syscall.O_CREAT:
+		createmode = syscall.OPEN_ALWAYS
+	case mode&syscall.O_TRUNC == syscall.O_TRUNC:
+		createmode = syscall.TRUNCATE_EXISTING
+	default:
+		createmode = syscall.OPEN_EXISTING
+	}
+	// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
+	//https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
+	const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
+	h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
+	return h, e
+}

部分文件因为文件数量过多而无法显示