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
|
IsValidationError() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHTTPErrorStatusCode retrieve status code from error message
|
// GetHTTPErrorStatusCode retrieves status code from error message
|
||||||
func GetHTTPErrorStatusCode(err error) int {
|
func GetHTTPErrorStatusCode(err error) int {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
|
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
|
||||||
|
@ -49,20 +49,23 @@ func GetHTTPErrorStatusCode(err error) int {
|
||||||
// If we need to differentiate between different possible error types,
|
// If we need to differentiate between different possible error types,
|
||||||
// we should create appropriate error types that implement the httpStatusError interface.
|
// we should create appropriate error types that implement the httpStatusError interface.
|
||||||
errStr := strings.ToLower(errMsg)
|
errStr := strings.ToLower(errMsg)
|
||||||
for keyword, status := range map[string]int{
|
for _, status := range []struct {
|
||||||
"not found": http.StatusNotFound,
|
keyword string
|
||||||
"no such": http.StatusNotFound,
|
code int
|
||||||
"bad parameter": http.StatusBadRequest,
|
}{
|
||||||
"no command": http.StatusBadRequest,
|
{"not found", http.StatusNotFound},
|
||||||
"conflict": http.StatusConflict,
|
{"no such", http.StatusNotFound},
|
||||||
"impossible": http.StatusNotAcceptable,
|
{"bad parameter", http.StatusBadRequest},
|
||||||
"wrong login/password": http.StatusUnauthorized,
|
{"no command", http.StatusBadRequest},
|
||||||
"unauthorized": http.StatusUnauthorized,
|
{"conflict", http.StatusConflict},
|
||||||
"hasn't been activated": http.StatusForbidden,
|
{"impossible", http.StatusNotAcceptable},
|
||||||
"this node": http.StatusNotAcceptable,
|
{"wrong login/password", http.StatusUnauthorized},
|
||||||
|
{"unauthorized", http.StatusUnauthorized},
|
||||||
|
{"hasn't been activated", http.StatusForbidden},
|
||||||
|
{"this node", http.StatusServiceUnavailable},
|
||||||
} {
|
} {
|
||||||
if strings.Contains(errStr, keyword) {
|
if strings.Contains(errStr, status.keyword) {
|
||||||
statusCode = status
|
statusCode = status.code
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ func (r *checkpointRouter) Routes() []router.Route {
|
||||||
|
|
||||||
func (r *checkpointRouter) initRoutes() {
|
func (r *checkpointRouter) initRoutes() {
|
||||||
r.routes = []router.Route{
|
r.routes = []router.Route{
|
||||||
router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints),
|
router.Experimental(router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints)),
|
||||||
router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint),
|
router.Experimental(router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint)),
|
||||||
router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint),
|
router.Experimental(router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
UnlockSwarm(req types.UnlockRequest) error
|
||||||
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
|
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
|
||||||
GetService(string) (types.Service, error)
|
GetService(string) (types.Service, error)
|
||||||
CreateService(types.ServiceSpec, string) (string, error)
|
CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
|
||||||
UpdateService(string, uint64, types.ServiceSpec, string, string) error
|
UpdateService(string, uint64, types.ServiceSpec, string, string) (*basictypes.ServiceUpdateResponse, error)
|
||||||
RemoveService(string) error
|
RemoveService(string) error
|
||||||
ServiceLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
|
ServiceLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
|
||||||
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
|
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package swarm
|
package swarm
|
||||||
|
|
||||||
import (
|
import "github.com/docker/docker/api/server/router"
|
||||||
"github.com/docker/docker/api/server/router"
|
|
||||||
"github.com/docker/docker/daemon"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swarmRouter is a router to talk with the build controller
|
// swarmRouter is a router to talk with the build controller
|
||||||
type swarmRouter struct {
|
type swarmRouter struct {
|
||||||
|
@ -12,14 +9,11 @@ type swarmRouter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouter initializes a new build router
|
// NewRouter initializes a new build router
|
||||||
func NewRouter(d *daemon.Daemon, b Backend) router.Router {
|
func NewRouter(b Backend) router.Router {
|
||||||
r := &swarmRouter{
|
r := &swarmRouter{
|
||||||
backend: b,
|
backend: b,
|
||||||
}
|
}
|
||||||
r.initRoutes()
|
r.initRoutes()
|
||||||
if d.HasExperimental() {
|
|
||||||
r.addExperimentalRoutes()
|
|
||||||
}
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,12 +22,6 @@ func (sr *swarmRouter) Routes() []router.Route {
|
||||||
return sr.routes
|
return sr.routes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr *swarmRouter) addExperimentalRoutes() {
|
|
||||||
sr.routes = append(sr.routes,
|
|
||||||
router.Cancellable(router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr *swarmRouter) initRoutes() {
|
func (sr *swarmRouter) initRoutes() {
|
||||||
sr.routes = []router.Route{
|
sr.routes = []router.Route{
|
||||||
router.NewPostRoute("/swarm/init", sr.initCluster),
|
router.NewPostRoute("/swarm/init", sr.initCluster),
|
||||||
|
@ -48,6 +36,7 @@ func (sr *swarmRouter) initRoutes() {
|
||||||
router.NewPostRoute("/services/create", sr.createService),
|
router.NewPostRoute("/services/create", sr.createService),
|
||||||
router.NewPostRoute("/services/{id}/update", sr.updateService),
|
router.NewPostRoute("/services/{id}/update", sr.updateService),
|
||||||
router.NewDeleteRoute("/services/{id}", sr.removeService),
|
router.NewDeleteRoute("/services/{id}", sr.removeService),
|
||||||
|
router.Experimental(router.Cancellable(router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs))),
|
||||||
router.NewGetRoute("/nodes", sr.getNodes),
|
router.NewGetRoute("/nodes", sr.getNodes),
|
||||||
router.NewGetRoute("/nodes/{id}", sr.getNode),
|
router.NewGetRoute("/nodes/{id}", sr.getNode),
|
||||||
router.NewDeleteRoute("/nodes/{id}", sr.removeNode),
|
router.NewDeleteRoute("/nodes/{id}", sr.removeNode),
|
||||||
|
|
|
@ -166,15 +166,13 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
|
||||||
// Get returns "" if the header does not exist
|
// Get returns "" if the header does not exist
|
||||||
encodedAuth := r.Header.Get("X-Registry-Auth")
|
encodedAuth := r.Header.Get("X-Registry-Auth")
|
||||||
|
|
||||||
id, err := sr.backend.CreateService(service, encodedAuth)
|
resp, err := sr.backend.CreateService(service, encodedAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error creating service %s: %v", service.Name, err)
|
logrus.Errorf("Error creating service %s: %v", service.Name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ServiceCreateResponse{
|
return httputils.WriteJSON(w, http.StatusCreated, resp)
|
||||||
ID: id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -194,11 +192,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
|
||||||
|
|
||||||
registryAuthFrom := r.URL.Query().Get("registryAuthFrom")
|
registryAuthFrom := r.URL.Query().Get("registryAuthFrom")
|
||||||
|
|
||||||
if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth, registryAuthFrom); err != nil {
|
resp, err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth, registryAuthFrom)
|
||||||
|
if err != nil {
|
||||||
logrus.Errorf("Error updating service %s: %v", vars["id"], err)
|
logrus.Errorf("Error updating service %s: %v", vars["id"], err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return httputils.WriteJSON(w, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
|
|
@ -102,7 +102,7 @@ func (s *Server) serveAPI() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPServer contains an instance of http server and the listener.
|
// HTTPServer contains an instance of http server and the listener.
|
||||||
// srv *http.Server, contains configuration to create a http server and a mux router with all api end points.
|
// srv *http.Server, contains configuration to create an http server and a mux router with all api end points.
|
||||||
// l net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
|
// l net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
|
||||||
type HTTPServer struct {
|
type HTTPServer struct {
|
||||||
srv *http.Server
|
srv *http.Server
|
||||||
|
|
|
@ -2218,6 +2218,16 @@ definitions:
|
||||||
Deleted:
|
Deleted:
|
||||||
description: "The image ID of an image that was deleted"
|
description: "The image ID of an image that was deleted"
|
||||||
type: "string"
|
type: "string"
|
||||||
|
ServiceUpdateResponse:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
Warnings:
|
||||||
|
description: "Optional warning messages"
|
||||||
|
type: "array"
|
||||||
|
items:
|
||||||
|
type: "string"
|
||||||
|
example:
|
||||||
|
Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
|
||||||
ContainerSummary:
|
ContainerSummary:
|
||||||
type: "array"
|
type: "array"
|
||||||
items:
|
items:
|
||||||
|
@ -6731,14 +6741,14 @@ paths:
|
||||||
description: "bad parameter"
|
description: "bad parameter"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
406:
|
|
||||||
description: "node is already part of a swarm"
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ErrorResponse"
|
|
||||||
500:
|
500:
|
||||||
description: "server error"
|
description: "server error"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
|
503:
|
||||||
|
description: "node is already part of a swarm"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ErrorResponse"
|
||||||
parameters:
|
parameters:
|
||||||
- name: "body"
|
- name: "body"
|
||||||
in: "body"
|
in: "body"
|
||||||
|
@ -6773,14 +6783,14 @@ paths:
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "no error"
|
description: "no error"
|
||||||
406:
|
|
||||||
description: "node is not part of a swarm"
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ErrorResponse"
|
|
||||||
500:
|
500:
|
||||||
description: "server error"
|
description: "server error"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
|
503:
|
||||||
|
description: "node is not part of a swarm"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ErrorResponse"
|
||||||
parameters:
|
parameters:
|
||||||
- name: "force"
|
- name: "force"
|
||||||
description: "Force leave swarm, even if this is the last manager or that it will break the cluster."
|
description: "Force leave swarm, even if this is the last manager or that it will break the cluster."
|
||||||
|
@ -6800,14 +6810,14 @@ paths:
|
||||||
description: "bad parameter"
|
description: "bad parameter"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
406:
|
|
||||||
description: "node is not part of a swarm"
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ErrorResponse"
|
|
||||||
500:
|
500:
|
||||||
description: "server error"
|
description: "server error"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
|
503:
|
||||||
|
description: "node is not part of a swarm"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ErrorResponse"
|
||||||
parameters:
|
parameters:
|
||||||
- name: "body"
|
- name: "body"
|
||||||
in: "body"
|
in: "body"
|
||||||
|
@ -6875,12 +6885,12 @@ paths:
|
||||||
ID:
|
ID:
|
||||||
description: "The ID of the created service."
|
description: "The ID of the created service."
|
||||||
type: "string"
|
type: "string"
|
||||||
|
Warning:
|
||||||
|
description: "Optional warning message"
|
||||||
|
type: "string"
|
||||||
example:
|
example:
|
||||||
ID: "ak7w3gjqoa3kuz8xcpnyy0pvl"
|
ID: "ak7w3gjqoa3kuz8xcpnyy0pvl"
|
||||||
406:
|
Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
|
||||||
description: "server error or node is not part of a swarm"
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ErrorResponse"
|
|
||||||
409:
|
409:
|
||||||
description: "name conflicts with an existing service"
|
description: "name conflicts with an existing service"
|
||||||
schema:
|
schema:
|
||||||
|
@ -6889,6 +6899,10 @@ paths:
|
||||||
description: "server error"
|
description: "server error"
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ErrorResponse"
|
$ref: "#/definitions/ErrorResponse"
|
||||||
|
503:
|
||||||
|
description: "server error or node is not part of a swarm"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ErrorResponse"
|
||||||
parameters:
|
parameters:
|
||||||
- name: "body"
|
- name: "body"
|
||||||
in: "body"
|
in: "body"
|
||||||
|
@ -6998,10 +7012,14 @@ paths:
|
||||||
/services/{id}/update:
|
/services/{id}/update:
|
||||||
post:
|
post:
|
||||||
summary: "Update a service"
|
summary: "Update a service"
|
||||||
operationId: "PostServicesUpdate"
|
operationId: "ServiceUpdate"
|
||||||
|
consumes: ["application/json"]
|
||||||
|
produces: ["application/json"]
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "no error"
|
description: "no error"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ImageDeleteResponse"
|
||||||
404:
|
404:
|
||||||
description: "no such service"
|
description: "no such service"
|
||||||
schema:
|
schema:
|
||||||
|
@ -7065,8 +7083,7 @@ paths:
|
||||||
description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
|
description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
|
||||||
type: "string"
|
type: "string"
|
||||||
|
|
||||||
tags:
|
tags: [Service]
|
||||||
- "Services"
|
|
||||||
/tasks:
|
/tasks:
|
||||||
get:
|
get:
|
||||||
summary: "List tasks"
|
summary: "List tasks"
|
||||||
|
|
|
@ -285,10 +285,12 @@ type ServiceCreateOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceCreateResponse contains the information returned to a client
|
// ServiceCreateResponse contains the information returned to a client
|
||||||
// on the creation of a new service.
|
// on the creation of a new service.
|
||||||
type ServiceCreateResponse struct {
|
type ServiceCreateResponse struct {
|
||||||
// ID is the ID of the created service.
|
// ID is the ID of the created service.
|
||||||
ID string
|
ID string
|
||||||
|
// Warnings is a set of non-fatal warning messages to pass on to the user.
|
||||||
|
Warnings []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values for RegistryAuthFrom in ServiceUpdateOptions
|
// Values for RegistryAuthFrom in ServiceUpdateOptions
|
||||||
|
|
12
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 {
|
type UpdateConfig struct {
|
||||||
// Maximum number of tasks to be updated in one iteration.
|
// Maximum number of tasks to be updated in one iteration.
|
||||||
// 0 means unlimited parallelism.
|
// 0 means unlimited parallelism.
|
||||||
Parallelism uint64 `json:",omitempty"`
|
Parallelism uint64
|
||||||
|
|
||||||
// Amount of time between updates.
|
// Amount of time between updates.
|
||||||
Delay time.Duration `json:",omitempty"`
|
Delay time.Duration `json:",omitempty"`
|
||||||
|
|
|
@ -3,6 +3,7 @@ package v1p24
|
||||||
|
|
||||||
import "github.com/docker/docker/api/types"
|
import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
// Info is a backcompatibility struct for the API 1.24
|
||||||
type Info struct {
|
type Info struct {
|
||||||
*types.InfoBase
|
*types.InfoBase
|
||||||
ExecutionDriver string
|
ExecutionDriver string
|
||||||
|
|
|
@ -11,7 +11,7 @@ type Volume struct {
|
||||||
// Required: true
|
// Required: true
|
||||||
Driver string `json:"Driver"`
|
Driver string `json:"Driver"`
|
||||||
|
|
||||||
// A mapping of abitrary key/value data set on this volume.
|
// User-defined key/value metadata.
|
||||||
// Required: true
|
// Required: true
|
||||||
Labels map[string]string `json:"Labels"`
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ type VolumesCreateBody struct {
|
||||||
// Required: true
|
// Required: true
|
||||||
DriverOpts map[string]string `json:"DriverOpts"`
|
DriverOpts map[string]string `json:"DriverOpts"`
|
||||||
|
|
||||||
// A mapping of arbitrary key/value data to set on the volume.
|
// User-defined key/value metadata.
|
||||||
// Required: true
|
// Required: true
|
||||||
Labels map[string]string `json:"Labels"`
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import "fmt"
|
||||||
// a command not supported on the platform.
|
// a command not supported on the platform.
|
||||||
func platformSupports(command string) error {
|
func platformSupports(command string) error {
|
||||||
switch command {
|
switch command {
|
||||||
case "user", "stopsignal":
|
case "stopsignal":
|
||||||
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -12,11 +12,8 @@ func NewCheckpointCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "checkpoint",
|
Use: "checkpoint",
|
||||||
Short: "Manage checkpoints",
|
Short: "Manage checkpoints",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newCreateCommand(dockerCli),
|
newCreateCommand(dockerCli),
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
dopts "github.com/docker/docker/opts"
|
dopts "github.com/docker/docker/opts"
|
||||||
"github.com/docker/go-connections/sockets"
|
"github.com/docker/go-connections/sockets"
|
||||||
"github.com/docker/go-connections/tlsconfig"
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,6 +74,13 @@ func (cli *DockerCli) In() *InStream {
|
||||||
return cli.in
|
return cli.in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowHelp shows the command help.
|
||||||
|
func (cli *DockerCli) ShowHelp(cmd *cobra.Command, args []string) error {
|
||||||
|
cmd.SetOutput(cli.err)
|
||||||
|
cmd.HelpFunc()(cmd, args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigFile returns the ConfigFile
|
// ConfigFile returns the ConfigFile
|
||||||
func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
||||||
return cli.configFile
|
return cli.configFile
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "container",
|
Use: "container",
|
||||||
Short: "Manage containers",
|
Short: "Manage containers",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
NewAttachCommand(dockerCli),
|
NewAttachCommand(dockerCli),
|
||||||
|
|
|
@ -59,20 +59,18 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
type preProcessor struct {
|
// listOptionsProcessor is used to set any container list options which may only
|
||||||
types.Container
|
// be embedded in the format template.
|
||||||
opts *types.ContainerListOptions
|
// This is passed directly into tmpl.Execute in order to allow the preprocessor
|
||||||
|
// to set any list options that were not provided by flags (e.g. `.Size`).
|
||||||
|
// It is using a `map[string]bool` so that unknown fields passed into the
|
||||||
|
// template format do not cause errors. These errors will get picked up when
|
||||||
|
// running through the actual template processor.
|
||||||
|
type listOptionsProcessor map[string]bool
|
||||||
|
|
||||||
// Fields that need to exist so the template doesn't error out
|
// Size sets the size of the map when called by a template execution.
|
||||||
// These are needed since they are available on the final object but are not
|
func (o listOptionsProcessor) Size() bool {
|
||||||
// fields in types.Container
|
o["size"] = true
|
||||||
// TODO(cpuguy83): this seems rather broken
|
|
||||||
Networks, CreatedAt, RunningFor bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size sets the size option when called by a template execution.
|
|
||||||
func (p *preProcessor) Size() bool {
|
|
||||||
p.opts.Size = true
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,20 +86,20 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||||
options.Limit = 1
|
options.Limit = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently only used with Size, so we can determine if the user
|
|
||||||
// put {{.Size}} in their format.
|
|
||||||
pre := &preProcessor{opts: options}
|
|
||||||
tmpl, err := templates.Parse(opts.format)
|
tmpl, err := templates.Parse(opts.format)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optionsProcessor := listOptionsProcessor{}
|
||||||
// This shouldn't error out but swallowing the error makes it harder
|
// This shouldn't error out but swallowing the error makes it harder
|
||||||
// to track down if preProcessor issues come up. Ref #24696
|
// to track down if preProcessor issues come up. Ref #24696
|
||||||
if err := tmpl.Execute(ioutil.Discard, pre); err != nil {
|
if err := tmpl.Execute(ioutil.Discard, optionsProcessor); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// At the moment all we need is to capture .Size for preprocessor
|
||||||
|
options.Size = opts.size || optionsProcessor["size"]
|
||||||
|
|
||||||
return options, nil
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,57 @@ func TestBuildContainerListOptions(t *testing.T) {
|
||||||
expectedLimit: 1,
|
expectedLimit: 1,
|
||||||
expectedFilters: make(map[string]string),
|
expectedFilters: make(map[string]string),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
psOpts: &psOptions{
|
||||||
|
all: true,
|
||||||
|
size: false,
|
||||||
|
last: 5,
|
||||||
|
filter: filters,
|
||||||
|
// With .Size, size should be true
|
||||||
|
format: "{{.Size}}",
|
||||||
|
},
|
||||||
|
expectedAll: true,
|
||||||
|
expectedSize: true,
|
||||||
|
expectedLimit: 5,
|
||||||
|
expectedFilters: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
psOpts: &psOptions{
|
||||||
|
all: true,
|
||||||
|
size: false,
|
||||||
|
last: 5,
|
||||||
|
filter: filters,
|
||||||
|
// With .Size, size should be true
|
||||||
|
format: "{{.Size}} {{.CreatedAt}} {{.Networks}}",
|
||||||
|
},
|
||||||
|
expectedAll: true,
|
||||||
|
expectedSize: true,
|
||||||
|
expectedLimit: 5,
|
||||||
|
expectedFilters: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
psOpts: &psOptions{
|
||||||
|
all: true,
|
||||||
|
size: false,
|
||||||
|
last: 5,
|
||||||
|
filter: filters,
|
||||||
|
// Without .Size, size should be false
|
||||||
|
format: "{{.CreatedAt}} {{.Networks}}",
|
||||||
|
},
|
||||||
|
expectedAll: true,
|
||||||
|
expectedSize: false,
|
||||||
|
expectedLimit: 5,
|
||||||
|
expectedFilters: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range contexts {
|
for _, c := range contexts {
|
||||||
|
|
|
@ -372,7 +372,7 @@ func TestContainerContextWriteJSONField(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerBackCompat(t *testing.T) {
|
func TestContainerBackCompat(t *testing.T) {
|
||||||
containers := []types.Container{types.Container{ID: "brewhaha"}}
|
containers := []types.Container{{ID: "brewhaha"}}
|
||||||
cases := []string{
|
cases := []string{
|
||||||
"ID",
|
"ID",
|
||||||
"Names",
|
"Names",
|
||||||
|
@ -390,7 +390,7 @@ func TestContainerBackCompat(t *testing.T) {
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
|
ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
|
||||||
if err := ContainerWrite(ctx, containers); err != nil {
|
if err := ContainerWrite(ctx, containers); err != nil {
|
||||||
t.Log("could not render template for field '%s': %v", c, err)
|
t.Logf("could not render template for field '%s': %v", c, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
|
|
@ -263,6 +263,9 @@ func (ctx *serviceInspectContext) HasResources() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *serviceInspectContext) HasResourceReservations() bool {
|
func (ctx *serviceInspectContext) HasResourceReservations() bool {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Reservations == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
|
return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +284,9 @@ func (ctx *serviceInspectContext) ResourceReservationMemory() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *serviceInspectContext) HasResourceLimits() bool {
|
func (ctx *serviceInspectContext) HasResourceLimits() bool {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Limits == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
|
return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "image",
|
Use: "image",
|
||||||
Short: "Manage images",
|
Short: "Manage images",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
NewBuildCommand(dockerCli),
|
NewBuildCommand(dockerCli),
|
||||||
|
|
|
@ -3,13 +3,13 @@ package image
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +43,9 @@ func runLoad(dockerCli *command.DockerCli, opts loadOptions) error {
|
||||||
|
|
||||||
var input io.Reader = dockerCli.In()
|
var input io.Reader = dockerCli.In()
|
||||||
if opts.input != "" {
|
if opts.input != "" {
|
||||||
file, err := os.Open(opts.input)
|
// We use system.OpenSequential to use sequential file access on Windows, avoiding
|
||||||
|
// depleting the standby list un-necessarily. On Linux, this equates to a regular os.Open.
|
||||||
|
file, err := system.OpenSequential(opts.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
var opts tagOptions
|
var opts tagOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "tag IMAGE[:TAG] IMAGE[:TAG]",
|
Use: "tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]",
|
||||||
Short: "Tag an image into a repository",
|
Short: "Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE",
|
||||||
Args: cli.ExactArgs(2),
|
Args: cli.ExactArgs(2),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
opts.image = args[0]
|
opts.image = args[0]
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewNetworkCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "network",
|
Use: "network",
|
||||||
Short: "Manage networks",
|
Short: "Manage networks",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newConnectCommand(dockerCli),
|
newConnectCommand(dockerCli),
|
||||||
|
|
|
@ -14,10 +14,7 @@ func NewNodeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "node",
|
Use: "node",
|
||||||
Short: "Manage Swarm nodes",
|
Short: "Manage Swarm nodes",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newDemoteCommand(dockerCli),
|
newDemoteCommand(dockerCli),
|
||||||
|
|
|
@ -12,10 +12,7 @@ func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "plugin",
|
Use: "plugin",
|
||||||
Short: "Manage plugins",
|
Short: "Manage plugins",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package secret
|
package secret
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
|
@ -15,9 +13,7 @@ func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "secret",
|
Use: "secret",
|
||||||
Short: "Manage Docker secrets",
|
Short: "Manage Docker secrets",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newSecretListCommand(dockerCli),
|
newSecretListCommand(dockerCli),
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "service",
|
Use: "service",
|
||||||
Short: "Manage services",
|
Short: "Manage services",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newCreateCommand(dockerCli),
|
newCreateCommand(dockerCli),
|
||||||
|
|
|
@ -90,6 +90,10 @@ func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, warning := range response.Warnings {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
|
fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,11 +82,15 @@ func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint6
|
||||||
|
|
||||||
serviceMode.Replicated.Replicas = &scale
|
serviceMode.Replicated.Replicas = &scale
|
||||||
|
|
||||||
err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
response, err := client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, warning := range response.Warnings {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
|
fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,11 +133,15 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str
|
||||||
updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
|
updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
|
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, warning := range response.Warnings {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
|
fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,8 @@ func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "stack",
|
Use: "stack",
|
||||||
Short: "Manage Docker stacks",
|
Short: "Manage Docker stacks",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newDeployCommand(dockerCli),
|
newDeployCommand(dockerCli),
|
||||||
|
|
|
@ -408,15 +408,20 @@ func deployServices(
|
||||||
if sendAuth {
|
if sendAuth {
|
||||||
updateOpts.EncodedRegistryAuth = encodedAuth
|
updateOpts.EncodedRegistryAuth = encodedAuth
|
||||||
}
|
}
|
||||||
if err := apiClient.ServiceUpdate(
|
response, err := apiClient.ServiceUpdate(
|
||||||
ctx,
|
ctx,
|
||||||
service.ID,
|
service.ID,
|
||||||
service.Version,
|
service.Version,
|
||||||
serviceSpec,
|
serviceSpec,
|
||||||
updateOpts,
|
updateOpts,
|
||||||
); err != nil {
|
)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, warning := range response.Warnings {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "Creating service %s\n", name)
|
fmt.Fprintf(out, "Creating service %s\n", name)
|
||||||
|
|
||||||
|
@ -526,7 +531,7 @@ func convertService(
|
||||||
func convertExtraHosts(extraHosts map[string]string) []string {
|
func convertExtraHosts(extraHosts map[string]string) []string {
|
||||||
hosts := []string{}
|
hosts := []string{}
|
||||||
for host, ip := range extraHosts {
|
for host, ip := range extraHosts {
|
||||||
hosts = append(hosts, fmt.Sprintf("%s %s", host, ip))
|
hosts = append(hosts, fmt.Sprintf("%s %s", ip, host))
|
||||||
}
|
}
|
||||||
return hosts
|
return hosts
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func printTable(out io.Writer, stacks []*stack) {
|
||||||
|
|
||||||
type stack struct {
|
type stack struct {
|
||||||
// Name is the name of the stack
|
// Name is the name of the stack
|
||||||
Name string
|
Name string
|
||||||
// Services is the number of the services
|
// Services is the number of the services
|
||||||
Services int
|
Services int
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewSwarmCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "swarm",
|
Use: "swarm",
|
||||||
Short: "Manage Swarm",
|
Short: "Manage Swarm",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newInitCommand(dockerCli),
|
newInitCommand(dockerCli),
|
||||||
|
|
|
@ -13,10 +13,7 @@ func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Use: "system",
|
Use: "system",
|
||||||
Short: "Manage Docker",
|
Short: "Manage Docker",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
NewEventsCommand(dockerCli),
|
NewEventsCommand(dockerCli),
|
||||||
|
|
|
@ -14,10 +14,7 @@ func NewVolumeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
Short: "Manage volumes",
|
Short: "Manage volumes",
|
||||||
Long: volumeDescription,
|
Long: volumeDescription,
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: dockerCli.ShowHelp,
|
||||||
cmd.SetOutput(dockerCli.Err())
|
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newCreateCommand(dockerCli),
|
newCreateCommand(dockerCli),
|
||||||
|
|
|
@ -124,7 +124,7 @@ type ServiceAPIClient interface {
|
||||||
ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
|
ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
|
||||||
ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
|
ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
|
||||||
ServiceRemove(ctx context.Context, serviceID string) error
|
ServiceRemove(ctx context.Context, serviceID string) error
|
||||||
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) error
|
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||||
ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error)
|
ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error)
|
||||||
TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
|
TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
|
||||||
TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
|
TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PluginInstall installs a plugin
|
// PluginInstall installs a plugin
|
||||||
func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error {
|
func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (err error) {
|
||||||
// FIXME(vdemeester) name is a ref, we might want to parse/validate it here.
|
// FIXME(vdemeester) name is a ref, we might want to parse/validate it here.
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("name", name)
|
query.Set("name", name)
|
||||||
|
@ -27,6 +27,14 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
delResp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
|
||||||
|
ensureReaderClosed(delResp)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var privileges types.PluginPrivileges
|
var privileges types.PluginPrivileges
|
||||||
if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
|
if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
|
@ -40,8 +48,6 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !accept {
|
if !accept {
|
||||||
resp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return pluginPermissionDenied{name}
|
return pluginPermissionDenied{name}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceUpdate updates a Service.
|
// ServiceUpdate updates a Service.
|
||||||
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) error {
|
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||||
var (
|
var (
|
||||||
headers map[string][]string
|
headers map[string][]string
|
||||||
query = url.Values{}
|
query = url.Values{}
|
||||||
|
@ -28,7 +29,13 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||||
|
|
||||||
query.Set("version", strconv.FormatUint(version.Index, 10))
|
query.Set("version", strconv.FormatUint(version.Index, 10))
|
||||||
|
|
||||||
|
var response types.ServiceUpdateResponse
|
||||||
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.body).Decode(&response)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestServiceUpdateError(t *testing.T) {
|
||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
_, err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
t.Fatalf("expected a Server Error, got %v", err)
|
||||||
}
|
}
|
||||||
|
@ -64,12 +64,12 @@ func TestServiceUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
StatusCode: http.StatusOK,
|
StatusCode: http.StatusOK,
|
||||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))),
|
Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))),
|
||||||
}, nil
|
}, nil
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ServiceUpdate(context.Background(), "service_id", updateCase.swarmVersion, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
_, err := client.ServiceUpdate(context.Background(), "service_id", updateCase.swarmVersion, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -34,27 +35,37 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
showVersion()
|
showVersion()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cmd.SetOutput(dockerCli.Err())
|
return dockerCli.ShowHelp(cmd, args)
|
||||||
cmd.HelpFunc()(cmd, args)
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// daemon command is special, we redirect directly to another binary
|
||||||
|
if cmd.Name() == "daemon" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// flags must be the top-level command flags, not cmd.Flags()
|
// flags must be the top-level command flags, not cmd.Flags()
|
||||||
opts.Common.SetDefaultOptions(flags)
|
opts.Common.SetDefaultOptions(flags)
|
||||||
dockerPreRun(opts)
|
dockerPreRun(opts)
|
||||||
return dockerCli.Initialize(opts)
|
if err := dockerCli.Initialize(opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cli.SetupRootCommand(cmd)
|
cli.SetupRootCommand(cmd)
|
||||||
|
|
||||||
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
|
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
|
||||||
if dockerCli.Client() == nil {
|
if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed.
|
||||||
// flags must be the top-level command flags, not cmd.Flags()
|
// flags must be the top-level command flags, not cmd.Flags()
|
||||||
opts.Common.SetDefaultOptions(flags)
|
opts.Common.SetDefaultOptions(flags)
|
||||||
dockerPreRun(opts)
|
dockerPreRun(opts)
|
||||||
dockerCli.Initialize(opts)
|
dockerCli.Initialize(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
|
||||||
|
ccmd.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
|
hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
|
||||||
|
|
||||||
if err := ccmd.Help(); err != nil {
|
if err := ccmd.Help(); err != nil {
|
||||||
|
@ -79,7 +90,7 @@ func noArgs(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0])
|
"docker: '%s' is not a docker command.\nSee 'docker --help'", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -153,3 +164,17 @@ func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error {
|
||||||
|
if !hasExperimental {
|
||||||
|
if _, ok := cmd.Tags["experimental"]; ok {
|
||||||
|
return errors.New("only supported with experimental daemon")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmdVersion, ok := cmd.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
|
||||||
|
return fmt.Errorf("only supported with daemon version >= %s", cmdVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/docker/docker/api/server/middleware"
|
"github.com/docker/docker/api/server/middleware"
|
||||||
"github.com/docker/docker/api/server/router"
|
"github.com/docker/docker/api/server/router"
|
||||||
"github.com/docker/docker/api/server/router/build"
|
"github.com/docker/docker/api/server/router/build"
|
||||||
|
checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
|
||||||
"github.com/docker/docker/api/server/router/container"
|
"github.com/docker/docker/api/server/router/container"
|
||||||
"github.com/docker/docker/api/server/router/image"
|
"github.com/docker/docker/api/server/router/image"
|
||||||
"github.com/docker/docker/api/server/router/network"
|
"github.com/docker/docker/api/server/router/network"
|
||||||
|
@ -461,25 +462,32 @@ func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) {
|
||||||
func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
|
func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
|
||||||
decoder := runconfig.ContainerDecoder{}
|
decoder := runconfig.ContainerDecoder{}
|
||||||
|
|
||||||
routers := []router.Router{}
|
routers := []router.Router{
|
||||||
|
// we need to add the checkpoint router before the container router or the DELETE gets masked
|
||||||
// we need to add the checkpoint router before the container router or the DELETE gets masked
|
checkpointrouter.NewRouter(d, decoder),
|
||||||
routers = addExperimentalRouters(routers, d, decoder)
|
|
||||||
|
|
||||||
routers = append(routers, []router.Router{
|
|
||||||
container.NewRouter(d, decoder),
|
container.NewRouter(d, decoder),
|
||||||
image.NewRouter(d, decoder),
|
image.NewRouter(d, decoder),
|
||||||
systemrouter.NewRouter(d, c),
|
systemrouter.NewRouter(d, c),
|
||||||
volume.NewRouter(d),
|
volume.NewRouter(d),
|
||||||
build.NewRouter(dockerfile.NewBuildManager(d)),
|
build.NewRouter(dockerfile.NewBuildManager(d)),
|
||||||
swarmrouter.NewRouter(d, c),
|
swarmrouter.NewRouter(c),
|
||||||
pluginrouter.NewRouter(plugin.GetManager()),
|
pluginrouter.NewRouter(plugin.GetManager()),
|
||||||
}...)
|
}
|
||||||
|
|
||||||
if d.NetworkControllerEnabled() {
|
if d.NetworkControllerEnabled() {
|
||||||
routers = append(routers, network.NewRouter(d, c))
|
routers = append(routers, network.NewRouter(d, c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.HasExperimental() {
|
||||||
|
for _, r := range routers {
|
||||||
|
for _, route := range r.Routes() {
|
||||||
|
if experimental, ok := route.(router.ExperimentalRoute); ok {
|
||||||
|
experimental.Enable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.InitRouter(utils.IsDebugEnabled(), routers...)
|
s.InitRouter(utils.IsDebugEnabled(), routers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
func (s *Health) CloseMonitorChannel() {
|
||||||
if s.stop != nil {
|
if s.stop != nil {
|
||||||
logrus.Debug("CloseMonitorChannel: waiting for probe to stop")
|
logrus.Debug("CloseMonitorChannel: waiting for probe to stop")
|
||||||
// This channel does not buffer. Once the write succeeds, the monitor
|
close(s.stop)
|
||||||
// has read the stop request and will not make any further updates
|
|
||||||
// to c.State.Health.
|
|
||||||
s.stop <- struct{}{}
|
|
||||||
s.stop = nil
|
s.stop = nil
|
||||||
logrus.Debug("CloseMonitorChannel done")
|
logrus.Debug("CloseMonitorChannel done")
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,6 @@ fi
|
||||||
|
|
||||||
flags=(
|
flags=(
|
||||||
NAMESPACES {NET,PID,IPC,UTS}_NS
|
NAMESPACES {NET,PID,IPC,UTS}_NS
|
||||||
DEVPTS_MULTIPLE_INSTANCES
|
|
||||||
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
|
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
|
||||||
KEYS
|
KEYS
|
||||||
VETH BRIDGE BRIDGE_NETFILTER
|
VETH BRIDGE BRIDGE_NETFILTER
|
||||||
|
@ -200,6 +199,10 @@ flags=(
|
||||||
POSIX_MQUEUE
|
POSIX_MQUEUE
|
||||||
)
|
)
|
||||||
check_flags "${flags[@]}"
|
check_flags "${flags[@]}"
|
||||||
|
if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -lt 8 ]; then
|
||||||
|
check_flags DEVPTS_MULTIPLE_INSTANCES
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo 'Optional Features:'
|
echo 'Optional Features:'
|
||||||
|
|
|
@ -1961,7 +1961,7 @@ __docker_swarm_subcommand() {
|
||||||
__docker_system_commands() {
|
__docker_system_commands() {
|
||||||
local -a _docker_system_subcommands
|
local -a _docker_system_subcommands
|
||||||
_docker_system_subcommands=(
|
_docker_system_subcommands=(
|
||||||
"df:Show docker disk usage"
|
"df:Show docker filesystem usage"
|
||||||
"events:Get real time events from the server"
|
"events:Get real time events from the server"
|
||||||
"info:Display system-wide information"
|
"info:Display system-wide information"
|
||||||
"prune:Remove unused data"
|
"prune:Remove unused data"
|
||||||
|
@ -1978,7 +1978,9 @@ __docker_system_subcommand() {
|
||||||
|
|
||||||
case "$words[1]" in
|
case "$words[1]" in
|
||||||
(df)
|
(df)
|
||||||
# @TODO
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help -v --verbose)"{-v,--verbose}"[Show detailed information on space usage]" && ret=0
|
||||||
;;
|
;;
|
||||||
(events)
|
(events)
|
||||||
_arguments $(__docker_arguments) \
|
_arguments $(__docker_arguments) \
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
distreference "github.com/docker/distribution/reference"
|
||||||
apierrors "github.com/docker/docker/api/errors"
|
apierrors "github.com/docker/docker/api/errors"
|
||||||
apitypes "github.com/docker/docker/api/types"
|
apitypes "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
|
@ -69,7 +70,7 @@ var ErrSwarmJoinTimeoutReached = fmt.Errorf("Timeout was reached before node was
|
||||||
// ErrSwarmLocked is returned if the swarm is encrypted and needs a key to unlock it.
|
// ErrSwarmLocked is returned if the swarm is encrypted and needs a key to unlock it.
|
||||||
var ErrSwarmLocked = fmt.Errorf("Swarm is encrypted and needs to be unlocked before it can be used. Please use \"docker swarm unlock\" to unlock it.")
|
var ErrSwarmLocked = fmt.Errorf("Swarm is encrypted and needs to be unlocked before it can be used. Please use \"docker swarm unlock\" to unlock it.")
|
||||||
|
|
||||||
// ErrSwarmCertificatesExipred is returned if docker was not started for the whole validity period and they had no chance to renew automatically.
|
// ErrSwarmCertificatesExpired is returned if docker was not started for the whole validity period and they had no chance to renew automatically.
|
||||||
var ErrSwarmCertificatesExpired = errors.New("Swarm certificates have expired. To replace them, leave the swarm and join again.")
|
var ErrSwarmCertificatesExpired = errors.New("Swarm certificates have expired. To replace them, leave the swarm and join again.")
|
||||||
|
|
||||||
// NetworkSubnetsProvider exposes functions for retrieving the subnets
|
// NetworkSubnetsProvider exposes functions for retrieving the subnets
|
||||||
|
@ -587,6 +588,15 @@ func (c *Cluster) GetUnlockKey() (string, error) {
|
||||||
|
|
||||||
// UnlockSwarm provides a key to decrypt data that is encrypted at rest.
|
// UnlockSwarm provides a key to decrypt data that is encrypted at rest.
|
||||||
func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
|
func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
|
||||||
|
c.RLock()
|
||||||
|
if !c.isActiveManager() {
|
||||||
|
if err := c.errNoManager(); err != ErrSwarmLocked {
|
||||||
|
c.RUnlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.RUnlock()
|
||||||
|
|
||||||
key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
|
key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1008,16 +1018,25 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
|
||||||
|
|
||||||
// imageWithDigestString takes an image such as name or name:tag
|
// imageWithDigestString takes an image such as name or name:tag
|
||||||
// and returns the image pinned to a digest, such as name@sha256:34234...
|
// and returns the image pinned to a digest, such as name@sha256:34234...
|
||||||
|
// Due to the difference between the docker/docker/reference, and the
|
||||||
|
// docker/distribution/reference packages, we're parsing the image twice.
|
||||||
|
// As the two packages converge, this function should be simplified.
|
||||||
|
// TODO(nishanttotla): After the packages converge, the function must
|
||||||
|
// convert distreference.Named -> distreference.Canonical, and the logic simplified.
|
||||||
func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
|
func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
|
||||||
ref, err := reference.ParseNamed(image)
|
ref, err := distreference.ParseNamed(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// only query registry if not a canonical reference (i.e. with digest)
|
// only query registry if not a canonical reference (i.e. with digest)
|
||||||
if _, ok := ref.(reference.Canonical); !ok {
|
if _, ok := ref.(distreference.Canonical); !ok {
|
||||||
ref = reference.WithDefaultTag(ref)
|
// create a docker/docker/reference Named object because GetRepository needs it
|
||||||
|
dockerRef, err := reference.ParseNamed(image)
|
||||||
namedTaggedRef, ok := ref.(reference.NamedTagged)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dockerRef = reference.WithDefaultTag(dockerRef)
|
||||||
|
namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("unable to cast image to NamedTagged reference object")
|
return "", fmt.Errorf("unable to cast image to NamedTagged reference object")
|
||||||
}
|
}
|
||||||
|
@ -1031,28 +1050,23 @@ func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authC
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(nishanttotla): Currently, the service would lose the tag while calling WithDigest
|
namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
|
||||||
// To prevent this, we create the image string manually, which is a bad idea in general
|
if err != nil {
|
||||||
// This will be fixed when https://github.com/docker/distribution/pull/2044 is vendored
|
return "", err
|
||||||
// namedDigestedRef, err := reference.WithDigest(ref, dscrptr.Digest)
|
}
|
||||||
// if err != nil {
|
return namedDigestedRef.String(), nil
|
||||||
// return "", err
|
|
||||||
// }
|
|
||||||
// return namedDigestedRef.String(), nil
|
|
||||||
return image + "@" + dscrptr.Digest.String(), nil
|
|
||||||
} else {
|
|
||||||
// reference already contains a digest, so just return it
|
|
||||||
return ref.String(), nil
|
|
||||||
}
|
}
|
||||||
|
// reference already contains a digest, so just return it
|
||||||
|
return ref.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateService creates a new service in a managed swarm cluster.
|
// CreateService creates a new service in a managed swarm cluster.
|
||||||
func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string, error) {
|
func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apitypes.ServiceCreateResponse, error) {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
defer c.RUnlock()
|
defer c.RUnlock()
|
||||||
|
|
||||||
if !c.isActiveManager() {
|
if !c.isActiveManager() {
|
||||||
return "", c.errNoManager()
|
return nil, c.errNoManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := c.getRequestContext()
|
ctx, cancel := c.getRequestContext()
|
||||||
|
@ -1060,17 +1074,17 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
|
||||||
|
|
||||||
err := c.populateNetworkID(ctx, c.client, &s)
|
err := c.populateNetworkID(ctx, c.client, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceSpec, err := convert.ServiceSpecToGRPC(s)
|
serviceSpec, err := convert.ServiceSpecToGRPC(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctnr := serviceSpec.Task.GetContainer()
|
ctnr := serviceSpec.Task.GetContainer()
|
||||||
if ctnr == nil {
|
if ctnr == nil {
|
||||||
return "", fmt.Errorf("service does not use container tasks")
|
return nil, fmt.Errorf("service does not use container tasks")
|
||||||
}
|
}
|
||||||
|
|
||||||
if encodedAuth != "" {
|
if encodedAuth != "" {
|
||||||
|
@ -1084,11 +1098,15 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
|
||||||
logrus.Warnf("invalid authconfig: %v", err)
|
logrus.Warnf("invalid authconfig: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp := &apitypes.ServiceCreateResponse{}
|
||||||
|
|
||||||
// pin image by digest
|
// pin image by digest
|
||||||
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
||||||
digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig)
|
digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error())
|
logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error())
|
||||||
|
resp.Warnings = append(resp.Warnings, fmt.Sprintf("unable to pin image %s to digest: %s", ctnr.Image, err.Error()))
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("pinning image %s by digest: %s", ctnr.Image, digestImage)
|
logrus.Debugf("pinning image %s by digest: %s", ctnr.Image, digestImage)
|
||||||
ctnr.Image = digestImage
|
ctnr.Image = digestImage
|
||||||
|
@ -1097,10 +1115,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string
|
||||||
|
|
||||||
r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
|
r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.Service.ID, nil
|
resp.ID = r.Service.ID
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetService returns a service based on an ID or name.
|
// GetService returns a service based on an ID or name.
|
||||||
|
@ -1123,12 +1142,12 @@ func (c *Cluster) GetService(input string) (types.Service, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateService updates existing service to match new properties.
|
// UpdateService updates existing service to match new properties.
|
||||||
func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, encodedAuth string, registryAuthFrom string) error {
|
func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, encodedAuth string, registryAuthFrom string) (*apitypes.ServiceUpdateResponse, error) {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
defer c.RUnlock()
|
defer c.RUnlock()
|
||||||
|
|
||||||
if !c.isActiveManager() {
|
if !c.isActiveManager() {
|
||||||
return c.errNoManager()
|
return nil, c.errNoManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := c.getRequestContext()
|
ctx, cancel := c.getRequestContext()
|
||||||
|
@ -1136,22 +1155,22 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
|
||||||
|
|
||||||
err := c.populateNetworkID(ctx, c.client, &spec)
|
err := c.populateNetworkID(ctx, c.client, &spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceSpec, err := convert.ServiceSpecToGRPC(spec)
|
serviceSpec, err := convert.ServiceSpecToGRPC(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
currentService, err := getService(ctx, c.client, serviceIDOrName)
|
currentService, err := getService(ctx, c.client, serviceIDOrName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newCtnr := serviceSpec.Task.GetContainer()
|
newCtnr := serviceSpec.Task.GetContainer()
|
||||||
if newCtnr == nil {
|
if newCtnr == nil {
|
||||||
return fmt.Errorf("service does not use container tasks")
|
return nil, fmt.Errorf("service does not use container tasks")
|
||||||
}
|
}
|
||||||
|
|
||||||
if encodedAuth != "" {
|
if encodedAuth != "" {
|
||||||
|
@ -1165,14 +1184,14 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
|
||||||
ctnr = currentService.Spec.Task.GetContainer()
|
ctnr = currentService.Spec.Task.GetContainer()
|
||||||
case apitypes.RegistryAuthFromPreviousSpec:
|
case apitypes.RegistryAuthFromPreviousSpec:
|
||||||
if currentService.PreviousSpec == nil {
|
if currentService.PreviousSpec == nil {
|
||||||
return fmt.Errorf("service does not have a previous spec")
|
return nil, fmt.Errorf("service does not have a previous spec")
|
||||||
}
|
}
|
||||||
ctnr = currentService.PreviousSpec.Task.GetContainer()
|
ctnr = currentService.PreviousSpec.Task.GetContainer()
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported registryAuthFromValue")
|
return nil, fmt.Errorf("unsupported registryAuthFromValue")
|
||||||
}
|
}
|
||||||
if ctnr == nil {
|
if ctnr == nil {
|
||||||
return fmt.Errorf("service does not use container tasks")
|
return nil, fmt.Errorf("service does not use container tasks")
|
||||||
}
|
}
|
||||||
newCtnr.PullOptions = ctnr.PullOptions
|
newCtnr.PullOptions = ctnr.PullOptions
|
||||||
// update encodedAuth so it can be used to pin image by digest
|
// update encodedAuth so it can be used to pin image by digest
|
||||||
|
@ -1188,11 +1207,15 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
|
||||||
logrus.Warnf("invalid authconfig: %v", err)
|
logrus.Warnf("invalid authconfig: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp := &apitypes.ServiceUpdateResponse{}
|
||||||
|
|
||||||
// pin image by digest
|
// pin image by digest
|
||||||
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
|
||||||
digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig)
|
digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error())
|
logrus.Warnf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error())
|
||||||
|
resp.Warnings = append(resp.Warnings, fmt.Sprintf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error()))
|
||||||
} else if newCtnr.Image != digestImage {
|
} else if newCtnr.Image != digestImage {
|
||||||
logrus.Debugf("pinning image %s by digest: %s", newCtnr.Image, digestImage)
|
logrus.Debugf("pinning image %s by digest: %s", newCtnr.Image, digestImage)
|
||||||
newCtnr.Image = digestImage
|
newCtnr.Image = digestImage
|
||||||
|
@ -1209,7 +1232,8 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return err
|
|
||||||
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveService removes a service from a managed swarm cluster.
|
// RemoveService removes a service from a managed swarm cluster.
|
||||||
|
@ -1369,7 +1393,7 @@ func (c *Cluster) GetNode(input string) (types.Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateNode updates existing nodes properties.
|
// UpdateNode updates existing nodes properties.
|
||||||
func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec) error {
|
func (c *Cluster) UpdateNode(input string, version uint64, spec types.NodeSpec) error {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
defer c.RUnlock()
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
@ -1385,10 +1409,15 @@ func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec)
|
||||||
ctx, cancel := c.getRequestContext()
|
ctx, cancel := c.getRequestContext()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
currentNode, err := getNode(ctx, c.client, input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
_, err = c.client.UpdateNode(
|
_, err = c.client.UpdateNode(
|
||||||
ctx,
|
ctx,
|
||||||
&swarmapi.UpdateNodeRequest{
|
&swarmapi.UpdateNodeRequest{
|
||||||
NodeID: nodeID,
|
NodeID: currentNode.ID,
|
||||||
Spec: &nodeSpec,
|
Spec: &nodeSpec,
|
||||||
NodeVersion: &swarmapi.Version{
|
NodeVersion: &swarmapi.Version{
|
||||||
Index: version,
|
Index: version,
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// merge merges two Config, the image container configuration (defaults values),
|
// merge merges two Config, the image container configuration (defaults values),
|
||||||
|
@ -32,9 +31,6 @@ func merge(userConf, imageConf *containertypes.Config) error {
|
||||||
if len(userConf.ExposedPorts) == 0 {
|
if len(userConf.ExposedPorts) == 0 {
|
||||||
userConf.ExposedPorts = imageConf.ExposedPorts
|
userConf.ExposedPorts = imageConf.ExposedPorts
|
||||||
} else if imageConf.ExposedPorts != nil {
|
} else if imageConf.ExposedPorts != nil {
|
||||||
if userConf.ExposedPorts == nil {
|
|
||||||
userConf.ExposedPorts = make(nat.PortSet)
|
|
||||||
}
|
|
||||||
for port := range imageConf.ExposedPorts {
|
for port := range imageConf.ExposedPorts {
|
||||||
if _, exists := userConf.ExposedPorts[port]; !exists {
|
if _, exists := userConf.ExposedPorts[port]; !exists {
|
||||||
userConf.ExposedPorts[port] = struct{}{}
|
userConf.ExposedPorts[port] = struct{}{}
|
||||||
|
@ -244,8 +240,8 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes := map[string]string{
|
attributes := map[string]string{
|
||||||
"comment": c.Comment,
|
"comment": c.Comment,
|
||||||
"imageID": id.String(),
|
"imageID": id.String(),
|
||||||
"imageRef": imageRef,
|
"imageRef": imageRef,
|
||||||
}
|
}
|
||||||
daemon.LogContainerEventWithAttributes(container, "commit", attributes)
|
daemon.LogContainerEventWithAttributes(container, "commit", attributes)
|
||||||
|
|
|
@ -16,14 +16,6 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
|
||||||
hostConfig.Isolation = daemon.defaultIsolation
|
hostConfig.Isolation = daemon.defaultIsolation
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.Mount(container); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer daemon.Unmount(container)
|
|
||||||
if err := container.SetupWorkingDirectory(0, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for spec := range config.Volumes {
|
for spec := range config.Volumes {
|
||||||
|
|
||||||
mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
|
mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
|
||||||
|
|
|
@ -550,7 +550,12 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
||||||
driverName = config.GraphDriver
|
driverName = config.GraphDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.RegistryService = registryService
|
||||||
d.PluginStore = pluginstore.NewStore(config.Root)
|
d.PluginStore = pluginstore.NewStore(config.Root)
|
||||||
|
// Plugin system initialization should happen before restore. Do not change order.
|
||||||
|
if err := d.pluginInit(config, containerdRemote); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
|
d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
|
||||||
StorePath: config.Root,
|
StorePath: config.Root,
|
||||||
|
@ -649,7 +654,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
||||||
Type: config.LogConfig.Type,
|
Type: config.LogConfig.Type,
|
||||||
Config: config.LogConfig.Config,
|
Config: config.LogConfig.Config,
|
||||||
}
|
}
|
||||||
d.RegistryService = registryService
|
|
||||||
d.EventsService = eventsService
|
d.EventsService = eventsService
|
||||||
d.volumes = volStore
|
d.volumes = volStore
|
||||||
d.root = config.Root
|
d.root = config.Root
|
||||||
|
@ -668,11 +672,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin system initialization should happen before restore. Do not change order.
|
|
||||||
if err := d.pluginInit(config, containerdRemote); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.restore(); err != nil {
|
if err := d.restore(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,9 +112,7 @@ func getCPUResources(config containertypes.Resources) *specs.CPU {
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.NanoCPUs > 0 {
|
if config.NanoCPUs > 0 {
|
||||||
// Use the default setting of 100ms, as is specified in:
|
|
||||||
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
|
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
|
||||||
// cpu.cfs_period_us=100ms
|
|
||||||
period := uint64(100 * time.Millisecond / time.Microsecond)
|
period := uint64(100 * time.Millisecond / time.Microsecond)
|
||||||
quota := uint64(config.NanoCPUs) * period / 1e9
|
quota := uint64(config.NanoCPUs) * period / 1e9
|
||||||
cpu.Period = &period
|
cpu.Period = &period
|
||||||
|
@ -361,8 +359,15 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
|
||||||
if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
|
if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
|
||||||
return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
|
return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
|
||||||
}
|
}
|
||||||
|
// The highest precision we could get on Linux is 0.001, by setting
|
||||||
|
// cpu.cfs_period_us=1000ms
|
||||||
|
// cpu.cfs_quota=1ms
|
||||||
|
// See the following link for details:
|
||||||
|
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
|
||||||
|
// Here we don't set the lower limit and it is up to the underlying platform (e.g., Linux) to return an error.
|
||||||
|
// The error message is 0.01 so that this is consistent with Windows
|
||||||
if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
|
if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
|
||||||
return warnings, fmt.Errorf("Range of Nano CPUs is from 1 to %d", int64(sysinfo.NumCPU())*1e9)
|
return warnings, fmt.Errorf("Range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
if resources.CPUShares > 0 && !sysInfo.CPUShares {
|
if resources.CPUShares > 0 && !sysInfo.CPUShares {
|
||||||
|
|
|
@ -129,8 +129,10 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool
|
||||||
if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
|
if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
|
||||||
return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set")
|
return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set")
|
||||||
}
|
}
|
||||||
|
// The precision we could get is 0.01, because on Windows we have to convert to CPUPercent.
|
||||||
|
// We don't set the lower limit here and it is up to the underlying platform (e.g., Windows) to return an error.
|
||||||
if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
|
if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
|
||||||
return warnings, fmt.Errorf("range of Nano CPUs is from 1 to %d", int64(sysinfo.NumCPU())*1e9)
|
return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resources.BlkioDeviceReadBps) > 0 {
|
if len(resources.BlkioDeviceReadBps) > 0 {
|
||||||
|
|
|
@ -27,7 +27,36 @@ func (d *Daemon) dumpDaemon(dir string) (string, error) {
|
||||||
return "", errors.Wrap(err, "failed to open file to write the daemon datastructure dump")
|
return "", errors.Wrap(err, "failed to open file to write the daemon datastructure dump")
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
spew.Fdump(f, d) // Does not return an error
|
|
||||||
|
dump := struct {
|
||||||
|
containers interface{}
|
||||||
|
names interface{}
|
||||||
|
links interface{}
|
||||||
|
execs interface{}
|
||||||
|
volumes interface{}
|
||||||
|
images interface{}
|
||||||
|
layers interface{}
|
||||||
|
imageReferences interface{}
|
||||||
|
downloads interface{}
|
||||||
|
uploads interface{}
|
||||||
|
registry interface{}
|
||||||
|
plugins interface{}
|
||||||
|
}{
|
||||||
|
containers: d.containers,
|
||||||
|
execs: d.execCommands,
|
||||||
|
volumes: d.volumes,
|
||||||
|
images: d.imageStore,
|
||||||
|
layers: d.layerStore,
|
||||||
|
imageReferences: d.referenceStore,
|
||||||
|
downloads: d.downloadManager,
|
||||||
|
uploads: d.uploadManager,
|
||||||
|
registry: d.RegistryService,
|
||||||
|
plugins: d.PluginStore,
|
||||||
|
names: d.nameIndex,
|
||||||
|
links: d.linkIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
spew.Fdump(f, dump) // Does not return an error
|
||||||
f.Sync()
|
f.Sync()
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package graphdriver
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
)
|
)
|
||||||
|
@ -26,5 +27,5 @@ func lookupPlugin(name, home string, opts []string, pg plugingetter.PluginGetter
|
||||||
|
|
||||||
func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
|
func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
|
||||||
proxy := &graphDriverProxy{name, c}
|
proxy := &graphDriverProxy{name, c}
|
||||||
return proxy, proxy.Init(home, opts)
|
return proxy, proxy.Init(filepath.Join(home, name), opts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,18 +165,16 @@ func (d *Driver) Exists(id string) bool {
|
||||||
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
|
return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
|
||||||
} else {
|
|
||||||
return d.create(id, parent, "", false, nil)
|
|
||||||
}
|
}
|
||||||
|
return d.create(id, parent, "", false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new read-only layer with the given id.
|
// Create creates a new read-only layer with the given id.
|
||||||
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
|
return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
|
||||||
} else {
|
|
||||||
return d.create(id, parent, "", true, nil)
|
|
||||||
}
|
}
|
||||||
|
return d.create(id, parent, "", true, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
|
func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/strslice"
|
"github.com/docker/docker/api/types/strslice"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
|
@ -63,11 +64,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, container *container.Cont
|
||||||
|
|
||||||
cmdSlice := strslice.StrSlice(container.Config.Healthcheck.Test)[1:]
|
cmdSlice := strslice.StrSlice(container.Config.Healthcheck.Test)[1:]
|
||||||
if p.shell {
|
if p.shell {
|
||||||
if runtime.GOOS != "windows" {
|
cmdSlice = append(getShell(container.Config), cmdSlice...)
|
||||||
cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
|
|
||||||
} else {
|
|
||||||
cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice)
|
entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice)
|
||||||
execConfig := exec.NewConfig()
|
execConfig := exec.NewConfig()
|
||||||
|
@ -107,10 +104,17 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, container *container.Cont
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the container's Status.Health struct based on the latest probe's result.
|
// Update the container's Status.Health struct based on the latest probe's result.
|
||||||
func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult) {
|
func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult, done chan struct{}) {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
// probe may have been cancelled while waiting on lock. Ignore result then
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
retries := c.Config.Healthcheck.Retries
|
retries := c.Config.Healthcheck.Retries
|
||||||
if retries <= 0 {
|
if retries <= 0 {
|
||||||
retries = defaultProbeRetries
|
retries = defaultProbeRetries
|
||||||
|
@ -183,7 +187,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
|
||||||
cancelProbe()
|
cancelProbe()
|
||||||
return
|
return
|
||||||
case result := <-results:
|
case result := <-results:
|
||||||
handleProbeResult(d, c, result)
|
handleProbeResult(d, c, result, stop)
|
||||||
// Stop timeout
|
// Stop timeout
|
||||||
cancelProbe()
|
cancelProbe()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -193,7 +197,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
|
||||||
Output: fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout),
|
Output: fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout),
|
||||||
Start: startTime,
|
Start: startTime,
|
||||||
End: time.Now(),
|
End: time.Now(),
|
||||||
})
|
}, stop)
|
||||||
cancelProbe()
|
cancelProbe()
|
||||||
// Wait for probe to exit (it might take a while to respond to the TERM
|
// Wait for probe to exit (it might take a while to respond to the TERM
|
||||||
// signal and we don't want dying probes to pile up).
|
// signal and we don't want dying probes to pile up).
|
||||||
|
@ -325,3 +329,13 @@ func min(x, y int) int {
|
||||||
}
|
}
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getShell(config *containertypes.Config) []string {
|
||||||
|
if len(config.Shell) != 0 {
|
||||||
|
return config.Shell
|
||||||
|
}
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return []string{"/bin/sh", "-c"}
|
||||||
|
}
|
||||||
|
return []string{"cmd", "/S", "/C"}
|
||||||
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ func TestHealthStates(t *testing.T) {
|
||||||
Start: startTime,
|
Start: startTime,
|
||||||
End: startTime,
|
End: startTime,
|
||||||
ExitCode: exitCode,
|
ExitCode: exitCode,
|
||||||
})
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// starting -> failed -> success -> failed
|
// starting -> failed -> success -> failed
|
||||||
|
|
|
@ -106,6 +106,7 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRepository returns a repository from the registry.
|
||||||
func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagged, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagged, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
||||||
// get repository info
|
// get repository info
|
||||||
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FillPlatformInfo fills the platform related info.
|
||||||
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
||||||
v.MemoryLimit = sysInfo.MemoryLimit
|
v.MemoryLimit = sysInfo.MemoryLimit
|
||||||
v.SwapLimit = sysInfo.SwapLimit
|
v.SwapLimit = sysInfo.SwapLimit
|
||||||
|
|
|
@ -5,5 +5,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FillPlatformInfo fills the platform related info.
|
||||||
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
func (daemon *Daemon) FillPlatformInfo(v *types.InfoBase, sysInfo *sysinfo.SysInfo) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@ Config provides the base accessible fields for working with V0 plugin format
|
||||||
|
|
||||||
- **docker.volumedriver/1.0**
|
- **docker.volumedriver/1.0**
|
||||||
|
|
||||||
|
- **docker.authz/1.0**
|
||||||
|
|
||||||
- **`socket`** *string*
|
- **`socket`** *string*
|
||||||
|
|
||||||
socket is the name of the socket the engine should use to communicate with the plugins.
|
socket is the name of the socket the engine should use to communicate with the plugins.
|
||||||
|
|
|
@ -199,8 +199,8 @@ drwx------ 19 root root 4096 Aug 8 17:56 rootfs
|
||||||
The `rootfs` directory represents the root filesystem of the plugin. In this
|
The `rootfs` directory represents the root filesystem of the plugin. In this
|
||||||
example, it was created from a Dockerfile:
|
example, it was created from a Dockerfile:
|
||||||
|
|
||||||
>**Note:** The `/run/docker/plugins` directory is mandatory for docker to communicate with
|
>**Note:** The `/run/docker/plugins` directory is mandatory inside of the
|
||||||
the plugin.
|
plugin's filesystem for docker to communicate with the plugin.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/vieux/docker-volume-sshfs
|
$ git clone https://github.com/vieux/docker-volume-sshfs
|
||||||
|
|
|
@ -165,6 +165,7 @@ This section lists each version from latest to oldest. Each listing includes a
|
||||||
* `POST /containers/create` now takes `StopTimeout` field.
|
* `POST /containers/create` now takes `StopTimeout` field.
|
||||||
* `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
|
* `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
|
||||||
* `POST /services/(id or name)/update` now accepts a `ForceUpdate` parameter inside the `TaskTemplate`, which causes the service to be updated even if there are no changes which would ordinarily trigger an update.
|
* `POST /services/(id or name)/update` now accepts a `ForceUpdate` parameter inside the `TaskTemplate`, which causes the service to be updated even if there are no changes which would ordinarily trigger an update.
|
||||||
|
* `POST /services/create` and `POST /services/(id or name)/update` now return a `Warnings` array.
|
||||||
* `GET /networks/(name)` now returns field `Created` in response to show network created time.
|
* `GET /networks/(name)` now returns field `Created` in response to show network created time.
|
||||||
* `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
|
* `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
|
||||||
* `GET /volumes`, `GET /volumes/(name)`, and `POST /volumes/create` now return the `Options` field which holds the driver specific options to use for when creating the volume.
|
* `GET /volumes`, `GET /volumes/(name)`, and `POST /volumes/create` now return the `Options` field which holds the driver specific options to use for when creating the volume.
|
||||||
|
@ -193,6 +194,8 @@ This section lists each version from latest to oldest. Each listing includes a
|
||||||
* `POST /plugins/(plugin name)/push` push a plugin.
|
* `POST /plugins/(plugin name)/push` push a plugin.
|
||||||
* `POST /plugins/create?name=(plugin name)` create a plugin.
|
* `POST /plugins/create?name=(plugin name)` create a plugin.
|
||||||
* `DELETE /plugins/(plugin name)` delete a plugin.
|
* `DELETE /plugins/(plugin name)` delete a plugin.
|
||||||
|
* `POST /node/(id or name)/update` now accepts both `id` or `name` to identify the node to update.
|
||||||
|
* `GET /images/json` now support a `reference` filter.
|
||||||
|
|
||||||
|
|
||||||
### v1.24 API changes
|
### v1.24 API changes
|
||||||
|
|
|
@ -1044,7 +1044,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.18/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1462,7 +1462,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.18/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1765,7 +1765,7 @@ Docker images report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.18/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1807,7 +1807,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.18/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1836,7 +1836,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.18/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1859,7 +1859,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.18/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.19/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1528,7 +1528,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.19/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1845,7 +1845,7 @@ Docker images report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.19/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1887,7 +1887,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.19/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1916,7 +1916,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.19/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1939,7 +1939,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.19/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
|
|
@ -1090,7 +1090,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.20/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1235,7 +1235,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.20/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -1682,7 +1682,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.20/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2000,7 +2000,7 @@ Docker images report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.20/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2042,7 +2042,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.20/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2071,7 +2071,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.20/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2094,7 +2094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.20/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
|
|
@ -1173,7 +1173,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.21/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1318,7 +1318,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.21/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -1835,7 +1835,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.21/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2155,7 +2155,7 @@ Docker images report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.21/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2198,7 +2198,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.21/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2227,7 +2227,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.21/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2250,7 +2250,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.21/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
@ -2637,7 +2637,7 @@ Instruct the driver to remove the volume (`name`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /volumes/tardis HTTP/1.1
|
DELETE /v1.21/volumes/tardis HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2912,7 +2912,7 @@ Instruct the driver to remove the network (`id`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
DELETE /v1.21/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|
|
@ -257,6 +257,7 @@ Create a container
|
||||||
"StopSignal": "SIGTERM",
|
"StopSignal": "SIGTERM",
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"Binds": ["/tmp:/tmp"],
|
"Binds": ["/tmp:/tmp"],
|
||||||
|
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||||
"Links": ["redis3:redis"],
|
"Links": ["redis3:redis"],
|
||||||
"Memory": 0,
|
"Memory": 0,
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 0,
|
||||||
|
@ -364,6 +365,8 @@ Create a container
|
||||||
_absolute_ path.
|
_absolute_ path.
|
||||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||||
inside the container. `container-dest` must be an _absolute_ path.
|
inside the container. `container-dest` must be an _absolute_ path.
|
||||||
|
- **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
|
||||||
|
mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
|
||||||
- **Links** - A list of links for the container. Each link entry should be
|
- **Links** - A list of links for the container. Each link entry should be
|
||||||
in the form of `container_name:alias`.
|
in the form of `container_name:alias`.
|
||||||
- **Memory** - Memory limit in bytes.
|
- **Memory** - Memory limit in bytes.
|
||||||
|
@ -1349,7 +1352,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.22/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1494,7 +1497,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.22/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -2049,7 +2052,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.22/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2394,7 +2397,7 @@ Docker networks report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.22/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2586,7 +2589,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.22/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2615,7 +2618,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.22/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2638,7 +2641,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.22/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
@ -2947,7 +2950,7 @@ Instruct the driver to remove the volume (`name`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /volumes/tardis HTTP/1.1
|
DELETE /v1.22/volumes/tardis HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3247,7 +3250,7 @@ Instruct the driver to remove the network (`id`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
DELETE /v1.22/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|
|
@ -281,6 +281,7 @@ Create a container
|
||||||
"StopSignal": "SIGTERM",
|
"StopSignal": "SIGTERM",
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"Binds": ["/tmp:/tmp"],
|
"Binds": ["/tmp:/tmp"],
|
||||||
|
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||||
"Links": ["redis3:redis"],
|
"Links": ["redis3:redis"],
|
||||||
"Memory": 0,
|
"Memory": 0,
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 0,
|
||||||
|
@ -389,6 +390,8 @@ Create a container
|
||||||
_absolute_ path.
|
_absolute_ path.
|
||||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||||
inside the container. `container-dest` must be an _absolute_ path.
|
inside the container. `container-dest` must be an _absolute_ path.
|
||||||
|
- **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
|
||||||
|
mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
|
||||||
- **Links** - A list of links for the container. Each link entry should be
|
- **Links** - A list of links for the container. Each link entry should be
|
||||||
in the form of `container_name:alias`.
|
in the form of `container_name:alias`.
|
||||||
- **Memory** - Memory limit in bytes.
|
- **Memory** - Memory limit in bytes.
|
||||||
|
@ -1384,7 +1387,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.23/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1529,7 +1532,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.23/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -2092,7 +2095,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.23/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2444,7 +2447,7 @@ Docker networks report the following events:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.23/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2636,7 +2639,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.23/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2665,7 +2668,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.23/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2688,7 +2691,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.23/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
@ -3005,7 +3008,7 @@ Return low-level information on the volume `name`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /volumes/tardis
|
GET /v1.23/volumes/tardis
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3036,7 +3039,7 @@ Instruct the driver to remove the volume (`name`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /volumes/tardis HTTP/1.1
|
DELETE /v1.23/volumes/tardis HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3364,7 +3367,7 @@ Instruct the driver to remove the network (`id`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
DELETE /v1.23/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,7 @@ Create a container
|
||||||
"StopSignal": "SIGTERM",
|
"StopSignal": "SIGTERM",
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"Binds": ["/tmp:/tmp"],
|
"Binds": ["/tmp:/tmp"],
|
||||||
|
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||||
"Links": ["redis3:redis"],
|
"Links": ["redis3:redis"],
|
||||||
"Memory": 0,
|
"Memory": 0,
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 0,
|
||||||
|
@ -404,6 +405,8 @@ Create a container
|
||||||
_absolute_ path.
|
_absolute_ path.
|
||||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||||
inside the container. `container-dest` must be an _absolute_ path.
|
inside the container. `container-dest` must be an _absolute_ path.
|
||||||
|
- **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
|
||||||
|
mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
|
||||||
- **Links** - A list of links for the container. Each link entry should be
|
- **Links** - A list of links for the container. Each link entry should be
|
||||||
in the form of `container_name:alias`.
|
in the form of `container_name:alias`.
|
||||||
- **Memory** - Memory limit in bytes.
|
- **Memory** - Memory limit in bytes.
|
||||||
|
@ -1411,7 +1414,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.24/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1524,7 +1527,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.24/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -2088,7 +2091,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.24/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2453,13 +2456,13 @@ Docker daemon report the following event:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.24/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Server: Docker/1.11.0 (linux)
|
Server: Docker/1.12.0 (linux)
|
||||||
Date: Fri, 29 Apr 2016 15:18:06 GMT
|
Date: Fri, 29 Apr 2016 15:18:06 GMT
|
||||||
Transfer-Encoding: chunked
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
@ -2646,7 +2649,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.24/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2675,7 +2678,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.24/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2698,7 +2701,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.24/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
@ -3030,7 +3033,7 @@ Return low-level information on the volume `name`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /volumes/tardis
|
GET /v1.24/volumes/tardis
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3082,7 +3085,7 @@ Instruct the driver to remove the volume (`name`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /volumes/tardis HTTP/1.1
|
DELETE /v1.24/volumes/tardis HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3414,7 +3417,7 @@ Instruct the driver to remove the network (`id`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
DELETE /v1.24/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3818,7 +3821,7 @@ Removes a plugin
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
```
|
```
|
||||||
DELETE /plugins/tiborvass/no-remove:latest HTTP/1.1
|
DELETE /v1.24/plugins/tiborvass/no-remove:latest HTTP/1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
The `:latest` tag is optional, and is used as default if omitted.
|
The `:latest` tag is optional, and is used as default if omitted.
|
||||||
|
@ -3912,7 +3915,7 @@ List nodes
|
||||||
"MemoryBytes": 8272408576
|
"MemoryBytes": 8272408576
|
||||||
},
|
},
|
||||||
"Engine": {
|
"Engine": {
|
||||||
"EngineVersion": "1.12.0-dev",
|
"EngineVersion": "1.12.0",
|
||||||
"Labels": {
|
"Labels": {
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
}
|
||||||
|
@ -3965,7 +3968,7 @@ List nodes
|
||||||
### Inspect a node
|
### Inspect a node
|
||||||
|
|
||||||
|
|
||||||
`GET /nodes/<id>`
|
`GET /nodes/(id or name)`
|
||||||
|
|
||||||
Return low-level information on the node `id`
|
Return low-level information on the node `id`
|
||||||
|
|
||||||
|
@ -4004,7 +4007,7 @@ Return low-level information on the node `id`
|
||||||
"MemoryBytes": 8272408576
|
"MemoryBytes": 8272408576
|
||||||
},
|
},
|
||||||
"Engine": {
|
"Engine": {
|
||||||
"EngineVersion": "1.12.0-dev",
|
"EngineVersion": "1.12.0",
|
||||||
"Labels": {
|
"Labels": {
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
}
|
||||||
|
@ -4047,13 +4050,13 @@ Return low-level information on the node `id`
|
||||||
### Remove a node
|
### Remove a node
|
||||||
|
|
||||||
|
|
||||||
`DELETE /nodes/(id)`
|
`DELETE /nodes/(id or name)`
|
||||||
|
|
||||||
Remove a node [`id`] from the swarm.
|
Remove a node from the swarm.
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
|
DELETE /v1.24/nodes/24ifsmvkjbyhk HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -4077,7 +4080,7 @@ Remove a node [`id`] from the swarm.
|
||||||
|
|
||||||
`POST /nodes/(id)/update`
|
`POST /nodes/(id)/update`
|
||||||
|
|
||||||
Update the node `id`.
|
Update a node.
|
||||||
|
|
||||||
The payload of the `POST` request is the new `NodeSpec` and
|
The payload of the `POST` request is the new `NodeSpec` and
|
||||||
overrides the current `NodeSpec` for the specified node.
|
overrides the current `NodeSpec` for the specified node.
|
||||||
|
@ -4693,7 +4696,7 @@ Stop and remove the service `id`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /services/16253994b7c4 HTTP/1.1
|
DELETE /v1.24/services/16253994b7c4 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|
|
@ -295,6 +295,7 @@ Create a container
|
||||||
"StopTimeout": 10,
|
"StopTimeout": 10,
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"Binds": ["/tmp:/tmp"],
|
"Binds": ["/tmp:/tmp"],
|
||||||
|
"Tmpfs": { "/run": "rw,noexec,nosuid,size=65536k" },
|
||||||
"Links": ["redis3:redis"],
|
"Links": ["redis3:redis"],
|
||||||
"Memory": 0,
|
"Memory": 0,
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 0,
|
||||||
|
@ -418,6 +419,8 @@ Create a container
|
||||||
_absolute_ path.
|
_absolute_ path.
|
||||||
+ `volume-name:container-dest:ro` to mount the volume read-only
|
+ `volume-name:container-dest:ro` to mount the volume read-only
|
||||||
inside the container. `container-dest` must be an _absolute_ path.
|
inside the container. `container-dest` must be an _absolute_ path.
|
||||||
|
- **Tmpfs** – A map of container directories which should be replaced by tmpfs mounts, and their corresponding
|
||||||
|
mount options. A JSON object in the form `{ "/run": "rw,noexec,nosuid,size=65536k" }`.
|
||||||
- **Links** - A list of links for the container. Each link entry should be
|
- **Links** - A list of links for the container. Each link entry should be
|
||||||
in the form of `container_name:alias`.
|
in the form of `container_name:alias`.
|
||||||
- **Memory** - Memory limit in bytes.
|
- **Memory** - Memory limit in bytes.
|
||||||
|
@ -1467,7 +1470,7 @@ Remove the container `id` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /containers/16253994b7c4?v=1 HTTP/1.1
|
DELETE /v1.25/containers/16253994b7c4?v=1 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -1580,7 +1583,7 @@ Upload a tar archive to be extracted to a path in the filesystem of container
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
PUT /containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
PUT /v1.25/containers/8cce319429b2/archive?path=/vol1 HTTP/1.1
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -2272,7 +2275,7 @@ Remove the image `name` from the filesystem
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /images/test HTTP/1.1
|
DELETE /v1.25/images/test HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -2610,7 +2613,7 @@ Display system-wide information
|
||||||
"Name": "WIN-V0V70C0LU5P",
|
"Name": "WIN-V0V70C0LU5P",
|
||||||
"Labels": null,
|
"Labels": null,
|
||||||
"ExperimentalBuild": false,
|
"ExperimentalBuild": false,
|
||||||
"ServerVersion": "1.13.0-dev",
|
"ServerVersion": "1.13.0",
|
||||||
"ClusterStore": "",
|
"ClusterStore": "",
|
||||||
"ClusterAdvertise": "",
|
"ClusterAdvertise": "",
|
||||||
"SecurityOptions": null,
|
"SecurityOptions": null,
|
||||||
|
@ -2898,13 +2901,13 @@ Docker daemon report the following event:
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /events?since=1374067924
|
GET /v1.25/events?since=1374067924
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Server: Docker/1.11.0 (linux)
|
Server: Docker/1.13.0 (linux)
|
||||||
Date: Fri, 29 Apr 2016 15:18:06 GMT
|
Date: Fri, 29 Apr 2016 15:18:06 GMT
|
||||||
Transfer-Encoding: chunked
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
@ -3091,7 +3094,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/ubuntu/get
|
GET /v1.25/images/ubuntu/get
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3120,7 +3123,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
GET /v1.25/images/get?names=myname%2Fmyapp%3Alatest&names=busybox
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3143,7 +3146,7 @@ See the [image tarball format](#image-tarball-format) for more details.
|
||||||
|
|
||||||
**Example request**
|
**Example request**
|
||||||
|
|
||||||
POST /images/load
|
POST /v1.25/images/load
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
|
|
||||||
Tarball in body
|
Tarball in body
|
||||||
|
@ -3490,7 +3493,7 @@ Return low-level information on the volume `name`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /volumes/tardis
|
GET /v1.25/volumes/tardis
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3547,7 +3550,7 @@ Instruct the driver to remove the volume (`name`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /volumes/tardis HTTP/1.1
|
DELETE /v1.25/volumes/tardis HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -3920,7 +3923,7 @@ Instruct the driver to remove the network (`id`).
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
DELETE /v1.25/networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -4296,7 +4299,7 @@ Content-Type: application/json
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
|
|
||||||
POST /plugins/tiborvass/no-remove/set
|
POST /v1.25/plugins/tiborvass/no-remove/set
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
["DEBUG=1"]
|
["DEBUG=1"]
|
||||||
|
@ -4375,7 +4378,7 @@ Removes a plugin
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
```
|
```
|
||||||
DELETE /plugins/tiborvass/no-remove:latest HTTP/1.1
|
DELETE /v1.25/plugins/tiborvass/no-remove:latest HTTP/1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
The `:latest` tag is optional, and is used as default if omitted.
|
The `:latest` tag is optional, and is used as default if omitted.
|
||||||
|
@ -4510,7 +4513,7 @@ List nodes
|
||||||
"MemoryBytes": 8272408576
|
"MemoryBytes": 8272408576
|
||||||
},
|
},
|
||||||
"Engine": {
|
"Engine": {
|
||||||
"EngineVersion": "1.12.0-dev",
|
"EngineVersion": "1.13.0",
|
||||||
"Labels": {
|
"Labels": {
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
}
|
||||||
|
@ -4564,7 +4567,7 @@ List nodes
|
||||||
### Inspect a node
|
### Inspect a node
|
||||||
|
|
||||||
|
|
||||||
`GET /nodes/<id>`
|
`GET /nodes/(id or name)`
|
||||||
|
|
||||||
Return low-level information on the node `id`
|
Return low-level information on the node `id`
|
||||||
|
|
||||||
|
@ -4603,7 +4606,7 @@ Return low-level information on the node `id`
|
||||||
"MemoryBytes": 8272408576
|
"MemoryBytes": 8272408576
|
||||||
},
|
},
|
||||||
"Engine": {
|
"Engine": {
|
||||||
"EngineVersion": "1.12.0-dev",
|
"EngineVersion": "1.13.0",
|
||||||
"Labels": {
|
"Labels": {
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
}
|
||||||
|
@ -4647,13 +4650,13 @@ Return low-level information on the node `id`
|
||||||
### Remove a node
|
### Remove a node
|
||||||
|
|
||||||
|
|
||||||
`DELETE /nodes/(id)`
|
`DELETE /nodes/(id or name)`
|
||||||
|
|
||||||
Remove a node [`id`] from the swarm.
|
Remove a node from the swarm.
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /nodes/24ifsmvkjbyhk HTTP/1.1
|
DELETE /v1.25/nodes/24ifsmvkjbyhk HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -4675,9 +4678,9 @@ Remove a node [`id`] from the swarm.
|
||||||
### Update a node
|
### Update a node
|
||||||
|
|
||||||
|
|
||||||
`POST /nodes/(id)/update`
|
`POST /nodes/(id or name)/update`
|
||||||
|
|
||||||
Update the node `id`.
|
Update a node.
|
||||||
|
|
||||||
The payload of the `POST` request is the new `NodeSpec` and
|
The payload of the `POST` request is the new `NodeSpec` and
|
||||||
overrides the current `NodeSpec` for the specified node.
|
overrides the current `NodeSpec` for the specified node.
|
||||||
|
@ -4813,7 +4816,7 @@ Initialize a new swarm. The body of the HTTP response includes the node ID.
|
||||||
Content-Length: 28
|
Content-Length: 28
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Date: Thu, 01 Sep 2016 21:49:13 GMT
|
Date: Thu, 01 Sep 2016 21:49:13 GMT
|
||||||
Server: Docker/1.12.0 (linux)
|
Server: Docker/1.13.0 (linux)
|
||||||
|
|
||||||
"7v2t30z9blmxuhnyo6s4cpenp"
|
"7v2t30z9blmxuhnyo6s4cpenp"
|
||||||
|
|
||||||
|
@ -4821,7 +4824,7 @@ Initialize a new swarm. The body of the HTTP response includes the node ID.
|
||||||
|
|
||||||
- **200** – no error
|
- **200** – no error
|
||||||
- **400** – bad parameter
|
- **400** – bad parameter
|
||||||
- **406** – node is already part of a swarm
|
- **503** – node is already part of a swarm
|
||||||
|
|
||||||
JSON Parameters:
|
JSON Parameters:
|
||||||
|
|
||||||
|
@ -4890,7 +4893,7 @@ Join an existing swarm
|
||||||
|
|
||||||
- **200** – no error
|
- **200** – no error
|
||||||
- **400** – bad parameter
|
- **400** – bad parameter
|
||||||
- **406** – node is already part of a swarm
|
- **503** – node is already part of a swarm
|
||||||
|
|
||||||
JSON Parameters:
|
JSON Parameters:
|
||||||
|
|
||||||
|
@ -4928,7 +4931,7 @@ Leave a swarm
|
||||||
**Status codes**:
|
**Status codes**:
|
||||||
|
|
||||||
- **200** – no error
|
- **200** – no error
|
||||||
- **406** – node is not part of a swarm
|
- **503** – node is not part of a swarm
|
||||||
|
|
||||||
### Retrieve the swarm's unlock key
|
### Retrieve the swarm's unlock key
|
||||||
|
|
||||||
|
@ -5024,7 +5027,7 @@ Update a swarm
|
||||||
|
|
||||||
- **200** – no error
|
- **200** – no error
|
||||||
- **400** – bad parameter
|
- **400** – bad parameter
|
||||||
- **406** – node is not part of a swarm
|
- **503** – node is not part of a swarm
|
||||||
|
|
||||||
JSON Parameters:
|
JSON Parameters:
|
||||||
|
|
||||||
|
@ -5262,14 +5265,15 @@ image](#create-an-image) section for more details.
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"ID":"ak7w3gjqoa3kuz8xcpnyy0pvl"
|
"ID": "ak7w3gjqoa3kuz8xcpnyy0pvl",
|
||||||
|
"Warnings": ["unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"]
|
||||||
}
|
}
|
||||||
|
|
||||||
**Status codes**:
|
**Status codes**:
|
||||||
|
|
||||||
- **201** – no error
|
- **201** – no error
|
||||||
- **406** – server error or node is not part of a swarm
|
|
||||||
- **409** – name conflicts with an existing object
|
- **409** – name conflicts with an existing object
|
||||||
|
- **503** – server error or node is not part of a swarm
|
||||||
|
|
||||||
**JSON Parameters**:
|
**JSON Parameters**:
|
||||||
|
|
||||||
|
@ -5367,7 +5371,7 @@ Stop and remove the service `id`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /services/16253994b7c4 HTTP/1.1
|
DELETE /v1.25/services/16253994b7c4 HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -5628,6 +5632,16 @@ image](#create-an-image) section for more details.
|
||||||
- **404** – no such service
|
- **404** – no such service
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"Warnings": ["unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### Get service logs
|
### Get service logs
|
||||||
|
|
||||||
`GET /services/(id or name)/logs`
|
`GET /services/(id or name)/logs`
|
||||||
|
@ -5639,7 +5653,7 @@ Get `stdout` and `stderr` logs from the service ``id``
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /services/4fa6e0f0c678/logs?stderr=1&stdout=1×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**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -5988,7 +6002,7 @@ List secrets
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /secrets HTTP/1.1
|
GET /v1.25/secrets HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -6026,7 +6040,7 @@ Create a secret
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /secrets/create HTTP/1.1
|
POST /v1.25/secrets/create HTTP/1.1
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -6049,8 +6063,8 @@ Create a secret
|
||||||
**Status codes**:
|
**Status codes**:
|
||||||
|
|
||||||
- **201** – no error
|
- **201** – no error
|
||||||
- **406** – server error or node is not part of a swarm
|
|
||||||
- **409** – name conflicts with an existing object
|
- **409** – name conflicts with an existing object
|
||||||
|
- **503** – server error or node is not part of a swarm
|
||||||
|
|
||||||
**JSON Parameters**:
|
**JSON Parameters**:
|
||||||
|
|
||||||
|
@ -6066,7 +6080,7 @@ Get details on the secret `id`
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
GET /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -6099,7 +6113,7 @@ Remove the secret `id` from the secret store
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
DELETE /secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
DELETE /v1.25/secrets/ktnbjxoalbkvbvedmg1urrz8h HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ Options:
|
||||||
- label=<key> or label=<key>=<value>
|
- label=<key> or label=<key>=<value>
|
||||||
- before=(<image-name>[:tag]|<image-id>|<image@digest>)
|
- before=(<image-name>[:tag]|<image-id>|<image@digest>)
|
||||||
- since=(<image-name>[:tag]|<image-id>|<image@digest>)
|
- since=(<image-name>[:tag]|<image-id>|<image@digest>)
|
||||||
|
- reference=(pattern of an image reference)
|
||||||
--format string Pretty-print images using a Go template
|
--format string Pretty-print images using a Go template
|
||||||
--help Print usage
|
--help Print usage
|
||||||
--no-trunc Don't truncate output
|
--no-trunc Don't truncate output
|
||||||
|
@ -229,6 +230,24 @@ Filtering with `since` would give:
|
||||||
image1 latest eeae25ada2aa 4 minutes ago 188.3 MB
|
image1 latest eeae25ada2aa 4 minutes ago 188.3 MB
|
||||||
image2 latest dea752e4e117 9 minutes ago 188.3 MB
|
image2 latest dea752e4e117 9 minutes ago 188.3 MB
|
||||||
|
|
||||||
|
#### Reference
|
||||||
|
|
||||||
|
The `reference` filter shows only images whose reference matches
|
||||||
|
the specified pattern.
|
||||||
|
|
||||||
|
$ docker images
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
busybox latest e02e811dd08f 5 weeks ago 1.09 MB
|
||||||
|
busybox uclibc e02e811dd08f 5 weeks ago 1.09 MB
|
||||||
|
busybox musl 733eb3059dce 5 weeks ago 1.21 MB
|
||||||
|
busybox glibc 21c16b6787c6 5 weeks ago 4.19 MB
|
||||||
|
|
||||||
|
Filtering with `reference` would give:
|
||||||
|
|
||||||
|
$ docker images --filter=reference='busy*:*libc'
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
busybox uclibc e02e811dd08f 5 weeks ago 1.09 MB
|
||||||
|
busybox glibc 21c16b6787c6 5 weeks ago 4.19 MB
|
||||||
|
|
||||||
## Formatting
|
## Formatting
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ available on the volume where `/var/lib/docker` is mounted.
|
||||||
|
|
||||||
## Display Docker system information
|
## Display Docker system information
|
||||||
|
|
||||||
Here is a sample output for a daemon running on Ubuntu, using the overlay
|
Here is a sample output for a daemon running on Ubuntu, using the overlay2
|
||||||
storage driver and a node that is part of a 2-node swarm:
|
storage driver and a node that is part of a 2-node swarm:
|
||||||
|
|
||||||
$ docker -D info
|
$ docker -D info
|
||||||
|
@ -56,49 +56,72 @@ storage driver and a node that is part of a 2-node swarm:
|
||||||
Paused: 1
|
Paused: 1
|
||||||
Stopped: 10
|
Stopped: 10
|
||||||
Images: 52
|
Images: 52
|
||||||
Server Version: 1.12.0-dev
|
Server Version: 1.13.0
|
||||||
Storage Driver: overlay
|
Storage Driver: overlay2
|
||||||
Backing Filesystem: extfs
|
Backing Filesystem: extfs
|
||||||
|
Supports d_type: true
|
||||||
|
Native Overlay Diff: false
|
||||||
Logging Driver: json-file
|
Logging Driver: json-file
|
||||||
Cgroup Driver: cgroupfs
|
Cgroup Driver: cgroupfs
|
||||||
Plugins:
|
Plugins:
|
||||||
Volume: local
|
Volume: local
|
||||||
Network: bridge null host overlay
|
Network: bridge host macvlan null overlay
|
||||||
Swarm:
|
Swarm: active
|
||||||
NodeID: 0gac67oclbxq7
|
NodeID: rdjq45w1op418waxlairloqbm
|
||||||
Is Manager: true
|
Is Manager: true
|
||||||
Managers: 2
|
ClusterID: te8kdyw33n36fqiz74bfjeixd
|
||||||
|
Managers: 1
|
||||||
Nodes: 2
|
Nodes: 2
|
||||||
Runtimes: default
|
Orchestration:
|
||||||
Default Runtime: default
|
Task History Retention Limit: 5
|
||||||
Security Options: apparmor seccomp
|
Raft:
|
||||||
Kernel Version: 4.4.0-21-generic
|
Snapshot Interval: 10000
|
||||||
Operating System: Ubuntu 16.04 LTS
|
Number of Old Snapshots to Retain: 0
|
||||||
|
Heartbeat Tick: 1
|
||||||
|
Election Tick: 3
|
||||||
|
Dispatcher:
|
||||||
|
Heartbeat Period: 5 seconds
|
||||||
|
CA Configuration:
|
||||||
|
Expiry Duration: 3 months
|
||||||
|
Node Address: 172.16.66.128 172.16.66.129
|
||||||
|
Manager Addresses:
|
||||||
|
172.16.66.128:2477
|
||||||
|
Runtimes: runc
|
||||||
|
Default Runtime: runc
|
||||||
|
Init Binary: docker-init
|
||||||
|
containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
|
||||||
|
runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
||||||
|
init version: N/A (expected: v0.13.0)
|
||||||
|
Security Options:
|
||||||
|
apparmor
|
||||||
|
seccomp
|
||||||
|
Profile: default
|
||||||
|
Kernel Version: 4.4.0-31-generic
|
||||||
|
Operating System: Ubuntu 16.04.1 LTS
|
||||||
OSType: linux
|
OSType: linux
|
||||||
Architecture: x86_64
|
Architecture: x86_64
|
||||||
CPUs: 24
|
CPUs: 2
|
||||||
Total Memory: 62.86 GiB
|
Total Memory: 1.937 GiB
|
||||||
Name: docker
|
Name: ubuntu
|
||||||
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
|
ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
|
||||||
Docker Root Dir: /var/lib/docker
|
Docker Root Dir: /var/lib/docker
|
||||||
Debug mode (client): true
|
Debug Mode (client): true
|
||||||
Debug mode (server): true
|
Debug Mode (server): true
|
||||||
File Descriptors: 59
|
File Descriptors: 30
|
||||||
Goroutines: 159
|
Goroutines: 123
|
||||||
System Time: 2016-04-26T10:04:06.14689342-04:00
|
System Time: 2016-11-12T17:24:37.955404361-08:00
|
||||||
EventsListeners: 0
|
EventsListeners: 0
|
||||||
Http Proxy: http://test:test@localhost:8080
|
Http Proxy: http://proxy.example.com:80/
|
||||||
Https Proxy: https://test:test@localhost:8080
|
|
||||||
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
||||||
Username: svendowideit
|
|
||||||
Registry: https://index.docker.io/v1/
|
Registry: https://index.docker.io/v1/
|
||||||
WARNING: No swap limit support
|
WARNING: No swap limit support
|
||||||
Labels:
|
Labels:
|
||||||
storage=ssd
|
storage=ssd
|
||||||
staging=true
|
staging=true
|
||||||
Insecure registries:
|
Experimental: false
|
||||||
myinsecurehost:5000
|
Insecure Registries:
|
||||||
127.0.0.0/8
|
127.0.0.0/8
|
||||||
|
Live Restore Enabled: false
|
||||||
|
|
||||||
The global `-D` option tells all `docker` commands to output debug information.
|
The global `-D` option tells all `docker` commands to output debug information.
|
||||||
|
|
||||||
|
@ -168,7 +191,7 @@ Here is a sample output for a daemon running on Windows Server 2016:
|
||||||
Paused: 0
|
Paused: 0
|
||||||
Stopped: 1
|
Stopped: 1
|
||||||
Images: 17
|
Images: 17
|
||||||
Server Version: 1.13.0-dev
|
Server Version: 1.13.0
|
||||||
Storage Driver: windowsfilter
|
Storage Driver: windowsfilter
|
||||||
Windows:
|
Windows:
|
||||||
Logging Driver: json-file
|
Logging Driver: json-file
|
||||||
|
|
|
@ -21,9 +21,10 @@ Usage: docker stats [OPTIONS] [CONTAINER...]
|
||||||
Display a live stream of container(s) resource usage statistics
|
Display a live stream of container(s) resource usage statistics
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-a, --all Show all containers (default shows just running)
|
-a, --all Show all containers (default shows just running)
|
||||||
--help Print usage
|
--format string Pretty-print images using a Go template
|
||||||
--no-stream Disable streaming stats and only pull the first result
|
--help Print usage
|
||||||
|
--no-stream Disable streaming stats and only pull the first result
|
||||||
```
|
```
|
||||||
|
|
||||||
The `docker stats` command returns a live data stream for running containers. To limit data to one or more specific containers, specify a list of container names or ids separated by a space. You can specify a stopped container but stopped containers do not return any data.
|
The `docker stats` command returns a live data stream for running containers. To limit data to one or more specific containers, specify a list of container names or ids separated by a space. You can specify a stopped container but stopped containers do not return any data.
|
||||||
|
@ -77,7 +78,9 @@ Valid placeholders for the Go template are listed below:
|
||||||
|
|
||||||
Placeholder | Description
|
Placeholder | Description
|
||||||
------------ | --------------------------------------------
|
------------ | --------------------------------------------
|
||||||
`.Container` | Container name or ID
|
`.Container` | Container name or ID (user input)
|
||||||
|
`.Name` | Container name
|
||||||
|
`.ID` | Container ID
|
||||||
`.CPUPerc` | CPU percentage
|
`.CPUPerc` | CPU percentage
|
||||||
`.MemUsage` | Memory usage
|
`.MemUsage` | Memory usage
|
||||||
`.NetIO` | Network IO
|
`.NetIO` | Network IO
|
||||||
|
|
|
@ -16,9 +16,9 @@ keywords: "tag, name, image"
|
||||||
# tag
|
# tag
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
Usage: docker tag IMAGE[:TAG] IMAGE[:TAG]
|
Usage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
|
||||||
|
|
||||||
Tag an image into a repository
|
Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--help Print usage
|
--help Print usage
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
|
TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
|
||||||
RUNC_COMMIT=ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
RUNC_COMMIT=51371867a01c467f08af739783b8beafc15
|
||||||
CONTAINERD_COMMIT=8517738ba4b82aff5662c97ca4627e7e4d03b531
|
CONTAINERD_COMMIT=03e5862ec0d8d3b3f750e19fca3ee367e13c090e
|
||||||
TINI_COMMIT=v0.13.0
|
TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
|
||||||
LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
|
LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
|
||||||
VNDR_COMMIT=f56bd4504b4fad07a357913687fb652ee54bb3b0
|
VNDR_COMMIT=f56bd4504b4fad07a357913687fb652ee54bb3b0
|
||||||
|
|
|
@ -8,7 +8,8 @@ swagger generate model -f api/swagger.yaml \
|
||||||
-n ImageSummary \
|
-n ImageSummary \
|
||||||
-n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \
|
-n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \
|
||||||
-n ErrorResponse \
|
-n ErrorResponse \
|
||||||
-n IdResponse
|
-n IdResponse \
|
||||||
|
-n ServiceUpdateResponse
|
||||||
|
|
||||||
swagger generate operation -f api/swagger.yaml \
|
swagger generate operation -f api/swagger.yaml \
|
||||||
-t api -a types -m types -C api/swagger-gen.yaml \
|
-t api -a types -m types -C api/swagger-gen.yaml \
|
||||||
|
|
|
@ -4,7 +4,7 @@ export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
source "${SCRIPTDIR}/.validate"
|
source "${SCRIPTDIR}/.validate"
|
||||||
|
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) )
|
files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' | grep -v '^api/types/container/' || true) )
|
||||||
unset IFS
|
unset IFS
|
||||||
|
|
||||||
errors=()
|
errors=()
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/pkg/symlink"
|
"github.com/docker/docker/pkg/symlink"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -164,7 +165,9 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
|
func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
|
||||||
rawTar, err := os.Open(filename)
|
// We use system.OpenSequential to use sequential file access on Windows, avoiding
|
||||||
|
// depleting the standby list. On Linux, this equates to a regular os.Open.
|
||||||
|
rawTar, err := system.OpenSequential(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading embedded tar: %v", err)
|
logrus.Debugf("Error reading embedded tar: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -315,8 +315,10 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
|
||||||
}
|
}
|
||||||
os.Symlink(relPath, layerPath)
|
os.Symlink(relPath, layerPath)
|
||||||
} else {
|
} else {
|
||||||
|
// Use system.CreateSequential rather than os.Create. This ensures sequential
|
||||||
tarFile, err := os.Create(layerPath)
|
// file access on Windows to avoid eating into MM standby list.
|
||||||
|
// On Linux, this equates to a regular os.Create.
|
||||||
|
tarFile, err := system.CreateSequential(layerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,7 +495,7 @@ func (d *Daemon) SockRequest(method, endpoint string, data interface{}) (int, []
|
||||||
return res.StatusCode, b, err
|
return res.StatusCode, b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SockRequestRaw executes a socket request on a daemon and returns a http
|
// SockRequestRaw executes a socket request on a daemon and returns an http
|
||||||
// response and a reader for the output data.
|
// response and a reader for the output data.
|
||||||
func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
|
func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
|
||||||
return sockRequestRawToDaemon(method, endpoint, data, ct, d.sock())
|
return sockRequestRawToDaemon(method, endpoint, data, ct, d.sock())
|
||||||
|
|
|
@ -1865,6 +1865,10 @@ func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) {
|
||||||
func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
|
func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
|
||||||
testRequires(c, DaemonIsWindows)
|
testRequires(c, DaemonIsWindows)
|
||||||
name := "testbuildwindowsaddcopypathprocessing"
|
name := "testbuildwindowsaddcopypathprocessing"
|
||||||
|
// TODO Windows (@jhowardmsft). Needs a follow-up PR to 22181 to
|
||||||
|
// support backslash such as .\\ being equivalent to ./ and c:\\ being
|
||||||
|
// equivalent to c:/. This is not currently (nor ever has been) supported
|
||||||
|
// by docker on the Windows platform.
|
||||||
dockerfile := `
|
dockerfile := `
|
||||||
FROM busybox
|
FROM busybox
|
||||||
# No trailing slash on COPY/ADD
|
# No trailing slash on COPY/ADD
|
||||||
|
@ -1874,8 +1878,8 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
|
||||||
WORKDIR /wc2
|
WORKDIR /wc2
|
||||||
ADD wc2 c:/wc2
|
ADD wc2 c:/wc2
|
||||||
WORKDIR c:/
|
WORKDIR c:/
|
||||||
RUN sh -c "[ $(cat c:/wc1/wc1) = 'hellowc1' ]"
|
RUN sh -c "[ $(cat c:/wc1) = 'hellowc1' ]"
|
||||||
RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]"
|
RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"
|
||||||
|
|
||||||
# Trailing slash on COPY/ADD, Windows-style path.
|
# Trailing slash on COPY/ADD, Windows-style path.
|
||||||
WORKDIR /wd1
|
WORKDIR /wd1
|
||||||
|
@ -7172,31 +7176,6 @@ RUN echo vegeta
|
||||||
c.Assert(out, checker.Contains, "Step 3/3 : RUN echo vegeta")
|
c.Assert(out, checker.Contains, "Step 3/3 : RUN echo vegeta")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies if COPY file . when WORKDIR is set to a non-existing directory,
|
|
||||||
// the directory is created and the file is copied into the directory,
|
|
||||||
// as opposed to the file being copied as a file with the name of the
|
|
||||||
// directory. Fix for 27545 (found on Windows, but regression good for Linux too)
|
|
||||||
func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) {
|
|
||||||
name := "testbuildcopyfiledotwithworkdir"
|
|
||||||
|
|
||||||
ctx, err := fakeContext(`FROM busybox
|
|
||||||
WORKDIR /foo
|
|
||||||
COPY file .
|
|
||||||
RUN ["cat", "/foo/file"]
|
|
||||||
`,
|
|
||||||
map[string]string{})
|
|
||||||
if err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
defer ctx.Close()
|
|
||||||
if err := ctx.Add("file", "content"); err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err = buildImageFromContext(name, ctx, true); err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
|
func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
|
||||||
testRequires(c, ExperimentalDaemon)
|
testRequires(c, ExperimentalDaemon)
|
||||||
dockerFile := `
|
dockerFile := `
|
||||||
|
@ -7287,3 +7266,20 @@ func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
|
||||||
_, err := buildImage("testopaquedirectory", dockerFile, false)
|
_, err := buildImage("testopaquedirectory", dockerFile, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows test for USER in dockerfile
|
||||||
|
func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsWindows)
|
||||||
|
name := "testbuildwindowsuser"
|
||||||
|
_, out, err := buildImageWithOut(name,
|
||||||
|
`FROM `+WindowsBaseImage+`
|
||||||
|
RUN net user user /add
|
||||||
|
USER user
|
||||||
|
RUN set username
|
||||||
|
`,
|
||||||
|
true)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Assert(strings.ToLower(out), checker.Contains, "username=user")
|
||||||
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
|
||||||
stdout, stderr, _, err = runCommandWithStdoutStderr(cmd)
|
stdout, stderr, _, err = runCommandWithStdoutStderr(cmd)
|
||||||
c.Assert(err, checker.NotNil)
|
c.Assert(err, checker.NotNil)
|
||||||
c.Assert(stdout, checker.Equals, "")
|
c.Assert(stdout, checker.Equals, "")
|
||||||
c.Assert(stderr, checker.Equals, "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n", check.Commentf("Unexcepted output for 'docker badCmd'\n"))
|
c.Assert(stderr, checker.Equals, "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'\n", check.Commentf("Unexcepted output for 'docker badCmd'\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) error {
|
func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) error {
|
||||||
|
|
|
@ -1072,3 +1072,19 @@ func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(out, checker.Contains, expectedOutput)
|
c.Assert(out, checker.Contains, expectedOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSwarmSuite) TestSwarmServiceInspectPretty(c *check.C) {
|
||||||
|
d := s.AddDaemon(c, true, true)
|
||||||
|
|
||||||
|
name := "top"
|
||||||
|
out, err := d.Cmd("service", "create", "--name", name, "--limit-cpu=0.5", "busybox", "top")
|
||||||
|
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||||
|
|
||||||
|
expectedOutput := `
|
||||||
|
Resources:
|
||||||
|
Limits:
|
||||||
|
CPU: 0.5`
|
||||||
|
out, err = d.Cmd("service", "inspect", "--pretty", name)
|
||||||
|
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||||
|
c.Assert(out, checker.Contains, expectedOutput, check.Commentf(out))
|
||||||
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ type remote struct {
|
||||||
func New(stateDir string, options ...RemoteOption) (_ Remote, err error) {
|
func New(stateDir string, options ...RemoteOption) (_ Remote, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specificed the correct address. Got error: %v", err)
|
err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specified the correct address. Got error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
r := &remote{
|
r := &remote{
|
||||||
|
|
|
@ -39,7 +39,7 @@ available on the volume where `/var/lib/docker` is mounted.
|
||||||
|
|
||||||
## Display Docker system information
|
## Display Docker system information
|
||||||
|
|
||||||
Here is a sample output for a daemon running on Ubuntu, using the overlay
|
Here is a sample output for a daemon running on Ubuntu, using the overlay2
|
||||||
storage driver:
|
storage driver:
|
||||||
|
|
||||||
$ docker -D info
|
$ docker -D info
|
||||||
|
@ -48,49 +48,74 @@ storage driver:
|
||||||
Paused: 1
|
Paused: 1
|
||||||
Stopped: 10
|
Stopped: 10
|
||||||
Images: 52
|
Images: 52
|
||||||
Server Version: 1.12.0-dev
|
Server Version: 1.13.0
|
||||||
Storage Driver: overlay
|
Storage Driver: overlay2
|
||||||
Backing Filesystem: extfs
|
Backing Filesystem: extfs
|
||||||
|
Supports d_type: true
|
||||||
|
Native Overlay Diff: false
|
||||||
Logging Driver: json-file
|
Logging Driver: json-file
|
||||||
Cgroup Driver: cgroupfs
|
Cgroup Driver: cgroupfs
|
||||||
Plugins:
|
Plugins:
|
||||||
Volume: local
|
Volume: local
|
||||||
Network: bridge null host overlay
|
Network: bridge host macvlan null overlay
|
||||||
Swarm:
|
Swarm: active
|
||||||
NodeID: 0gac67oclbxq7
|
NodeID: rdjq45w1op418waxlairloqbm
|
||||||
IsManager: YES
|
Is Manager: true
|
||||||
Managers: 2
|
ClusterID: te8kdyw33n36fqiz74bfjeixd
|
||||||
|
Managers: 1
|
||||||
Nodes: 2
|
Nodes: 2
|
||||||
Runtimes: default
|
Orchestration:
|
||||||
Default Runtime: default
|
Task History Retention Limit: 5
|
||||||
Security Options: apparmor seccomp
|
Raft:
|
||||||
Kernel Version: 4.4.0-21-generic
|
Snapshot Interval: 10000
|
||||||
Operating System: Ubuntu 16.04 LTS
|
Number of Old Snapshots to Retain: 0
|
||||||
|
Heartbeat Tick: 1
|
||||||
|
Election Tick: 3
|
||||||
|
Dispatcher:
|
||||||
|
Heartbeat Period: 5 seconds
|
||||||
|
CA Configuration:
|
||||||
|
Expiry Duration: 3 months
|
||||||
|
Node Address: 172.16.66.128 172.16.66.129
|
||||||
|
Manager Addresses:
|
||||||
|
172.16.66.128:2477
|
||||||
|
Runtimes: runc
|
||||||
|
Default Runtime: runc
|
||||||
|
Init Binary: docker-init
|
||||||
|
containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531
|
||||||
|
runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2
|
||||||
|
init version: N/A (expected: v0.13.0)
|
||||||
|
Security Options:
|
||||||
|
apparmor
|
||||||
|
seccomp
|
||||||
|
Profile: default
|
||||||
|
Kernel Version: 4.4.0-31-generic
|
||||||
|
Operating System: Ubuntu 16.04.1 LTS
|
||||||
OSType: linux
|
OSType: linux
|
||||||
Architecture: x86_64
|
Architecture: x86_64
|
||||||
CPUs: 24
|
CPUs: 2
|
||||||
Total Memory: 62.86 GiB
|
Total Memory: 1.937 GiB
|
||||||
Name: docker
|
Name: ubuntu
|
||||||
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
|
ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326
|
||||||
Docker Root Dir: /var/lib/docker
|
Docker Root Dir: /var/lib/docker
|
||||||
Debug mode (client): true
|
Debug Mode (client): true
|
||||||
Debug mode (server): true
|
Debug Mode (server): true
|
||||||
File Descriptors: 59
|
File Descriptors: 30
|
||||||
Goroutines: 159
|
Goroutines: 123
|
||||||
System Time: 2016-04-26T10:04:06.14689342-04:00
|
System Time: 2016-11-12T17:24:37.955404361-08:00
|
||||||
EventsListeners: 0
|
EventsListeners: 0
|
||||||
Http Proxy: http://test:test@localhost:8080
|
Http Proxy: http://proxy.example.com:80/
|
||||||
Https Proxy: https://test:test@localhost:8080
|
|
||||||
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com
|
||||||
Username: svendowideit
|
|
||||||
Registry: https://index.docker.io/v1/
|
Registry: https://index.docker.io/v1/
|
||||||
WARNING: No swap limit support
|
WARNING: No swap limit support
|
||||||
Labels:
|
Labels:
|
||||||
storage=ssd
|
storage=ssd
|
||||||
staging=true
|
staging=true
|
||||||
Insecure registries:
|
Experimental: false
|
||||||
myinsecurehost:5000
|
Insecure Registries:
|
||||||
127.0.0.0/8
|
127.0.0.0/8
|
||||||
|
Live Restore Enabled: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The global `-D` option tells all `docker` commands to output debug information.
|
The global `-D` option tells all `docker` commands to output debug information.
|
||||||
|
|
||||||
|
|
|
@ -907,7 +907,7 @@ should fix the problem.
|
||||||
## Mapping Ports for External Usage
|
## Mapping Ports for External Usage
|
||||||
|
|
||||||
The exposed port of an application can be mapped to a host port using the **-p**
|
The exposed port of an application can be mapped to a host port using the **-p**
|
||||||
flag. For example, a httpd port 80 can be mapped to the host port 8080 using the
|
flag. For example, an httpd port 80 can be mapped to the host port 8080 using the
|
||||||
following:
|
following:
|
||||||
|
|
||||||
# docker run -p 8080:80 -d -i -t fedora/httpd
|
# docker run -p 8080:80 -d -i -t fedora/httpd
|
||||||
|
|
|
@ -30,6 +30,8 @@ Display a live stream of one or more containers' resource usage statistics
|
||||||
Pretty-print containers statistics using a Go template.
|
Pretty-print containers statistics using a Go template.
|
||||||
Valid placeholders:
|
Valid placeholders:
|
||||||
.Container - Container name or ID.
|
.Container - Container name or ID.
|
||||||
|
.Name - Container name.
|
||||||
|
.ID - Container ID.
|
||||||
.CPUPerc - CPU percentage.
|
.CPUPerc - CPU percentage.
|
||||||
.MemUsage - Memory usage.
|
.MemUsage - Memory usage.
|
||||||
.NetIO - Network IO.
|
.NetIO - Network IO.
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
% Docker Community
|
% Docker Community
|
||||||
% JUNE 2014
|
% JUNE 2014
|
||||||
# NAME
|
# NAME
|
||||||
docker-tag - Tag an image into a repository
|
docker-tag - Create a tag `TARGET_IMAGE` that refers to `SOURCE_IMAGE`
|
||||||
|
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
**docker tag**
|
**docker tag**
|
||||||
[**--help**]
|
[**--help**]
|
||||||
NAME[:TAG] NAME[:TAG]
|
SOURCE_NAME[:TAG] TARGET_NAME[:TAG]
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
Assigns a new alias to an image in a registry. An alias refers to the
|
Assigns a new alias to an image in a registry. An alias refers to the
|
||||||
|
|
|
@ -16,6 +16,7 @@ const (
|
||||||
portOptMode = "mode"
|
portOptMode = "mode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PortOpt represents a port config in swarm mode.
|
||||||
type PortOpt struct {
|
type PortOpt struct {
|
||||||
ports []swarm.PortConfig
|
ports []swarm.PortConfig
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,7 +374,10 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
||||||
file, err := os.Open(path)
|
// We use system.OpenSequential to ensure we use sequential file
|
||||||
|
// access on Windows to avoid depleting the standby list.
|
||||||
|
// On Linux, this equates to a regular os.Open.
|
||||||
|
file, err := system.OpenSequential(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -412,8 +415,10 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
||||||
}
|
}
|
||||||
|
|
||||||
case tar.TypeReg, tar.TypeRegA:
|
case tar.TypeReg, tar.TypeRegA:
|
||||||
// Source is regular file
|
// Source is regular file. We use system.OpenFileSequential to use sequential
|
||||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
// file access to avoid depleting the standby list on Windows.
|
||||||
|
// On Linux, this equates to a regular os.OpenFile
|
||||||
|
file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
package devicemapper
|
package devicemapper
|
||||||
|
|
||||||
// LibraryDeferredRemovalsupport is not supported when statically linked.
|
// LibraryDeferredRemovalSupport is not supported when statically linked.
|
||||||
const LibraryDeferredRemovalSupport = false
|
const LibraryDeferredRemovalSupport = false
|
||||||
|
|
||||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||||
|
|
|
@ -86,7 +86,7 @@ func ParseAdvertise(advertise string) (string, error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
return "", fmt.Errorf("couldnt find a valid ip-address in interface %s", advertise)
|
return "", fmt.Errorf("could not find a valid ip-address in interface %s", advertise)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = net.JoinHostPort(addr, port)
|
addr = net.JoinHostPort(addr, port)
|
||||||
|
|
|
@ -155,18 +155,18 @@ func (r *multiReadSeeker) Read(b []byte) (int, error) {
|
||||||
r.pos = &pos{0, 0}
|
r.pos = &pos{0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
bCap := int64(cap(b))
|
bLen := int64(len(b))
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
var rdr io.ReadSeeker
|
var rdr io.ReadSeeker
|
||||||
|
|
||||||
for _, rdr = range r.readers[r.pos.idx:] {
|
for _, rdr = range r.readers[r.pos.idx:] {
|
||||||
readBytes, err := io.CopyN(buf, rdr, bCap)
|
readBytes, err := io.CopyN(buf, rdr, bLen)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
bCap -= readBytes
|
bLen -= readBytes
|
||||||
|
|
||||||
if bCap == 0 {
|
if bLen == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ioutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -188,3 +189,23 @@ func TestMultiReadSeekerCurAfterSet(t *testing.T) {
|
||||||
t.Fatalf("reader size does not match, got %d, expected %d", size, mid+18)
|
t.Fatalf("reader size does not match, got %d, expected %d", size, mid+18)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultiReadSeekerSmallReads(t *testing.T) {
|
||||||
|
readers := []io.ReadSeeker{}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
integer := make([]byte, 4, 4)
|
||||||
|
binary.BigEndian.PutUint32(integer, uint32(i))
|
||||||
|
readers = append(readers, bytes.NewReader(integer))
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := MultiReadSeeker(readers...)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
var integer uint32
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &integer); err != nil {
|
||||||
|
t.Fatalf("Read from NewMultiReadSeeker failed: %v", err)
|
||||||
|
}
|
||||||
|
if uint32(i) != integer {
|
||||||
|
t.Fatalf("Read wrong value from NewMultiReadSeeker: %d != %d", i, integer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,3 +23,32 @@ func MkdirAll(path string, perm os.FileMode) error {
|
||||||
func IsAbs(path string) bool {
|
func IsAbs(path string) bool {
|
||||||
return filepath.IsAbs(path)
|
return filepath.IsAbs(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The functions below here are wrappers for the equivalents in the os package.
|
||||||
|
// They are passthrough on Unix platforms, and only relevant on Windows.
|
||||||
|
|
||||||
|
// CreateSequential creates the named file with mode 0666 (before umask), truncating
|
||||||
|
// it if it already exists. If successful, methods on the returned
|
||||||
|
// File can be used for I/O; the associated file descriptor has mode
|
||||||
|
// O_RDWR.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func CreateSequential(name string) (*os.File, error) {
|
||||||
|
return os.Create(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenSequential opens the named file for reading. If successful, methods on
|
||||||
|
// the returned file can be used for reading; the associated file
|
||||||
|
// descriptor has mode O_RDONLY.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func OpenSequential(name string) (*os.File, error) {
|
||||||
|
return os.Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFileSequential is the generalized open call; most users will use Open
|
||||||
|
// or Create instead. It opens the named file with specified flag
|
||||||
|
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
|
||||||
|
// methods on the returned File can be used for I/O.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
return os.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ func mkdirWithACL(name string) error {
|
||||||
sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
|
sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
|
||||||
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &os.PathError{"mkdir", name, err}
|
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||||
}
|
}
|
||||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||||
sa.InheritHandle = 1
|
sa.InheritHandle = 1
|
||||||
|
@ -106,12 +106,12 @@ func mkdirWithACL(name string) error {
|
||||||
|
|
||||||
namep, err := syscall.UTF16PtrFromString(name)
|
namep, err := syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &os.PathError{"mkdir", name, err}
|
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
e := syscall.CreateDirectory(namep, &sa)
|
e := syscall.CreateDirectory(namep, &sa)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return &os.PathError{"mkdir", name, e}
|
return &os.PathError{Op: "mkdir", Path: name, Err: e}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -131,3 +131,106 @@ func IsAbs(path string) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The origin of the functions below here are the golang OS and syscall packages,
|
||||||
|
// slightly modified to only cope with files, not directories due to the
|
||||||
|
// specific use case.
|
||||||
|
//
|
||||||
|
// The alteration is to allow a file on Windows to be opened with
|
||||||
|
// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating
|
||||||
|
// the standby list, particularly when accessing large files such as layer.tar.
|
||||||
|
|
||||||
|
// CreateSequential creates the named file with mode 0666 (before umask), truncating
|
||||||
|
// it if it already exists. If successful, methods on the returned
|
||||||
|
// File can be used for I/O; the associated file descriptor has mode
|
||||||
|
// O_RDWR.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func CreateSequential(name string) (*os.File, error) {
|
||||||
|
return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenSequential opens the named file for reading. If successful, methods on
|
||||||
|
// the returned file can be used for reading; the associated file
|
||||||
|
// descriptor has mode O_RDONLY.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func OpenSequential(name string) (*os.File, error) {
|
||||||
|
return OpenFileSequential(name, os.O_RDONLY, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFileSequential is the generalized open call; most users will use Open
|
||||||
|
// or Create instead.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) {
|
||||||
|
if name == "" {
|
||||||
|
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
|
||||||
|
}
|
||||||
|
r, errf := syscallOpenFileSequential(name, flag, 0)
|
||||||
|
if errf == nil {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
return nil, &os.PathError{Op: "open", Path: name, Err: errf}
|
||||||
|
}
|
||||||
|
|
||||||
|
func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
|
||||||
|
r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(r), name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInheritSa() *syscall.SecurityAttributes {
|
||||||
|
var sa syscall.SecurityAttributes
|
||||||
|
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||||
|
sa.InheritHandle = 1
|
||||||
|
return &sa
|
||||||
|
}
|
||||||
|
|
||||||
|
func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||||
|
}
|
||||||
|
pathp, err := syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return syscall.InvalidHandle, err
|
||||||
|
}
|
||||||
|
var access uint32
|
||||||
|
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
|
||||||
|
case syscall.O_RDONLY:
|
||||||
|
access = syscall.GENERIC_READ
|
||||||
|
case syscall.O_WRONLY:
|
||||||
|
access = syscall.GENERIC_WRITE
|
||||||
|
case syscall.O_RDWR:
|
||||||
|
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
|
||||||
|
}
|
||||||
|
if mode&syscall.O_CREAT != 0 {
|
||||||
|
access |= syscall.GENERIC_WRITE
|
||||||
|
}
|
||||||
|
if mode&syscall.O_APPEND != 0 {
|
||||||
|
access &^= syscall.GENERIC_WRITE
|
||||||
|
access |= syscall.FILE_APPEND_DATA
|
||||||
|
}
|
||||||
|
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
|
||||||
|
var sa *syscall.SecurityAttributes
|
||||||
|
if mode&syscall.O_CLOEXEC == 0 {
|
||||||
|
sa = makeInheritSa()
|
||||||
|
}
|
||||||
|
var createmode uint32
|
||||||
|
switch {
|
||||||
|
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||||
|
createmode = syscall.CREATE_NEW
|
||||||
|
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
|
||||||
|
createmode = syscall.CREATE_ALWAYS
|
||||||
|
case mode&syscall.O_CREAT == syscall.O_CREAT:
|
||||||
|
createmode = syscall.OPEN_ALWAYS
|
||||||
|
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
|
||||||
|
createmode = syscall.TRUNCATE_EXISTING
|
||||||
|
default:
|
||||||
|
createmode = syscall.OPEN_EXISTING
|
||||||
|
}
|
||||||
|
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
|
||||||
|
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
|
||||||
|
const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
|
||||||
|
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
||||||
|
return h, e
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue