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

1.13.0-rc2 cherry-picks : part 2
This commit is contained in:
Brian Goff 2016-11-20 10:43:55 -05:00 committed by GitHub
commit f7ae8204cb
117 changed files with 1166 additions and 534 deletions

View file

@ -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{ for _, status := range []struct {
"not found": http.StatusNotFound, keyword string
"no such": http.StatusNotFound, code int
"bad parameter": http.StatusBadRequest, }{
"no command": http.StatusBadRequest, {"not found", http.StatusNotFound},
"conflict": http.StatusConflict, {"no such", http.StatusNotFound},
"impossible": http.StatusNotAcceptable, {"bad parameter", http.StatusBadRequest},
"wrong login/password": http.StatusUnauthorized, {"no command", http.StatusBadRequest},
"unauthorized": http.StatusUnauthorized, {"conflict", http.StatusConflict},
"hasn't been activated": http.StatusForbidden, {"impossible", http.StatusNotAcceptable},
"this node": 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) { if strings.Contains(errStr, status.keyword) {
statusCode = status statusCode = status.code
break break
} }
} }

View file

@ -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.Experimental(router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints)),
router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint), router.Experimental(router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint)),
router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint), router.Experimental(router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint)),
} }
} }

View file

@ -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,
}
}

View file

@ -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) CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
UpdateService(string, uint64, types.ServiceSpec, string, string) 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)

View file

@ -1,9 +1,6 @@
package swarm package swarm
import ( import "github.com/docker/docker/api/server/router"
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/daemon"
)
// 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),

View file

@ -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{ return httputils.WriteJSON(w, http.StatusCreated, resp)
ID: id,
})
} }
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 {

View file

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

View file

@ -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: Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
description: "server error or node is not part of a swarm"
schema:
$ref: "#/definitions/ErrorResponse"
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: tags: [Service]
- "Services"
/tasks: /tasks:
get: get:
summary: "List tasks" summary: "List tasks"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err()) Tags: map[string]string{"experimental": "", "version": "1.25"},
cmd.HelpFunc()(cmd, args)
},
Tags: map[string]string{"experimental": "", "version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View file

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

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
NewAttachCommand(dockerCli), NewAttachCommand(dockerCli),

View file

@ -59,20 +59,18 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
return &cmd return &cmd
} }
type preProcessor struct { // listOptionsProcessor is used to set any container list options which may only
types.Container // be embedded in the format template.
opts *types.ContainerListOptions // 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
// Fields that need to exist so the template doesn't error out // Size sets the size of the map when called by a template execution.
// These are needed since they are available on the final object but are not func (o listOptionsProcessor) Size() bool {
// fields in types.Container o["size"] = true
// 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
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
} }

View file

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

View file

@ -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()

View file

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

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
NewBuildCommand(dockerCli), NewBuildCommand(dockerCli),

View file

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

View file

@ -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]", Use: "tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]",
Short: "Tag an image into a repository", 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]

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
newConnectCommand(dockerCli), newConnectCommand(dockerCli),

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
newDemoteCommand(dockerCli), newDemoteCommand(dockerCli),

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(

View file

@ -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) { RunE: dockerCli.ShowHelp,
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
},
} }
cmd.AddCommand( cmd.AddCommand(
newSecretListCommand(dockerCli), newSecretListCommand(dockerCli),

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View file

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

View file

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

View file

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

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err()) Tags: map[string]string{"experimental": "", "version": "1.25"},
cmd.HelpFunc()(cmd, args)
},
Tags: map[string]string{"experimental": "", "version": "1.25"},
} }
cmd.AddCommand( cmd.AddCommand(
newDeployCommand(dockerCli), newDeployCommand(dockerCli),

View file

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

View file

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

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
newInitCommand(dockerCli), newInitCommand(dockerCli),

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
NewEventsCommand(dockerCli), NewEventsCommand(dockerCli),

View file

@ -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) { RunE: dockerCli.ShowHelp,
cmd.SetOutput(dockerCli.Err())
cmd.HelpFunc()(cmd, args)
},
} }
cmd.AddCommand( cmd.AddCommand(
newCreateCommand(dockerCli), newCreateCommand(dockerCli),

View file

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

View file

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

View file

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

View file

@ -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)
} }

View file

@ -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()) return dockerCli.ShowHelp(cmd, args)
cmd.HelpFunc()(cmd, args)
return nil
}, },
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
}

View file

@ -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{} routers := []router.Router{
// we need to add the checkpoint router before the container router or the DELETE gets masked
// we need to add the checkpoint router before the container router or the DELETE gets masked checkpointrouter.NewRouter(d, decoder),
routers = addExperimentalRouters(routers, d, decoder)
routers = append(routers, []router.Router{
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...)
} }

View file

@ -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))
}

View file

@ -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 close(s.stop)
// has read the stop request and will not make any further updates
// to c.State.Health.
s.stop <- struct{}{}
s.stop = nil s.stop = nil
logrus.Debug("CloseMonitorChannel done") logrus.Debug("CloseMonitorChannel done")
} }

View file

@ -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:'

View file

@ -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) \

View file

@ -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 { if _, ok := ref.(distreference.Canonical); !ok {
ref = reference.WithDefaultTag(ref) // create a docker/docker/reference Named object because GetRepository needs it
dockerRef, err := reference.ParseNamed(image)
namedTaggedRef, ok := ref.(reference.NamedTagged) 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 namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
// To prevent this, we create the image string manually, which is a bad idea in general if err != nil {
// This will be fixed when https://github.com/docker/distribution/pull/2044 is vendored return "", err
// namedDigestedRef, err := reference.WithDigest(ref, dscrptr.Digest) }
// if err != nil { return namedDigestedRef.String(), 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
} }
// 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,

View file

@ -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, "comment": c.Comment,
"imageID": id.String(), "imageID": id.String(),
"imageRef": imageRef, "imageRef": imageRef,
} }
daemon.LogContainerEventWithAttributes(container, "commit", attributes) daemon.LogContainerEventWithAttributes(container, "commit", attributes)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
} }

View file

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

View file

@ -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(getShell(container.Config), cmdSlice...)
cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
} else {
cmdSlice = append([]string{"cmd", "/S", "/C"}, 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"}
}

View file

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

View file

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

View file

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

View file

@ -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) {
} }

View file

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

View file

@ -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 >**Note:** The `/run/docker/plugins` directory is mandatory inside of the
the plugin. 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

View file

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

View file

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

View file

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

View file

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

View file

@ -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**:

View file

@ -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**:

View file

@ -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**:

View file

@ -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**:

View file

@ -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**:

View file

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

View file

@ -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 Server Version: 1.13.0
Storage Driver: overlay 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 Network: bridge host macvlan null overlay
Swarm: Swarm: active
NodeID: 0gac67oclbxq7 NodeID: rdjq45w1op418waxlairloqbm
Is Manager: true Is Manager: true
Managers: 2 ClusterID: te8kdyw33n36fqiz74bfjeixd
Managers: 1
Nodes: 2 Nodes: 2
Runtimes: default Orchestration:
Default Runtime: default Task History Retention Limit: 5
Security Options: apparmor seccomp Raft:
Kernel Version: 4.4.0-21-generic Snapshot Interval: 10000
Operating System: Ubuntu 16.04 LTS 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 CPUs: 2
Total Memory: 62.86 GiB Total Memory: 1.937 GiB
Name: docker Name: ubuntu
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S 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 (client): true
Debug mode (server): true Debug Mode (server): true
File Descriptors: 59 File Descriptors: 30
Goroutines: 159 Goroutines: 123
System Time: 2016-04-26T10:04:06.14689342-04:00 System Time: 2016-11-12T17:24:37.955404361-08:00
EventsListeners: 0 EventsListeners: 0
Http Proxy: http://test:test@localhost:8080 Http Proxy: http://proxy.example.com:80/
Https Proxy: https://test:test@localhost:8080
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: Experimental: false
myinsecurehost:5000 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

View file

@ -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) -a, --all Show all containers (default shows just running)
--help Print usage --format string Pretty-print images using a Go template
--no-stream Disable streaming stats and only pull the first result --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

View file

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

View file

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

View file

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

View file

@ -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=()

View file

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

View file

@ -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 {
// Use system.CreateSequential rather than os.Create. This ensures sequential
tarFile, err := os.Create(layerPath) // 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
} }

View file

@ -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())

View file

@ -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:/wc1) = 'hellowc1' ]"
RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]" 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")
}

View file

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

View file

@ -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))
}

View file

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

View file

@ -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 Server Version: 1.13.0
Storage Driver: overlay 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 Network: bridge host macvlan null overlay
Swarm: Swarm: active
NodeID: 0gac67oclbxq7 NodeID: rdjq45w1op418waxlairloqbm
IsManager: YES Is Manager: true
Managers: 2 ClusterID: te8kdyw33n36fqiz74bfjeixd
Managers: 1
Nodes: 2 Nodes: 2
Runtimes: default Orchestration:
Default Runtime: default Task History Retention Limit: 5
Security Options: apparmor seccomp Raft:
Kernel Version: 4.4.0-21-generic Snapshot Interval: 10000
Operating System: Ubuntu 16.04 LTS 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 CPUs: 2
Total Memory: 62.86 GiB Total Memory: 1.937 GiB
Name: docker Name: ubuntu
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S 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 (client): true
Debug mode (server): true Debug Mode (server): true
File Descriptors: 59 File Descriptors: 30
Goroutines: 159 Goroutines: 123
System Time: 2016-04-26T10:04:06.14689342-04:00 System Time: 2016-11-12T17:24:37.955404361-08:00
EventsListeners: 0 EventsListeners: 0
Http Proxy: http://test:test@localhost:8080 Http Proxy: http://proxy.example.com:80/
Https Proxy: https://test:test@localhost:8080
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: Experimental: false
myinsecurehost:5000 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.

View file

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

View file

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

View file

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

View file

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

View file

@ -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 // Source is regular file. We use system.OpenFileSequential to use sequential
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode()) // 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
} }

View file

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

View file

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

View file

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

View file

@ -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)
}
}
}

View file

@ -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)
}

View file

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

Some files were not shown because too many files have changed in this diff Show more