Merge pull request #28489 from vieux/1.13.0-rc2-cherrypicks
1.13.0-rc2 cherry-picks : part 2
This commit is contained in:
commit
f7ae8204cb
117 changed files with 1166 additions and 534 deletions
|
@ -28,7 +28,7 @@ type inputValidationError interface {
|
|||
IsValidationError() bool
|
||||
}
|
||||
|
||||
// GetHTTPErrorStatusCode retrieve status code from error message
|
||||
// GetHTTPErrorStatusCode retrieves status code from error message
|
||||
func GetHTTPErrorStatusCode(err error) int {
|
||||
if err == nil {
|
||||
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,
|
||||
// we should create appropriate error types that implement the httpStatusError interface.
|
||||
errStr := strings.ToLower(errMsg)
|
||||
for keyword, status := range map[string]int{
|
||||
"not found": http.StatusNotFound,
|
||||
"no such": http.StatusNotFound,
|
||||
"bad parameter": http.StatusBadRequest,
|
||||
"no command": http.StatusBadRequest,
|
||||
"conflict": http.StatusConflict,
|
||||
"impossible": http.StatusNotAcceptable,
|
||||
"wrong login/password": http.StatusUnauthorized,
|
||||
"unauthorized": http.StatusUnauthorized,
|
||||
"hasn't been activated": http.StatusForbidden,
|
||||
"this node": http.StatusNotAcceptable,
|
||||
for _, status := range []struct {
|
||||
keyword string
|
||||
code int
|
||||
}{
|
||||
{"not found", http.StatusNotFound},
|
||||
{"no such", http.StatusNotFound},
|
||||
{"bad parameter", http.StatusBadRequest},
|
||||
{"no command", http.StatusBadRequest},
|
||||
{"conflict", http.StatusConflict},
|
||||
{"impossible", http.StatusNotAcceptable},
|
||||
{"wrong login/password", http.StatusUnauthorized},
|
||||
{"unauthorized", http.StatusUnauthorized},
|
||||
{"hasn't been activated", http.StatusForbidden},
|
||||
{"this node", http.StatusServiceUnavailable},
|
||||
} {
|
||||
if strings.Contains(errStr, keyword) {
|
||||
statusCode = status
|
||||
if strings.Contains(errStr, status.keyword) {
|
||||
statusCode = status.code
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ func (r *checkpointRouter) Routes() []router.Route {
|
|||
|
||||
func (r *checkpointRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints),
|
||||
router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint),
|
||||
router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint),
|
||||
router.Experimental(router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints)),
|
||||
router.Experimental(router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint)),
|
||||
router.Experimental(router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint)),
|
||||
}
|
||||
}
|
||||
|
|
67
api/server/router/experimental.go
Normal file
67
api/server/router/experimental.go
Normal 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,
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ type Backend interface {
|
|||
UnlockSwarm(req types.UnlockRequest) error
|
||||
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
|
||||
GetService(string) (types.Service, error)
|
||||
CreateService(types.ServiceSpec, string) (string, error)
|
||||
UpdateService(string, uint64, types.ServiceSpec, string, string) error
|
||||
CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
|
||||
UpdateService(string, uint64, types.ServiceSpec, string, string) (*basictypes.ServiceUpdateResponse, error)
|
||||
RemoveService(string) error
|
||||
ServiceLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
|
||||
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package swarm
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/daemon"
|
||||
)
|
||||
import "github.com/docker/docker/api/server/router"
|
||||
|
||||
// swarmRouter is a router to talk with the build controller
|
||||
type swarmRouter struct {
|
||||
|
@ -12,14 +9,11 @@ type swarmRouter struct {
|
|||
}
|
||||
|
||||
// NewRouter initializes a new build router
|
||||
func NewRouter(d *daemon.Daemon, b Backend) router.Router {
|
||||
func NewRouter(b Backend) router.Router {
|
||||
r := &swarmRouter{
|
||||
backend: b,
|
||||
}
|
||||
r.initRoutes()
|
||||
if d.HasExperimental() {
|
||||
r.addExperimentalRoutes()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -28,12 +22,6 @@ func (sr *swarmRouter) Routes() []router.Route {
|
|||
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() {
|
||||
sr.routes = []router.Route{
|
||||
router.NewPostRoute("/swarm/init", sr.initCluster),
|
||||
|
@ -48,6 +36,7 @@ func (sr *swarmRouter) initRoutes() {
|
|||
router.NewPostRoute("/services/create", sr.createService),
|
||||
router.NewPostRoute("/services/{id}/update", sr.updateService),
|
||||
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/{id}", sr.getNode),
|
||||
router.NewDeleteRoute("/nodes/{id}", sr.removeNode),
|
||||
|
|
|
@ -166,15 +166,13 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
|
|||
// Get returns "" if the header does not exist
|
||||
encodedAuth := r.Header.Get("X-Registry-Auth")
|
||||
|
||||
id, err := sr.backend.CreateService(service, encodedAuth)
|
||||
resp, err := sr.backend.CreateService(service, encodedAuth)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating service %s: %v", service.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ServiceCreateResponse{
|
||||
ID: id,
|
||||
})
|
||||
return httputils.WriteJSON(w, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
@ -194,11 +192,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
|
|||
|
||||
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)
|
||||
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 {
|
||||
|
|
|
@ -102,7 +102,7 @@ func (s *Server) serveAPI() error {
|
|||
}
|
||||
|
||||
// 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.
|
||||
type HTTPServer struct {
|
||||
srv *http.Server
|
||||
|
|
|
@ -2218,6 +2218,16 @@ definitions:
|
|||
Deleted:
|
||||
description: "The image ID of an image that was deleted"
|
||||
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:
|
||||
type: "array"
|
||||
items:
|
||||
|
@ -6731,14 +6741,14 @@ paths:
|
|||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
406:
|
||||
description: "node is already part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
500:
|
||||
description: "server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
503:
|
||||
description: "node is already part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
parameters:
|
||||
- name: "body"
|
||||
in: "body"
|
||||
|
@ -6773,14 +6783,14 @@ paths:
|
|||
responses:
|
||||
200:
|
||||
description: "no error"
|
||||
406:
|
||||
description: "node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
500:
|
||||
description: "server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
503:
|
||||
description: "node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
parameters:
|
||||
- name: "force"
|
||||
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"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
406:
|
||||
description: "node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
500:
|
||||
description: "server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
503:
|
||||
description: "node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
parameters:
|
||||
- name: "body"
|
||||
in: "body"
|
||||
|
@ -6875,12 +6885,12 @@ paths:
|
|||
ID:
|
||||
description: "The ID of the created service."
|
||||
type: "string"
|
||||
Warning:
|
||||
description: "Optional warning message"
|
||||
type: "string"
|
||||
example:
|
||||
ID: "ak7w3gjqoa3kuz8xcpnyy0pvl"
|
||||
406:
|
||||
description: "server error or node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
|
||||
409:
|
||||
description: "name conflicts with an existing service"
|
||||
schema:
|
||||
|
@ -6889,6 +6899,10 @@ paths:
|
|||
description: "server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
503:
|
||||
description: "server error or node is not part of a swarm"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
parameters:
|
||||
- name: "body"
|
||||
in: "body"
|
||||
|
@ -6998,10 +7012,14 @@ paths:
|
|||
/services/{id}/update:
|
||||
post:
|
||||
summary: "Update a service"
|
||||
operationId: "PostServicesUpdate"
|
||||
operationId: "ServiceUpdate"
|
||||
consumes: ["application/json"]
|
||||
produces: ["application/json"]
|
||||
responses:
|
||||
200:
|
||||
description: "no error"
|
||||
schema:
|
||||
$ref: "#/definitions/ImageDeleteResponse"
|
||||
404:
|
||||
description: "no such service"
|
||||
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)"
|
||||
type: "string"
|
||||
|
||||
tags:
|
||||
- "Services"
|
||||
tags: [Service]
|
||||
/tasks:
|
||||
get:
|
||||
summary: "List tasks"
|
||||
|
|
|
@ -285,10 +285,12 @@ type ServiceCreateOptions struct {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
// ID is the ID of the created service.
|
||||
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
|
||||
|
|
12
api/types/service_update_response.go
Normal file
12
api/types/service_update_response.go
Normal 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"`
|
||||
}
|
|
@ -74,7 +74,7 @@ const (
|
|||
type UpdateConfig struct {
|
||||
// Maximum number of tasks to be updated in one iteration.
|
||||
// 0 means unlimited parallelism.
|
||||
Parallelism uint64 `json:",omitempty"`
|
||||
Parallelism uint64
|
||||
|
||||
// Amount of time between updates.
|
||||
Delay time.Duration `json:",omitempty"`
|
||||
|
|
|
@ -3,6 +3,7 @@ package v1p24
|
|||
|
||||
import "github.com/docker/docker/api/types"
|
||||
|
||||
// Info is a backcompatibility struct for the API 1.24
|
||||
type Info struct {
|
||||
*types.InfoBase
|
||||
ExecutionDriver string
|
||||
|
|
|
@ -11,7 +11,7 @@ type Volume struct {
|
|||
// Required: true
|
||||
Driver string `json:"Driver"`
|
||||
|
||||
// A mapping of abitrary key/value data set on this volume.
|
||||
// User-defined key/value metadata.
|
||||
// Required: true
|
||||
Labels map[string]string `json:"Labels"`
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ type VolumesCreateBody struct {
|
|||
// Required: true
|
||||
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
|
||||
Labels map[string]string `json:"Labels"`
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import "fmt"
|
|||
// a command not supported on the platform.
|
||||
func platformSupports(command string) error {
|
||||
switch command {
|
||||
case "user", "stopsignal":
|
||||
case "stopsignal":
|
||||
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -12,11 +12,8 @@ func NewCheckpointCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
dopts "github.com/docker/docker/opts"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
|
@ -73,6 +74,13 @@ func (cli *DockerCli) In() *InStream {
|
|||
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
|
||||
func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
||||
return cli.configFile
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "container",
|
||||
Short: "Manage containers",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewAttachCommand(dockerCli),
|
||||
|
|
|
@ -59,20 +59,18 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
return &cmd
|
||||
}
|
||||
|
||||
type preProcessor struct {
|
||||
types.Container
|
||||
opts *types.ContainerListOptions
|
||||
// listOptionsProcessor is used to set any container list options which may only
|
||||
// be embedded in the format template.
|
||||
// This is passed directly into tmpl.Execute in order to allow the preprocessor
|
||||
// to set any list options that were not provided by flags (e.g. `.Size`).
|
||||
// It is using a `map[string]bool` so that unknown fields passed into the
|
||||
// template format do not cause errors. These errors will get picked up when
|
||||
// running through the actual template processor.
|
||||
type listOptionsProcessor map[string]bool
|
||||
|
||||
// Fields that need to exist so the template doesn't error out
|
||||
// These are needed since they are available on the final object but are not
|
||||
// fields in types.Container
|
||||
// TODO(cpuguy83): this seems rather broken
|
||||
Networks, CreatedAt, RunningFor bool
|
||||
}
|
||||
|
||||
// Size sets the size option when called by a template execution.
|
||||
func (p *preProcessor) Size() bool {
|
||||
p.opts.Size = true
|
||||
// Size sets the size of the map when called by a template execution.
|
||||
func (o listOptionsProcessor) Size() bool {
|
||||
o["size"] = true
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -88,20 +86,20 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
|||
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)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
optionsProcessor := listOptionsProcessor{}
|
||||
// This shouldn't error out but swallowing the error makes it harder
|
||||
// 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
|
||||
}
|
||||
// At the moment all we need is to capture .Size for preprocessor
|
||||
options.Size = opts.size || optionsProcessor["size"]
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
|
|
@ -46,6 +46,57 @@ func TestBuildContainerListOptions(t *testing.T) {
|
|||
expectedLimit: 1,
|
||||
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 {
|
||||
|
|
|
@ -372,7 +372,7 @@ func TestContainerContextWriteJSONField(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestContainerBackCompat(t *testing.T) {
|
||||
containers := []types.Container{types.Container{ID: "brewhaha"}}
|
||||
containers := []types.Container{{ID: "brewhaha"}}
|
||||
cases := []string{
|
||||
"ID",
|
||||
"Names",
|
||||
|
@ -390,7 +390,7 @@ func TestContainerBackCompat(t *testing.T) {
|
|||
for _, c := range cases {
|
||||
ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
|
||||
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()
|
||||
}
|
||||
buf.Reset()
|
||||
|
|
|
@ -263,6 +263,9 @@ func (ctx *serviceInspectContext) HasResources() 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
|
||||
}
|
||||
|
||||
|
@ -281,6 +284,9 @@ func (ctx *serviceInspectContext) ResourceReservationMemory() string {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "image",
|
||||
Short: "Manage images",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewBuildCommand(dockerCli),
|
||||
|
|
|
@ -3,13 +3,13 @@ package image
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -43,7 +43,9 @@ func runLoad(dockerCli *command.DockerCli, opts loadOptions) error {
|
|||
|
||||
var input io.Reader = dockerCli.In()
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
var opts tagOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "tag IMAGE[:TAG] IMAGE[:TAG]",
|
||||
Short: "Tag an image into a repository",
|
||||
Use: "tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]",
|
||||
Short: "Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.image = args[0]
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewNetworkCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "network",
|
||||
Short: "Manage networks",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newConnectCommand(dockerCli),
|
||||
|
|
|
@ -14,10 +14,7 @@ func NewNodeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "node",
|
||||
Short: "Manage Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDemoteCommand(dockerCli),
|
||||
|
|
|
@ -12,10 +12,7 @@ func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/docker/cli"
|
||||
|
@ -15,9 +13,7 @@ func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "secret",
|
||||
Short: "Manage Docker secrets",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newSecretListCommand(dockerCli),
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "service",
|
||||
Short: "Manage services",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
|
|
@ -90,6 +90,10 @@ func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintln(dockerCli.Err(), warning)
|
||||
}
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -82,11 +82,15 @@ func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint6
|
|||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintln(dockerCli.Err(), warning)
|
||||
}
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -133,11 +133,15 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintln(dockerCli.Err(), warning)
|
||||
}
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,11 +12,8 @@ func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "stack",
|
||||
Short: "Manage Docker stacks",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDeployCommand(dockerCli),
|
||||
|
|
|
@ -408,15 +408,20 @@ func deployServices(
|
|||
if sendAuth {
|
||||
updateOpts.EncodedRegistryAuth = encodedAuth
|
||||
}
|
||||
if err := apiClient.ServiceUpdate(
|
||||
response, err := apiClient.ServiceUpdate(
|
||||
ctx,
|
||||
service.ID,
|
||||
service.Version,
|
||||
serviceSpec,
|
||||
updateOpts,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintln(dockerCli.Err(), warning)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(out, "Creating service %s\n", name)
|
||||
|
||||
|
@ -526,7 +531,7 @@ func convertService(
|
|||
func convertExtraHosts(extraHosts map[string]string) []string {
|
||||
hosts := []string{}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ func printTable(out io.Writer, stacks []*stack) {
|
|||
|
||||
type stack struct {
|
||||
// Name is the name of the stack
|
||||
Name string
|
||||
Name string
|
||||
// Services is the number of the services
|
||||
Services int
|
||||
}
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewSwarmCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "swarm",
|
||||
Short: "Manage Swarm",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newInitCommand(dockerCli),
|
||||
|
|
|
@ -13,10 +13,7 @@ func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Use: "system",
|
||||
Short: "Manage Docker",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewEventsCommand(dockerCli),
|
||||
|
|
|
@ -14,10 +14,7 @@ func NewVolumeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
Short: "Manage volumes",
|
||||
Long: volumeDescription,
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
},
|
||||
RunE: dockerCli.ShowHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
|
|
@ -124,7 +124,7 @@ type ServiceAPIClient interface {
|
|||
ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
|
||||
ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, 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)
|
||||
TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
|
||||
TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
// 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.
|
||||
query := url.Values{}
|
||||
query.Set("name", name)
|
||||
|
@ -27,6 +27,14 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
|||
ensureReaderClosed(resp)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
delResp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
|
||||
ensureReaderClosed(delResp)
|
||||
}
|
||||
}()
|
||||
|
||||
var privileges types.PluginPrivileges
|
||||
if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
|
||||
ensureReaderClosed(resp)
|
||||
|
@ -40,8 +48,6 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
|||
return err
|
||||
}
|
||||
if !accept {
|
||||
resp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
|
||||
ensureReaderClosed(resp)
|
||||
return pluginPermissionDenied{name}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// 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 (
|
||||
headers map[string][]string
|
||||
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))
|
||||
|
||||
var response types.ServiceUpdateResponse
|
||||
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)
|
||||
return err
|
||||
return response, err
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestServiceUpdateError(t *testing.T) {
|
|||
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" {
|
||||
t.Fatalf("expected a Server Error, got %v", err)
|
||||
}
|
||||
|
@ -64,12 +64,12 @@ func TestServiceUpdate(t *testing.T) {
|
|||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))),
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))),
|
||||
}, 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
@ -34,27 +35,37 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
showVersion()
|
||||
return nil
|
||||
}
|
||||
cmd.SetOutput(dockerCli.Err())
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
return nil
|
||||
return dockerCli.ShowHelp(cmd, args)
|
||||
},
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
// 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()
|
||||
opts.Common.SetDefaultOptions(flags)
|
||||
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)
|
||||
|
||||
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()
|
||||
opts.Common.SetDefaultOptions(flags)
|
||||
dockerPreRun(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())
|
||||
|
||||
if err := ccmd.Help(); err != nil {
|
||||
|
@ -79,7 +90,7 @@ func noArgs(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/docker/docker/api/server/middleware"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"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/image"
|
||||
"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) {
|
||||
decoder := runconfig.ContainerDecoder{}
|
||||
|
||||
routers := []router.Router{}
|
||||
|
||||
// we need to add the checkpoint router before the container router or the DELETE gets masked
|
||||
routers = addExperimentalRouters(routers, d, decoder)
|
||||
|
||||
routers = append(routers, []router.Router{
|
||||
routers := []router.Router{
|
||||
// we need to add the checkpoint router before the container router or the DELETE gets masked
|
||||
checkpointrouter.NewRouter(d, decoder),
|
||||
container.NewRouter(d, decoder),
|
||||
image.NewRouter(d, decoder),
|
||||
systemrouter.NewRouter(d, c),
|
||||
volume.NewRouter(d),
|
||||
build.NewRouter(dockerfile.NewBuildManager(d)),
|
||||
swarmrouter.NewRouter(d, c),
|
||||
swarmrouter.NewRouter(c),
|
||||
pluginrouter.NewRouter(plugin.GetManager()),
|
||||
}...)
|
||||
}
|
||||
|
||||
if d.NetworkControllerEnabled() {
|
||||
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...)
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -42,10 +42,7 @@ func (s *Health) OpenMonitorChannel() chan struct{} {
|
|||
func (s *Health) CloseMonitorChannel() {
|
||||
if s.stop != nil {
|
||||
logrus.Debug("CloseMonitorChannel: waiting for probe to stop")
|
||||
// This channel does not buffer. Once the write succeeds, the monitor
|
||||
// has read the stop request and will not make any further updates
|
||||
// to c.State.Health.
|
||||
s.stop <- struct{}{}
|
||||
close(s.stop)
|
||||
s.stop = nil
|
||||
logrus.Debug("CloseMonitorChannel done")
|
||||
}
|
||||
|
|
|
@ -188,7 +188,6 @@ fi
|
|||
|
||||
flags=(
|
||||
NAMESPACES {NET,PID,IPC,UTS}_NS
|
||||
DEVPTS_MULTIPLE_INSTANCES
|
||||
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
|
||||
KEYS
|
||||
VETH BRIDGE BRIDGE_NETFILTER
|
||||
|
@ -200,6 +199,10 @@ flags=(
|
|||
POSIX_MQUEUE
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -lt 8 ]; then
|
||||
check_flags DEVPTS_MULTIPLE_INSTANCES
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
echo 'Optional Features:'
|
||||
|
|
|
@ -1961,7 +1961,7 @@ __docker_swarm_subcommand() {
|
|||
__docker_system_commands() {
|
||||
local -a _docker_system_subcommands
|
||||
_docker_system_subcommands=(
|
||||
"df:Show docker disk usage"
|
||||
"df:Show docker filesystem usage"
|
||||
"events:Get real time events from the server"
|
||||
"info:Display system-wide information"
|
||||
"prune:Remove unused data"
|
||||
|
@ -1978,7 +1978,9 @@ __docker_system_subcommand() {
|
|||
|
||||
case "$words[1]" in
|
||||
(df)
|
||||
# @TODO
|
||||
_arguments $(__docker_arguments) \
|
||||
$opts_help \
|
||||
"($help -v --verbose)"{-v,--verbose}"[Show detailed information on space usage]" && ret=0
|
||||
;;
|
||||
(events)
|
||||
_arguments $(__docker_arguments) \
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
distreference "github.com/docker/distribution/reference"
|
||||
apierrors "github.com/docker/docker/api/errors"
|
||||
apitypes "github.com/docker/docker/api/types"
|
||||
"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.
|
||||
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.")
|
||||
|
||||
// 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.
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
// 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) {
|
||||
ref, err := reference.ParseNamed(image)
|
||||
ref, err := distreference.ParseNamed(image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// only query registry if not a canonical reference (i.e. with digest)
|
||||
if _, ok := ref.(reference.Canonical); !ok {
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
|
||||
namedTaggedRef, ok := ref.(reference.NamedTagged)
|
||||
if _, ok := ref.(distreference.Canonical); !ok {
|
||||
// create a docker/docker/reference Named object because GetRepository needs it
|
||||
dockerRef, err := reference.ParseNamed(image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerRef = reference.WithDefaultTag(dockerRef)
|
||||
namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
|
||||
// TODO(nishanttotla): Currently, the service would lose the tag while calling WithDigest
|
||||
// To prevent this, we create the image string manually, which is a bad idea in general
|
||||
// This will be fixed when https://github.com/docker/distribution/pull/2044 is vendored
|
||||
// namedDigestedRef, err := reference.WithDigest(ref, dscrptr.Digest)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// return namedDigestedRef.String(), nil
|
||||
return image + "@" + dscrptr.Digest.String(), nil
|
||||
} else {
|
||||
// reference already contains a digest, so just return it
|
||||
return ref.String(), nil
|
||||
namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return namedDigestedRef.String(), nil
|
||||
}
|
||||
// reference already contains a digest, so just return it
|
||||
return ref.String(), nil
|
||||
}
|
||||
|
||||
// CreateService creates a new service in a managed swarm cluster.
|
||||
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()
|
||||
defer c.RUnlock()
|
||||
|
||||
if !c.isActiveManager() {
|
||||
return "", c.errNoManager()
|
||||
return nil, c.errNoManager()
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceSpec, err := convert.ServiceSpecToGRPC(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctnr := serviceSpec.Task.GetContainer()
|
||||
if ctnr == nil {
|
||||
return "", fmt.Errorf("service does not use container tasks")
|
||||
return nil, fmt.Errorf("service does not use container tasks")
|
||||
}
|
||||
|
||||
if encodedAuth != "" {
|
||||
|
@ -1084,11 +1098,15 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
|
|||
logrus.Warnf("invalid authconfig: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
resp := &apitypes.ServiceCreateResponse{}
|
||||
|
||||
// pin image by digest
|
||||
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
||||
digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig)
|
||||
if err != nil {
|
||||
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 {
|
||||
logrus.Debugf("pinning image %s by digest: %s", 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})
|
||||
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.
|
||||
|
@ -1123,12 +1142,12 @@ func (c *Cluster) GetService(input string) (types.Service, error) {
|
|||
}
|
||||
|
||||
// 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()
|
||||
defer c.RUnlock()
|
||||
|
||||
if !c.isActiveManager() {
|
||||
return c.errNoManager()
|
||||
return nil, c.errNoManager()
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceSpec, err := convert.ServiceSpecToGRPC(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentService, err := getService(ctx, c.client, serviceIDOrName)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newCtnr := serviceSpec.Task.GetContainer()
|
||||
if newCtnr == nil {
|
||||
return fmt.Errorf("service does not use container tasks")
|
||||
return nil, fmt.Errorf("service does not use container tasks")
|
||||
}
|
||||
|
||||
if encodedAuth != "" {
|
||||
|
@ -1165,14 +1184,14 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
|
|||
ctnr = currentService.Spec.Task.GetContainer()
|
||||
case apitypes.RegistryAuthFromPreviousSpec:
|
||||
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()
|
||||
default:
|
||||
return fmt.Errorf("unsupported registryAuthFromValue")
|
||||
return nil, fmt.Errorf("unsupported registryAuthFromValue")
|
||||
}
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
resp := &apitypes.ServiceUpdateResponse{}
|
||||
|
||||
// pin image by digest
|
||||
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
||||
digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig)
|
||||
if err != nil {
|
||||
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 {
|
||||
logrus.Debugf("pinning image %s by digest: %s", 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.
|
||||
|
@ -1369,7 +1393,7 @@ func (c *Cluster) GetNode(input string) (types.Node, error) {
|
|||
}
|
||||
|
||||
// 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()
|
||||
defer c.RUnlock()
|
||||
|
||||
|
@ -1385,10 +1409,15 @@ func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec)
|
|||
ctx, cancel := c.getRequestContext()
|
||||
defer cancel()
|
||||
|
||||
currentNode, err := getNode(ctx, c.client, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.client.UpdateNode(
|
||||
ctx,
|
||||
&swarmapi.UpdateNodeRequest{
|
||||
NodeID: nodeID,
|
||||
NodeID: currentNode.ID,
|
||||
Spec: &nodeSpec,
|
||||
NodeVersion: &swarmapi.Version{
|
||||
Index: version,
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
userConf.ExposedPorts = imageConf.ExposedPorts
|
||||
} else if imageConf.ExposedPorts != nil {
|
||||
if userConf.ExposedPorts == nil {
|
||||
userConf.ExposedPorts = make(nat.PortSet)
|
||||
}
|
||||
for port := range imageConf.ExposedPorts {
|
||||
if _, exists := userConf.ExposedPorts[port]; !exists {
|
||||
userConf.ExposedPorts[port] = struct{}{}
|
||||
|
@ -244,8 +240,8 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
|
|||
}
|
||||
|
||||
attributes := map[string]string{
|
||||
"comment": c.Comment,
|
||||
"imageID": id.String(),
|
||||
"comment": c.Comment,
|
||||
"imageID": id.String(),
|
||||
"imageRef": imageRef,
|
||||
}
|
||||
daemon.LogContainerEventWithAttributes(container, "commit", attributes)
|
||||
|
|
|
@ -16,14 +16,6 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
|
|||
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 {
|
||||
|
||||
mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
|
||||
|
|
|
@ -550,7 +550,12 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
|||
driverName = config.GraphDriver
|
||||
}
|
||||
|
||||
d.RegistryService = registryService
|
||||
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{
|
||||
StorePath: config.Root,
|
||||
|
@ -649,7 +654,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
|||
Type: config.LogConfig.Type,
|
||||
Config: config.LogConfig.Config,
|
||||
}
|
||||
d.RegistryService = registryService
|
||||
d.EventsService = eventsService
|
||||
d.volumes = volStore
|
||||
d.root = config.Root
|
||||
|
@ -668,11 +672,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
|||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -112,9 +112,7 @@ func getCPUResources(config containertypes.Resources) *specs.CPU {
|
|||
}
|
||||
|
||||
if config.NanoCPUs > 0 {
|
||||
// Use the default setting of 100ms, as is specified in:
|
||||
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
|
||||
// cpu.cfs_period_us=100ms
|
||||
period := uint64(100 * time.Millisecond / time.Microsecond)
|
||||
quota := uint64(config.NanoCPUs) * period / 1e9
|
||||
cpu.Period = &period
|
||||
|
@ -361,8 +359,15 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
|
|||
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")
|
||||
}
|
||||
// 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 {
|
||||
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 {
|
||||
|
|
|
@ -129,8 +129,10 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool
|
|||
if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
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()
|
||||
return path, nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package graphdriver
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"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) {
|
||||
proxy := &graphDriverProxy{name, c}
|
||||
return proxy, proxy.Init(home, opts)
|
||||
return proxy, proxy.Init(filepath.Join(home, name), opts)
|
||||
}
|
||||
|
|
|
@ -165,18 +165,16 @@ func (d *Driver) Exists(id string) bool {
|
|||
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
if opts != nil {
|
||||
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.
|
||||
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
if opts != nil {
|
||||
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 {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"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/container"
|
||||
"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:]
|
||||
if p.shell {
|
||||
if runtime.GOOS != "windows" {
|
||||
cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
|
||||
} else {
|
||||
cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...)
|
||||
}
|
||||
cmdSlice = append(getShell(container.Config), cmdSlice...)
|
||||
}
|
||||
entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice)
|
||||
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.
|
||||
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()
|
||||
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
|
||||
if retries <= 0 {
|
||||
retries = defaultProbeRetries
|
||||
|
@ -183,7 +187,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
|
|||
cancelProbe()
|
||||
return
|
||||
case result := <-results:
|
||||
handleProbeResult(d, c, result)
|
||||
handleProbeResult(d, c, result, stop)
|
||||
// Stop timeout
|
||||
cancelProbe()
|
||||
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),
|
||||
Start: startTime,
|
||||
End: time.Now(),
|
||||
})
|
||||
}, stop)
|
||||
cancelProbe()
|
||||
// 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).
|
||||
|
@ -325,3 +329,13 @@ func min(x, y int) int {
|
|||
}
|
||||
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"}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestHealthStates(t *testing.T) {
|
|||
Start: startTime,
|
||||
End: startTime,
|
||||
ExitCode: exitCode,
|
||||
})
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// starting -> failed -> success -> failed
|
||||
|
|
|
@ -106,6 +106,7 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
|
|||
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) {
|
||||
// get repository info
|
||||
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
// FillPlatformInfo fills the platform related info.
|
||||
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
||||
v.MemoryLimit = sysInfo.MemoryLimit
|
||||
v.SwapLimit = sysInfo.SwapLimit
|
||||
|
|
|
@ -5,5 +5,6 @@ import (
|
|||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
// FillPlatformInfo fills the platform related info.
|
||||
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ Config provides the base accessible fields for working with V0 plugin format
|
|||
|
||||
- **docker.volumedriver/1.0**
|
||||
|
||||
- **docker.authz/1.0**
|
||||
|
||||
- **`socket`** *string*
|
||||
|
||||
socket is the name of the socket the engine should use to communicate with the plugins.
|
||||
|
|
|
@ -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
|
||||
example, it was created from a Dockerfile:
|
||||
|
||||
>**Note:** The `/run/docker/plugins` directory is mandatory for docker to communicate with
|
||||
the plugin.
|
||||
>**Note:** The `/run/docker/plugins` directory is mandatory inside of the
|
||||
plugin's filesystem for docker to communicate with the plugin.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/vieux/docker-volume-sshfs
|
||||
|
|
|
@ -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 /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/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.
|
||||
* `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.
|
||||
|
@ -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/create?name=(plugin name)` create 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
|
||||
|
|
|
@ -1044,7 +1044,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.18/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1462,7 +1462,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.18/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1765,7 +1765,7 @@ Docker images report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.18/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1807,7 +1807,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.18/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1836,7 +1836,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.18/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1859,7 +1859,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.18/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
|
|
@ -1083,7 +1083,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.19/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1528,7 +1528,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.19/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1845,7 +1845,7 @@ Docker images report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.19/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1887,7 +1887,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.19/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1916,7 +1916,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.19/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1939,7 +1939,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.19/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
|
|
@ -1090,7 +1090,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.20/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1235,7 +1235,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -1682,7 +1682,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.20/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2000,7 +2000,7 @@ Docker images report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.20/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2042,7 +2042,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.20/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2071,7 +2071,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.20/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2094,7 +2094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.20/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
|
|
@ -1173,7 +1173,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.21/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1318,7 +1318,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -1835,7 +1835,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.21/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2155,7 +2155,7 @@ Docker images report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.21/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2198,7 +2198,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.21/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2227,7 +2227,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.21/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2250,7 +2250,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.21/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
@ -2637,7 +2637,7 @@ Instruct the driver to remove the volume (`name`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /volumes/tardis HTTP/1.1
|
||||
DELETE /v1.21/volumes/tardis HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2912,7 +2912,7 @@ Instruct the driver to remove the network (`id`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
DELETE /v1.21/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
|
|
@ -257,6 +257,7 @@ Create a container
|
|||
"StopSignal": "SIGTERM",
|
||||
"HostConfig": {
|
||||
"Binds": ["/tmp:/tmp"],
|
||||
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||
"Links": ["redis3:redis"],
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
|
@ -364,6 +365,8 @@ Create a container
|
|||
_absolute_ path.
|
||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||
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
|
||||
in the form of `container_name:alias`.
|
||||
- **Memory** - Memory limit in bytes.
|
||||
|
@ -1349,7 +1352,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.22/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1494,7 +1497,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -2049,7 +2052,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.22/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2394,7 +2397,7 @@ Docker networks report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.22/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2586,7 +2589,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.22/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2615,7 +2618,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.22/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2638,7 +2641,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.22/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
@ -2947,7 +2950,7 @@ Instruct the driver to remove the volume (`name`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /volumes/tardis HTTP/1.1
|
||||
DELETE /v1.22/volumes/tardis HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3247,7 +3250,7 @@ Instruct the driver to remove the network (`id`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
DELETE /v1.22/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
|
|
@ -281,6 +281,7 @@ Create a container
|
|||
"StopSignal": "SIGTERM",
|
||||
"HostConfig": {
|
||||
"Binds": ["/tmp:/tmp"],
|
||||
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||
"Links": ["redis3:redis"],
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
|
@ -389,6 +390,8 @@ Create a container
|
|||
_absolute_ path.
|
||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||
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
|
||||
in the form of `container_name:alias`.
|
||||
- **Memory** - Memory limit in bytes.
|
||||
|
@ -1384,7 +1387,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.23/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1529,7 +1532,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -2092,7 +2095,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.23/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2444,7 +2447,7 @@ Docker networks report the following events:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.23/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2636,7 +2639,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.23/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2665,7 +2668,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.23/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2688,7 +2691,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.23/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
@ -3005,7 +3008,7 @@ Return low-level information on the volume `name`
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /volumes/tardis
|
||||
GET /v1.23/volumes/tardis
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3036,7 +3039,7 @@ Instruct the driver to remove the volume (`name`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /volumes/tardis HTTP/1.1
|
||||
DELETE /v1.23/volumes/tardis HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3364,7 +3367,7 @@ Instruct the driver to remove the network (`id`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
DELETE /v1.23/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
|
|
@ -290,6 +290,7 @@ Create a container
|
|||
"StopSignal": "SIGTERM",
|
||||
"HostConfig": {
|
||||
"Binds": ["/tmp:/tmp"],
|
||||
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||
"Links": ["redis3:redis"],
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
|
@ -404,6 +405,8 @@ Create a container
|
|||
_absolute_ path.
|
||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||
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
|
||||
in the form of `container_name:alias`.
|
||||
- **Memory** - Memory limit in bytes.
|
||||
|
@ -1411,7 +1414,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.24/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1524,7 +1527,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -2088,7 +2091,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.24/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2453,13 +2456,13 @@ Docker daemon report the following event:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.24/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
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
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
|
@ -2646,7 +2649,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.24/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2675,7 +2678,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.24/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2698,7 +2701,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.24/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
@ -3030,7 +3033,7 @@ Return low-level information on the volume `name`
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /volumes/tardis
|
||||
GET /v1.24/volumes/tardis
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3082,7 +3085,7 @@ Instruct the driver to remove the volume (`name`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /volumes/tardis HTTP/1.1
|
||||
DELETE /v1.24/volumes/tardis HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3414,7 +3417,7 @@ Instruct the driver to remove the network (`id`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
DELETE /v1.24/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3818,7 +3821,7 @@ Removes a plugin
|
|||
**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.
|
||||
|
@ -3912,7 +3915,7 @@ List nodes
|
|||
"MemoryBytes": 8272408576
|
||||
},
|
||||
"Engine": {
|
||||
"EngineVersion": "1.12.0-dev",
|
||||
"EngineVersion": "1.12.0",
|
||||
"Labels": {
|
||||
"foo": "bar",
|
||||
}
|
||||
|
@ -3965,7 +3968,7 @@ List nodes
|
|||
### Inspect a node
|
||||
|
||||
|
||||
`GET /nodes/<id>`
|
||||
`GET /nodes/(id or name)`
|
||||
|
||||
Return low-level information on the node `id`
|
||||
|
||||
|
@ -4004,7 +4007,7 @@ Return low-level information on the node `id`
|
|||
"MemoryBytes": 8272408576
|
||||
},
|
||||
"Engine": {
|
||||
"EngineVersion": "1.12.0-dev",
|
||||
"EngineVersion": "1.12.0",
|
||||
"Labels": {
|
||||
"foo": "bar",
|
||||
}
|
||||
|
@ -4047,13 +4050,13 @@ Return low-level information on the node `id`
|
|||
### 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**:
|
||||
|
||||
DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
|
||||
DELETE /v1.24/nodes/24ifsmvkjbyhk HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -4077,7 +4080,7 @@ Remove a node [`id`] from the swarm.
|
|||
|
||||
`POST /nodes/(id)/update`
|
||||
|
||||
Update the node `id`.
|
||||
Update a node.
|
||||
|
||||
The payload of the `POST` request is the new `NodeSpec` and
|
||||
overrides the current `NodeSpec` for the specified node.
|
||||
|
@ -4693,7 +4696,7 @@ Stop and remove the service `id`
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /services/16253994b7c4 HTTP/1.1
|
||||
DELETE /v1.24/services/16253994b7c4 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
|
|
@ -295,6 +295,7 @@ Create a container
|
|||
"StopTimeout": 10,
|
||||
"HostConfig": {
|
||||
"Binds": ["/tmp:/tmp"],
|
||||
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||
"Links": ["redis3:redis"],
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
|
@ -418,6 +419,8 @@ Create a container
|
|||
_absolute_ path.
|
||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||
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
|
||||
in the form of `container_name:alias`.
|
||||
- **Memory** - Memory limit in bytes.
|
||||
|
@ -1467,7 +1470,7 @@ Remove the container `id` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
||||
DELETE /v1.25/containers/16253994b7c4?v=1 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -1580,7 +1583,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
|||
|
||||
**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
|
||||
|
||||
{% raw %}
|
||||
|
@ -2272,7 +2275,7 @@ Remove the image `name` from the filesystem
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /images/test HTTP/1.1
|
||||
DELETE /v1.25/images/test HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -2610,7 +2613,7 @@ Display system-wide information
|
|||
"Name": "WIN-V0V70C0LU5P",
|
||||
"Labels": null,
|
||||
"ExperimentalBuild": false,
|
||||
"ServerVersion": "1.13.0-dev",
|
||||
"ServerVersion": "1.13.0",
|
||||
"ClusterStore": "",
|
||||
"ClusterAdvertise": "",
|
||||
"SecurityOptions": null,
|
||||
|
@ -2898,13 +2901,13 @@ Docker daemon report the following event:
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
GET /v1.25/events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
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
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
|
@ -3091,7 +3094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/ubuntu/get
|
||||
GET /v1.25/images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3120,7 +3123,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
GET /v1.25/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3143,7 +3146,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
|||
|
||||
**Example request**
|
||||
|
||||
POST /images/load
|
||||
POST /v1.25/images/load
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Tarball in body
|
||||
|
@ -3490,7 +3493,7 @@ Return low-level information on the volume `name`
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /volumes/tardis
|
||||
GET /v1.25/volumes/tardis
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3547,7 +3550,7 @@ Instruct the driver to remove the volume (`name`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /volumes/tardis HTTP/1.1
|
||||
DELETE /v1.25/volumes/tardis HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -3920,7 +3923,7 @@ Instruct the driver to remove the network (`id`).
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
DELETE /v1.25/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -4296,7 +4299,7 @@ Content-Type: application/json
|
|||
**Example request**:
|
||||
|
||||
|
||||
POST /plugins/tiborvass/no-remove/set
|
||||
POST /v1.25/plugins/tiborvass/no-remove/set
|
||||
Content-Type: application/json
|
||||
|
||||
["DEBUG=1"]
|
||||
|
@ -4375,7 +4378,7 @@ Removes a plugin
|
|||
**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.
|
||||
|
@ -4510,7 +4513,7 @@ List nodes
|
|||
"MemoryBytes": 8272408576
|
||||
},
|
||||
"Engine": {
|
||||
"EngineVersion": "1.12.0-dev",
|
||||
"EngineVersion": "1.13.0",
|
||||
"Labels": {
|
||||
"foo": "bar",
|
||||
}
|
||||
|
@ -4564,7 +4567,7 @@ List nodes
|
|||
### Inspect a node
|
||||
|
||||
|
||||
`GET /nodes/<id>`
|
||||
`GET /nodes/(id or name)`
|
||||
|
||||
Return low-level information on the node `id`
|
||||
|
||||
|
@ -4603,7 +4606,7 @@ Return low-level information on the node `id`
|
|||
"MemoryBytes": 8272408576
|
||||
},
|
||||
"Engine": {
|
||||
"EngineVersion": "1.12.0-dev",
|
||||
"EngineVersion": "1.13.0",
|
||||
"Labels": {
|
||||
"foo": "bar",
|
||||
}
|
||||
|
@ -4647,13 +4650,13 @@ Return low-level information on the node `id`
|
|||
### 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**:
|
||||
|
||||
DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
|
||||
DELETE /v1.25/nodes/24ifsmvkjbyhk HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -4675,9 +4678,9 @@ Remove a node [`id`] from the swarm.
|
|||
### 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
|
||||
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-Type: application/json
|
||||
Date: Thu, 01 Sep 2016 21:49:13 GMT
|
||||
Server: Docker/1.12.0 (linux)
|
||||
Server: Docker/1.13.0 (linux)
|
||||
|
||||
"7v2t30z9blmxuhnyo6s4cpenp"
|
||||
|
||||
|
@ -4821,7 +4824,7 @@ Initialize a new swarm. The body of the HTTP response includes the node ID.
|
|||
|
||||
- **200** – no error
|
||||
- **400** – bad parameter
|
||||
- **406** – node is already part of a swarm
|
||||
- **503** – node is already part of a swarm
|
||||
|
||||
JSON Parameters:
|
||||
|
||||
|
@ -4890,7 +4893,7 @@ Join an existing swarm
|
|||
|
||||
- **200** – no error
|
||||
- **400** – bad parameter
|
||||
- **406** – node is already part of a swarm
|
||||
- **503** – node is already part of a swarm
|
||||
|
||||
JSON Parameters:
|
||||
|
||||
|
@ -4928,7 +4931,7 @@ Leave a swarm
|
|||
**Status codes**:
|
||||
|
||||
- **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
|
||||
|
||||
|
@ -5024,7 +5027,7 @@ Update a swarm
|
|||
|
||||
- **200** – no error
|
||||
- **400** – bad parameter
|
||||
- **406** – node is not part of a swarm
|
||||
- **503** – node is not part of a swarm
|
||||
|
||||
JSON Parameters:
|
||||
|
||||
|
@ -5262,14 +5265,15 @@ image](#create-an-image) section for more details.
|
|||
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**:
|
||||
|
||||
- **201** – no error
|
||||
- **406** – server error or node is not part of a swarm
|
||||
- **409** – name conflicts with an existing object
|
||||
- **503** – server error or node is not part of a swarm
|
||||
|
||||
**JSON Parameters**:
|
||||
|
||||
|
@ -5367,7 +5371,7 @@ Stop and remove the service `id`
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /services/16253994b7c4 HTTP/1.1
|
||||
DELETE /v1.25/services/16253994b7c4 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -5628,6 +5632,16 @@ image](#create-an-image) section for more details.
|
|||
- **404** – no such service
|
||||
- **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 /services/(id or name)/logs`
|
||||
|
@ -5639,7 +5653,7 @@ Get `stdout` and `stderr` logs from the service ``id``
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /services/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10&since=1428990821 HTTP/1.1
|
||||
GET /v1.25/services/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10&since=1428990821 HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -5988,7 +6002,7 @@ List secrets
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /secrets HTTP/1.1
|
||||
GET /v1.25/secrets HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -6026,7 +6040,7 @@ Create a secret
|
|||
|
||||
**Example request**:
|
||||
|
||||
POST /secrets/create HTTP/1.1
|
||||
POST /v1.25/secrets/create HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
|
@ -6049,8 +6063,8 @@ Create a secret
|
|||
**Status codes**:
|
||||
|
||||
- **201** – no error
|
||||
- **406** – server error or node is not part of a swarm
|
||||
- **409** – name conflicts with an existing object
|
||||
- **503** – server error or node is not part of a swarm
|
||||
|
||||
**JSON Parameters**:
|
||||
|
||||
|
@ -6066,7 +6080,7 @@ Get details on the secret `id`
|
|||
|
||||
**Example request**:
|
||||
|
||||
GET /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||
GET /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
@ -6099,7 +6113,7 @@ Remove the secret `id` from the secret store
|
|||
|
||||
**Example request**:
|
||||
|
||||
DELETE /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||
DELETE /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ Options:
|
|||
- label=<key> or label=<key>=<value>
|
||||
- before=(<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
|
||||
--help Print usage
|
||||
--no-trunc Don't truncate output
|
||||
|
@ -229,6 +230,24 @@ Filtering with `since` would give:
|
|||
image1 latest eeae25ada2aa 4 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
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ available on the volume where `/var/lib/docker` is mounted.
|
|||
|
||||
## 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:
|
||||
|
||||
$ docker -D info
|
||||
|
@ -56,49 +56,72 @@ storage driver and a node that is part of a 2-node swarm:
|
|||
Paused: 1
|
||||
Stopped: 10
|
||||
Images: 52
|
||||
Server Version: 1.12.0-dev
|
||||
Storage Driver: overlay
|
||||
Server Version: 1.13.0
|
||||
Storage Driver: overlay2
|
||||
Backing Filesystem: extfs
|
||||
Supports d_type: true
|
||||
Native Overlay Diff: false
|
||||
Logging Driver: json-file
|
||||
Cgroup Driver: cgroupfs
|
||||
Plugins:
|
||||
Volume: local
|
||||
Network: bridge null host overlay
|
||||
Swarm:
|
||||
NodeID: 0gac67oclbxq7
|
||||
Network: bridge host macvlan null overlay
|
||||
Swarm: active
|
||||
NodeID: rdjq45w1op418waxlairloqbm
|
||||
Is Manager: true
|
||||
Managers: 2
|
||||
ClusterID: te8kdyw33n36fqiz74bfjeixd
|
||||
Managers: 1
|
||||
Nodes: 2
|
||||
Runtimes: default
|
||||
Default Runtime: default
|
||||
Security Options: apparmor seccomp
|
||||
Kernel Version: 4.4.0-21-generic
|
||||
Operating System: Ubuntu 16.04 LTS
|
||||
Orchestration:
|
||||
Task History Retention Limit: 5
|
||||
Raft:
|
||||
Snapshot Interval: 10000
|
||||
Number of Old Snapshots to Retain: 0
|
||||
Heartbeat Tick: 1
|
||||
Election Tick: 3
|
||||
Dispatcher:
|
||||
Heartbeat Period: 5 seconds
|
||||
CA Configuration:
|
||||
Expiry Duration: 3 months
|
||||
Node Address: 172.16.66.128 172.16.66.129
|
||||
Manager Addresses:
|
||||
172.16.66.128:2477
|
||||
Runtimes: runc
|
||||
Default Runtime: runc
|
||||
Init Binary: docker-init
|
||||
containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
|
||||
runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
||||
init version: N/A (expected: v0.13.0)
|
||||
Security Options:
|
||||
apparmor
|
||||
seccomp
|
||||
Profile: default
|
||||
Kernel Version: 4.4.0-31-generic
|
||||
Operating System: Ubuntu 16.04.1 LTS
|
||||
OSType: linux
|
||||
Architecture: x86_64
|
||||
CPUs: 24
|
||||
Total Memory: 62.86 GiB
|
||||
Name: docker
|
||||
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
|
||||
CPUs: 2
|
||||
Total Memory: 1.937 GiB
|
||||
Name: ubuntu
|
||||
ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
|
||||
Docker Root Dir: /var/lib/docker
|
||||
Debug mode (client): true
|
||||
Debug mode (server): true
|
||||
File Descriptors: 59
|
||||
Goroutines: 159
|
||||
System Time: 2016-04-26T10:04:06.14689342-04:00
|
||||
Debug Mode (client): true
|
||||
Debug Mode (server): true
|
||||
File Descriptors: 30
|
||||
Goroutines: 123
|
||||
System Time: 2016-11-12T17:24:37.955404361-08:00
|
||||
EventsListeners: 0
|
||||
Http Proxy: http://test:test@localhost:8080
|
||||
Https Proxy: https://test:test@localhost:8080
|
||||
Http Proxy: http://proxy.example.com:80/
|
||||
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
||||
Username: svendowideit
|
||||
Registry: https://index.docker.io/v1/
|
||||
WARNING: No swap limit support
|
||||
Labels:
|
||||
storage=ssd
|
||||
staging=true
|
||||
Insecure registries:
|
||||
myinsecurehost:5000
|
||||
Experimental: false
|
||||
Insecure Registries:
|
||||
127.0.0.0/8
|
||||
Live Restore Enabled: false
|
||||
|
||||
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
|
||||
Stopped: 1
|
||||
Images: 17
|
||||
Server Version: 1.13.0-dev
|
||||
Server Version: 1.13.0
|
||||
Storage Driver: windowsfilter
|
||||
Windows:
|
||||
Logging Driver: json-file
|
||||
|
|
|
@ -21,9 +21,10 @@ Usage: docker stats [OPTIONS] [CONTAINER...]
|
|||
Display a live stream of container(s) resource usage statistics
|
||||
|
||||
Options:
|
||||
-a, --all Show all containers (default shows just running)
|
||||
--help Print usage
|
||||
--no-stream Disable streaming stats and only pull the first result
|
||||
-a, --all Show all containers (default shows just running)
|
||||
--format string Pretty-print images using a Go template
|
||||
--help Print usage
|
||||
--no-stream Disable streaming stats and only pull the first result
|
||||
```
|
||||
|
||||
The `docker stats` command returns a live data stream for running containers. To limit data to one or more specific containers, specify a list of container names or ids separated by a space. You can specify a stopped container but stopped containers do not return any data.
|
||||
|
@ -77,7 +78,9 @@ Valid placeholders for the Go template are listed below:
|
|||
|
||||
Placeholder | Description
|
||||
------------ | --------------------------------------------
|
||||
`.Container` | Container name or ID
|
||||
`.Container` | Container name or ID (user input)
|
||||
`.Name` | Container name
|
||||
`.ID` | Container ID
|
||||
`.CPUPerc` | CPU percentage
|
||||
`.MemUsage` | Memory usage
|
||||
`.NetIO` | Network IO
|
||||
|
|
|
@ -16,9 +16,9 @@ keywords: "tag, name, image"
|
|||
# tag
|
||||
|
||||
```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:
|
||||
--help Print usage
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
|
||||
RUNC_COMMIT=ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
||||
CONTAINERD_COMMIT=8517738ba4b82aff5662c97ca4627e7e4d03b531
|
||||
TINI_COMMIT=v0.13.0
|
||||
RUNC_COMMIT=51371867a01c467f08af739783b8beafc15
|
||||
CONTAINERD_COMMIT=03e5862ec0d8d3b3f750e19fca3ee367e13c090e
|
||||
TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
|
||||
LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
|
||||
VNDR_COMMIT=f56bd4504b4fad07a357913687fb652ee54bb3b0
|
||||
|
|
|
@ -8,7 +8,8 @@ swagger generate model -f api/swagger.yaml \
|
|||
-n ImageSummary \
|
||||
-n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \
|
||||
-n ErrorResponse \
|
||||
-n IdResponse
|
||||
-n IdResponse \
|
||||
-n ServiceUpdateResponse
|
||||
|
||||
swagger generate operation -f api/swagger.yaml \
|
||||
-t api -a types -m types -C api/swagger-gen.yaml \
|
||||
|
|
|
@ -4,7 +4,7 @@ export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||
source "${SCRIPTDIR}/.validate"
|
||||
|
||||
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
|
||||
|
||||
errors=()
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"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) {
|
||||
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 {
|
||||
logrus.Debugf("Error reading embedded tar: %v", err)
|
||||
return nil, err
|
||||
|
|
|
@ -315,8 +315,10 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
|
|||
}
|
||||
os.Symlink(relPath, layerPath)
|
||||
} else {
|
||||
|
||||
tarFile, err := os.Create(layerPath)
|
||||
// Use system.CreateSequential rather than os.Create. This ensures sequential
|
||||
// file access on Windows to avoid eating into MM standby list.
|
||||
// On Linux, this equates to a regular os.Create.
|
||||
tarFile, err := system.CreateSequential(layerPath)
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
|
|
|
@ -495,7 +495,7 @@ func (d *Daemon) SockRequest(method, endpoint string, data interface{}) (int, []
|
|||
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.
|
||||
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())
|
||||
|
|
|
@ -1865,6 +1865,10 @@ func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) {
|
|||
func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
|
||||
testRequires(c, DaemonIsWindows)
|
||||
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 := `
|
||||
FROM busybox
|
||||
# No trailing slash on COPY/ADD
|
||||
|
@ -1874,8 +1878,8 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
|
|||
WORKDIR /wc2
|
||||
ADD wc2 c:/wc2
|
||||
WORKDIR c:/
|
||||
RUN sh -c "[ $(cat c:/wc1/wc1) = 'hellowc1' ]"
|
||||
RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]"
|
||||
RUN sh -c "[ $(cat c:/wc1) = 'hellowc1' ]"
|
||||
RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"
|
||||
|
||||
# Trailing slash on COPY/ADD, Windows-style path.
|
||||
WORKDIR /wd1
|
||||
|
@ -7172,31 +7176,6 @@ 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) {
|
||||
testRequires(c, ExperimentalDaemon)
|
||||
dockerFile := `
|
||||
|
@ -7287,3 +7266,20 @@ func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
|
|||
_, err := buildImage("testopaquedirectory", dockerFile, false)
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
|
|||
stdout, stderr, _, err = runCommandWithStdoutStderr(cmd)
|
||||
c.Assert(err, checker.NotNil)
|
||||
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 {
|
||||
|
|
|
@ -1072,3 +1072,19 @@ func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ type remote struct {
|
|||
func New(stateDir string, options ...RemoteOption) (_ Remote, err error) {
|
||||
defer func() {
|
||||
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{
|
||||
|
|
|
@ -39,7 +39,7 @@ available on the volume where `/var/lib/docker` is mounted.
|
|||
|
||||
## 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:
|
||||
|
||||
$ docker -D info
|
||||
|
@ -48,49 +48,74 @@ storage driver:
|
|||
Paused: 1
|
||||
Stopped: 10
|
||||
Images: 52
|
||||
Server Version: 1.12.0-dev
|
||||
Storage Driver: overlay
|
||||
Server Version: 1.13.0
|
||||
Storage Driver: overlay2
|
||||
Backing Filesystem: extfs
|
||||
Supports d_type: true
|
||||
Native Overlay Diff: false
|
||||
Logging Driver: json-file
|
||||
Cgroup Driver: cgroupfs
|
||||
Plugins:
|
||||
Volume: local
|
||||
Network: bridge null host overlay
|
||||
Swarm:
|
||||
NodeID: 0gac67oclbxq7
|
||||
IsManager: YES
|
||||
Managers: 2
|
||||
Network: bridge host macvlan null overlay
|
||||
Swarm: active
|
||||
NodeID: rdjq45w1op418waxlairloqbm
|
||||
Is Manager: true
|
||||
ClusterID: te8kdyw33n36fqiz74bfjeixd
|
||||
Managers: 1
|
||||
Nodes: 2
|
||||
Runtimes: default
|
||||
Default Runtime: default
|
||||
Security Options: apparmor seccomp
|
||||
Kernel Version: 4.4.0-21-generic
|
||||
Operating System: Ubuntu 16.04 LTS
|
||||
Orchestration:
|
||||
Task History Retention Limit: 5
|
||||
Raft:
|
||||
Snapshot Interval: 10000
|
||||
Number of Old Snapshots to Retain: 0
|
||||
Heartbeat Tick: 1
|
||||
Election Tick: 3
|
||||
Dispatcher:
|
||||
Heartbeat Period: 5 seconds
|
||||
CA Configuration:
|
||||
Expiry Duration: 3 months
|
||||
Node Address: 172.16.66.128 172.16.66.129
|
||||
Manager Addresses:
|
||||
172.16.66.128:2477
|
||||
Runtimes: runc
|
||||
Default Runtime: runc
|
||||
Init Binary: docker-init
|
||||
containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
|
||||
runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
||||
init version: N/A (expected: v0.13.0)
|
||||
Security Options:
|
||||
apparmor
|
||||
seccomp
|
||||
Profile: default
|
||||
Kernel Version: 4.4.0-31-generic
|
||||
Operating System: Ubuntu 16.04.1 LTS
|
||||
OSType: linux
|
||||
Architecture: x86_64
|
||||
CPUs: 24
|
||||
Total Memory: 62.86 GiB
|
||||
Name: docker
|
||||
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
|
||||
CPUs: 2
|
||||
Total Memory: 1.937 GiB
|
||||
Name: ubuntu
|
||||
ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
|
||||
Docker Root Dir: /var/lib/docker
|
||||
Debug mode (client): true
|
||||
Debug mode (server): true
|
||||
File Descriptors: 59
|
||||
Goroutines: 159
|
||||
System Time: 2016-04-26T10:04:06.14689342-04:00
|
||||
Debug Mode (client): true
|
||||
Debug Mode (server): true
|
||||
File Descriptors: 30
|
||||
Goroutines: 123
|
||||
System Time: 2016-11-12T17:24:37.955404361-08:00
|
||||
EventsListeners: 0
|
||||
Http Proxy: http://test:test@localhost:8080
|
||||
Https Proxy: https://test:test@localhost:8080
|
||||
Http Proxy: http://proxy.example.com:80/
|
||||
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
||||
Username: svendowideit
|
||||
Registry: https://index.docker.io/v1/
|
||||
WARNING: No swap limit support
|
||||
Labels:
|
||||
storage=ssd
|
||||
staging=true
|
||||
Insecure registries:
|
||||
myinsecurehost:5000
|
||||
Experimental: false
|
||||
Insecure Registries:
|
||||
127.0.0.0/8
|
||||
Live Restore Enabled: false
|
||||
|
||||
|
||||
|
||||
The global `-D` option tells all `docker` commands to output debug information.
|
||||
|
||||
|
|
|
@ -907,7 +907,7 @@ should fix the problem.
|
|||
## Mapping Ports for External Usage
|
||||
|
||||
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:
|
||||
|
||||
# docker run -p 8080:80 -d -i -t fedora/httpd
|
||||
|
|
|
@ -30,6 +30,8 @@ Display a live stream of one or more containers' resource usage statistics
|
|||
Pretty-print containers statistics using a Go template.
|
||||
Valid placeholders:
|
||||
.Container - Container name or ID.
|
||||
.Name - Container name.
|
||||
.ID - Container ID.
|
||||
.CPUPerc - CPU percentage.
|
||||
.MemUsage - Memory usage.
|
||||
.NetIO - Network IO.
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
% Docker Community
|
||||
% JUNE 2014
|
||||
# NAME
|
||||
docker-tag - Tag an image into a repository
|
||||
docker-tag - Create a tag `TARGET_IMAGE` that refers to `SOURCE_IMAGE`
|
||||
|
||||
# SYNOPSIS
|
||||
**docker tag**
|
||||
[**--help**]
|
||||
NAME[:TAG] NAME[:TAG]
|
||||
SOURCE_NAME[:TAG] TARGET_NAME[:TAG]
|
||||
|
||||
# DESCRIPTION
|
||||
Assigns a new alias to an image in a registry. An alias refers to the
|
||||
|
|
|
@ -16,6 +16,7 @@ const (
|
|||
portOptMode = "mode"
|
||||
)
|
||||
|
||||
// PortOpt represents a port config in swarm mode.
|
||||
type PortOpt struct {
|
||||
ports []swarm.PortConfig
|
||||
}
|
||||
|
|
|
@ -374,7 +374,10 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
|||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -412,8 +415,10 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
|||
}
|
||||
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
// Source is regular file
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
// Source is regular file. We use system.OpenFileSequential to use sequential
|
||||
// file access to avoid depleting the standby list on Windows.
|
||||
// On Linux, this equates to a regular os.OpenFile
|
||||
file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
package devicemapper
|
||||
|
||||
// LibraryDeferredRemovalsupport is not supported when statically linked.
|
||||
// LibraryDeferredRemovalSupport is not supported when statically linked.
|
||||
const LibraryDeferredRemovalSupport = false
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
|
|
|
@ -86,7 +86,7 @@ func ParseAdvertise(advertise string) (string, error) {
|
|||
break
|
||||
}
|
||||
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)
|
||||
|
|
|
@ -155,18 +155,18 @@ func (r *multiReadSeeker) Read(b []byte) (int, error) {
|
|||
r.pos = &pos{0, 0}
|
||||
}
|
||||
|
||||
bCap := int64(cap(b))
|
||||
bLen := int64(len(b))
|
||||
buf := bytes.NewBuffer(nil)
|
||||
var rdr io.ReadSeeker
|
||||
|
||||
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 {
|
||||
return -1, err
|
||||
}
|
||||
bCap -= readBytes
|
||||
bLen -= readBytes
|
||||
|
||||
if bCap == 0 {
|
||||
if bLen == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ioutils
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,3 +23,32 @@ func MkdirAll(path string, perm os.FileMode) error {
|
|||
func IsAbs(path string) bool {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func mkdirWithACL(name string) error {
|
|||
sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
|
||||
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
||||
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.InheritHandle = 1
|
||||
|
@ -106,12 +106,12 @@ func mkdirWithACL(name string) error {
|
|||
|
||||
namep, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return &os.PathError{"mkdir", name, err}
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||
}
|
||||
|
||||
e := syscall.CreateDirectory(namep, &sa)
|
||||
if e != nil {
|
||||
return &os.PathError{"mkdir", name, e}
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -131,3 +131,106 @@ func IsAbs(path string) bool {
|
|||
}
|
||||
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
Loading…
Reference in a new issue