Bump github.com/docker/docker

Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.27-0.20230918140811-29a0e76e6495+incompatible to 24.0.7+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/commits/v24.0.7)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
pull/496/head
dependabot[bot] 7 months ago committed by GitHub
parent 80af149b02
commit 4fce8eb54f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,7 +7,7 @@ require (
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/docker/cli v20.10.15+incompatible
github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible
github.com/docker/docker v24.0.7+incompatible
github.com/fatih/color v1.10.0
github.com/go-errors/errors v1.4.2
github.com/gookit/color v1.5.0

@ -16,8 +16,8 @@ github.com/docker/cli v20.10.15+incompatible h1:HGO75iIgpyuG1m0hw0Kp7hY5o7XELmY1
github.com/docker/cli v20.10.15+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible h1:pgbpJ4kql71BzDNYSe18E489Ni4vn4yQ+0Xj8567Lvc=
github.com/docker/docker v20.10.27-0.20230918140811-29a0e76e6495+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=

File diff suppressed because it is too large Load Diff

@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client.
const (
// DefaultVersion of Current REST API
DefaultVersion = "1.41"
DefaultVersion = "1.43"
// NoBaseImageSpecifier is the symbol used by the FROM
// command to specify that no base image is to be used.

File diff suppressed because it is too large Load Diff

@ -1,22 +1,7 @@
package types // import "github.com/docker/docker/api/types"
import "github.com/docker/docker/api/types/registry"
// AuthConfig contains authorization information for connecting to a Registry
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
// Email is an optional value associated with the username.
// This field is deprecated and will be removed in a later
// version of docker.
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
// IdentityToken is used to authenticate the user and get
// an access token for the registry.
IdentityToken string `json:"identitytoken,omitempty"`
// RegistryToken is a bearer token to be sent to a registry
RegistryToken string `json:"registrytoken,omitempty"`
}
// AuthConfig contains authorization information for connecting to a Registry.
//
// Deprecated: use github.com/docker/docker/api/types/registry.AuthConfig
type AuthConfig = registry.AuthConfig

@ -7,6 +7,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/registry"
units "github.com/docker/go-units"
)
@ -59,7 +60,6 @@ type ContainerExecInspect struct {
// ContainerListOptions holds parameters to list containers with.
type ContainerListOptions struct {
Quiet bool
Size bool
All bool
Latest bool
@ -113,10 +113,16 @@ type NetworkListOptions struct {
Filters filters.Args
}
// NewHijackedResponse intializes a HijackedResponse type
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
Conn net.Conn
Reader *bufio.Reader
mediaType string
Conn net.Conn
Reader *bufio.Reader
}
// Close closes the hijacked connection and reader.
@ -124,6 +130,15 @@ func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
@ -166,7 +181,7 @@ type ImageBuildOptions struct {
// at all (nil). See the parsing of buildArgs in
// api/server/router/build/build_routes.go for even more info.
BuildArgs map[string]*string
AuthConfigs map[string]AuthConfig
AuthConfigs map[string]registry.AuthConfig
Context io.Reader
Labels map[string]string
// squash the resulting image's layers to the parent
@ -236,10 +251,20 @@ type ImageImportOptions struct {
Platform string // Platform is the target platform of the image
}
// ImageListOptions holds parameters to filter the list of images with.
// ImageListOptions holds parameters to list images with.
type ImageListOptions struct {
All bool
// All controls whether all images in the graph are filtered, or just
// the heads.
All bool
// Filters is a JSON-encoded set of filter arguments.
Filters filters.Args
// SharedSize indicates whether the shared size of images should be computed.
SharedSize bool
// ContainerCount indicates whether container count should be computed.
ContainerCount bool
}
// ImageLoadResponse returns information to the client about a load process.

@ -3,7 +3,7 @@ package types // import "github.com/docker/docker/api/types"
import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// configs holds structs used for internal communication between the
@ -16,7 +16,7 @@ type ContainerCreateConfig struct {
Config *container.Config
HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig
Platform *specs.Platform
Platform *ocispec.Platform
AdjustCPUShares bool
}
@ -33,6 +33,7 @@ type ExecConfig struct {
User string // User that will run the command
Privileged bool // Is the container in privileged mode
Tty bool // Attach standard streams to a tty.
ConsoleSize *[2]uint `json:",omitempty"` // Initial console size [height, width]
AttachStdin bool // Attach the standard input, makes possible user interaction
AttachStderr bool // Attach the standard error
AttachStdout bool // Attach the standard output

@ -0,0 +1,6 @@
package container
// ContainerChangeResponseItem change item in response to ContainerChanges operation
//
// Deprecated: use [FilesystemChange].
type ContainerChangeResponseItem = FilesystemChange

@ -0,0 +1,15 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ChangeType Kind of change
//
// Can be one of:
//
// - `0`: Modified ("C")
// - `1`: Added ("A")
// - `2`: Deleted ("D")
//
// swagger:model ChangeType
type ChangeType uint8

@ -0,0 +1,23 @@
package container
const (
// ChangeModify represents the modify operation.
ChangeModify ChangeType = 0
// ChangeAdd represents the add operation.
ChangeAdd ChangeType = 1
// ChangeDelete represents the delete operation.
ChangeDelete ChangeType = 2
)
func (ct ChangeType) String() string {
switch ct {
case ChangeModify:
return "C"
case ChangeAdd:
return "A"
case ChangeDelete:
return "D"
default:
return ""
}
}

@ -1,6 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
import (
"io"
"time"
"github.com/docker/docker/api/types/strslice"
@ -13,6 +14,24 @@ import (
// Docker interprets it as 3 nanoseconds.
const MinimumDuration = 1 * time.Millisecond
// StopOptions holds the options to stop or restart a container.
type StopOptions struct {
// Signal (optional) is the signal to send to the container to (gracefully)
// stop it before forcibly terminating the container with SIGKILL after the
// timeout expires. If not value is set, the default (SIGTERM) is used.
Signal string `json:",omitempty"`
// Timeout (optional) is the timeout (in seconds) to wait for the container
// to stop gracefully before forcibly terminating it with SIGKILL.
//
// - Use nil to use the default timeout (10 seconds).
// - Use '-1' to wait indefinitely.
// - Use '0' to not wait for the container to exit gracefully, and
// immediately proceeds to forcibly terminating the container.
// - Other positive values are used as timeout (in seconds).
Timeout *int `json:",omitempty"`
}
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
type HealthConfig struct {
// Test is the test to perform to check that the container is healthy.
@ -34,6 +53,14 @@ type HealthConfig struct {
Retries int `json:",omitempty"`
}
// ExecStartOptions holds the options to start container's exec.
type ExecStartOptions struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
ConsoleSize *[2]uint `json:",omitempty"`
}
// Config contains the configuration data about a container.
// It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".

@ -1,20 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerChangeResponseItem change item in response to ContainerChanges operation
// swagger:model ContainerChangeResponseItem
type ContainerChangeResponseItem struct {
// Kind of change
// Required: true
Kind uint8 `json:"Kind"`
// Path to file that has changed
// Required: true
Path string `json:"Path"`
}

@ -1,20 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerCreateCreatedBody OK response to ContainerCreate operation
// swagger:model ContainerCreateCreatedBody
type ContainerCreateCreatedBody struct {
// The ID of the created container
// Required: true
ID string `json:"Id"`
// Warnings encountered when creating the container
// Required: true
Warnings []string `json:"Warnings"`
}

@ -1,28 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerWaitOKBodyError container waiting error, if any
// swagger:model ContainerWaitOKBodyError
type ContainerWaitOKBodyError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}
// ContainerWaitOKBody OK response to ContainerWait operation
// swagger:model ContainerWaitOKBody
type ContainerWaitOKBody struct {
// error
// Required: true
Error *ContainerWaitOKBodyError `json:"Error"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

@ -0,0 +1,19 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// CreateResponse ContainerCreateResponse
//
// OK response to ContainerCreate operation
// swagger:model CreateResponse
type CreateResponse struct {
// The ID of the created container
// Required: true
ID string `json:"Id"`
// Warnings encountered when creating the container
// Required: true
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,19 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// FilesystemChange Change in the container's filesystem.
//
// swagger:model FilesystemChange
type FilesystemChange struct {
// kind
// Required: true
Kind ChangeType `json:"Kind"`
// Path to file or directory that has changed.
//
// Required: true
Path string `json:"Path"`
}

@ -13,19 +13,26 @@ import (
// CgroupnsMode represents the cgroup namespace mode of the container
type CgroupnsMode string
// cgroup namespace modes for containers
const (
CgroupnsModeEmpty CgroupnsMode = ""
CgroupnsModePrivate CgroupnsMode = "private"
CgroupnsModeHost CgroupnsMode = "host"
)
// IsPrivate indicates whether the container uses its own private cgroup namespace
func (c CgroupnsMode) IsPrivate() bool {
return c == "private"
return c == CgroupnsModePrivate
}
// IsHost indicates whether the container shares the host's cgroup namespace
func (c CgroupnsMode) IsHost() bool {
return c == "host"
return c == CgroupnsModeHost
}
// IsEmpty indicates whether the container cgroup namespace mode is unset
func (c CgroupnsMode) IsEmpty() bool {
return c == ""
return c == CgroupnsModeEmpty
}
// Valid indicates whether the cgroup namespace mode is valid
@ -37,60 +44,70 @@ func (c CgroupnsMode) Valid() bool {
// values are platform specific
type Isolation string
// Isolation modes for containers
const (
IsolationEmpty Isolation = "" // IsolationEmpty is unspecified (same behavior as default)
IsolationDefault Isolation = "default" // IsolationDefault is the default isolation mode on current daemon
IsolationProcess Isolation = "process" // IsolationProcess is process isolation mode
IsolationHyperV Isolation = "hyperv" // IsolationHyperV is HyperV isolation mode
)
// IsDefault indicates the default isolation technology of a container. On Linux this
// is the native driver. On Windows, this is a Windows Server Container.
func (i Isolation) IsDefault() bool {
return strings.ToLower(string(i)) == "default" || string(i) == ""
// TODO consider making isolation-mode strict (case-sensitive)
v := Isolation(strings.ToLower(string(i)))
return v == IsolationDefault || v == IsolationEmpty
}
// IsHyperV indicates the use of a Hyper-V partition for isolation
func (i Isolation) IsHyperV() bool {
return strings.ToLower(string(i)) == "hyperv"
// TODO consider making isolation-mode strict (case-sensitive)
return Isolation(strings.ToLower(string(i))) == IsolationHyperV
}
// IsProcess indicates the use of process isolation
func (i Isolation) IsProcess() bool {
return strings.ToLower(string(i)) == "process"
// TODO consider making isolation-mode strict (case-sensitive)
return Isolation(strings.ToLower(string(i))) == IsolationProcess
}
const (
// IsolationEmpty is unspecified (same behavior as default)
IsolationEmpty = Isolation("")
// IsolationDefault is the default isolation mode on current daemon
IsolationDefault = Isolation("default")
// IsolationProcess is process isolation mode
IsolationProcess = Isolation("process")
// IsolationHyperV is HyperV isolation mode
IsolationHyperV = Isolation("hyperv")
)
// IpcMode represents the container ipc stack.
type IpcMode string
// IpcMode constants
const (
IPCModeNone IpcMode = "none"
IPCModeHost IpcMode = "host"
IPCModeContainer IpcMode = "container"
IPCModePrivate IpcMode = "private"
IPCModeShareable IpcMode = "shareable"
)
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
func (n IpcMode) IsPrivate() bool {
return n == "private"
return n == IPCModePrivate
}
// IsHost indicates whether the container shares the host's ipc namespace.
func (n IpcMode) IsHost() bool {
return n == "host"
return n == IPCModeHost
}
// IsShareable indicates whether the container's ipc namespace can be shared with another container.
func (n IpcMode) IsShareable() bool {
return n == "shareable"
return n == IPCModeShareable
}
// IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
_, ok := containerID(string(n))
return ok
}
// IsNone indicates whether container IpcMode is set to "none".
func (n IpcMode) IsNone() bool {
return n == "none"
return n == IPCModeNone
}
// IsEmpty indicates whether container IpcMode is empty
@ -100,16 +117,14 @@ func (n IpcMode) IsEmpty() bool {
// Valid indicates whether the ipc mode is valid.
func (n IpcMode) Valid() bool {
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
}
// Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == "container" {
return parts[1]
}
return ""
func (n IpcMode) Container() (idOrName string) {
idOrName, _ = containerID(string(n))
return idOrName
}
// NetworkMode represents the container network stack.
@ -132,17 +147,14 @@ func (n NetworkMode) IsPrivate() bool {
// IsContainer indicates whether container uses a container network stack.
func (n NetworkMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
_, ok := containerID(string(n))
return ok
}
// ConnectedContainer is the id of the container which network this container is connected to.
func (n NetworkMode) ConnectedContainer() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
func (n NetworkMode) ConnectedContainer() (idOrName string) {
idOrName, _ = containerID(string(n))
return idOrName
}
// UserDefined indicates user-created network
@ -163,18 +175,12 @@ func (n UsernsMode) IsHost() bool {
// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
return !(n.IsHost())
return !n.IsHost()
}
// Valid indicates whether the userns is valid.
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
default:
return false
}
return true
return n == "" || n.IsHost()
}
// CgroupSpec represents the cgroup to use for the container.
@ -182,22 +188,20 @@ type CgroupSpec string
// IsContainer indicates whether the container is using another container cgroup
func (c CgroupSpec) IsContainer() bool {
parts := strings.SplitN(string(c), ":", 2)
return len(parts) > 1 && parts[0] == "container"
_, ok := containerID(string(c))
return ok
}
// Valid indicates whether the cgroup spec is valid.
func (c CgroupSpec) Valid() bool {
return c.IsContainer() || c == ""
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
return c == "" || c.IsContainer()
}
// Container returns the name of the container whose cgroup will be used.
func (c CgroupSpec) Container() string {
parts := strings.SplitN(string(c), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
// Container returns the ID or name of the container whose cgroup will be used.
func (c CgroupSpec) Container() (idOrName string) {
idOrName, _ = containerID(string(c))
return idOrName
}
// UTSMode represents the UTS namespace of the container.
@ -205,7 +209,7 @@ type UTSMode string
// IsPrivate indicates whether the container uses its private UTS namespace.
func (n UTSMode) IsPrivate() bool {
return !(n.IsHost())
return !n.IsHost()
}
// IsHost indicates whether the container uses the host's UTS namespace.
@ -215,13 +219,7 @@ func (n UTSMode) IsHost() bool {
// Valid indicates whether the UTS namespace is valid.
func (n UTSMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
default:
return false
}
return true
return n == "" || n.IsHost()
}
// PidMode represents the pid namespace of the container.
@ -239,32 +237,19 @@ func (n PidMode) IsHost() bool {
// IsContainer indicates whether the container uses a container's pid namespace.
func (n PidMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
_, ok := containerID(string(n))
return ok
}
// Valid indicates whether the pid namespace is valid.
func (n PidMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
case "container":
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
return n == "" || n.IsHost() || validContainer(string(n))
}
// Container returns the name of the container whose pid namespace is going to be used.
func (n PidMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
func (n PidMode) Container() (idOrName string) {
idOrName, _ = containerID(string(n))
return idOrName
}
// DeviceRequest represents a request for devices from a device driver.
@ -326,7 +311,7 @@ type LogMode string
// Available logging modes
const (
LogModeUnset = ""
LogModeUnset LogMode = ""
LogModeBlocking LogMode = "blocking"
LogModeNonBlock LogMode = "non-blocking"
)
@ -361,14 +346,17 @@ type Resources struct {
Devices []DeviceMapping // List of devices to map inside the container
DeviceCgroupRules []string // List of rule to be added to the device cgroup
DeviceRequests []DeviceRequest // List of device requests for device drivers
KernelMemory int64 // Kernel memory limit (in bytes), Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes)
MemoryReservation int64 // Memory soft limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
OomKillDisable *bool // Whether to disable OOM Killer or not
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
Ulimits []*units.Ulimit // List of ulimits to be set in the container
// KernelMemory specifies the kernel memory limit (in bytes) for the container.
// Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes.
KernelMemory int64 `json:",omitempty"`
KernelMemoryTCP int64 `json:",omitempty"` // Hard limit for kernel TCP buffer memory (in bytes)
MemoryReservation int64 // Memory soft limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
OomKillDisable *bool // Whether to disable OOM Killer or not
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
Ulimits []*units.Ulimit // List of ulimits to be set in the container
// Applicable to Windows
CPUCount int64 `json:"CpuCount"` // CPU count
@ -390,15 +378,17 @@ type UpdateConfig struct {
// Portable information *should* appear in Config.
type HostConfig struct {
// Applicable to all platforms
Binds []string // List of volume bindings for this container
ContainerIDFile string // File (path) where the containerId is written
LogConfig LogConfig // Configuration of the logs for this container
NetworkMode NetworkMode // Network mode to use for the container
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
RestartPolicy RestartPolicy // Restart policy to be used for the container
AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container
Binds []string // List of volume bindings for this container
ContainerIDFile string // File (path) where the containerId is written
LogConfig LogConfig // Configuration of the logs for this container
NetworkMode NetworkMode // Network mode to use for the container
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
RestartPolicy RestartPolicy // Restart policy to be used for the container
AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container
ConsoleSize [2]uint // Initial console size (height,width)
Annotations map[string]string `json:",omitempty"` // Arbitrary non-identifying metadata attached to container and provided to the runtime
// Applicable to UNIX platforms
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
@ -427,8 +417,7 @@ type HostConfig struct {
Runtime string `json:",omitempty"` // Runtime to use with this container
// Applicable to Windows
ConsoleSize [2]uint // Initial console size (height,width)
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
// Contains container's resources (cgroups, ulimits)
Resources
@ -445,3 +434,23 @@ type HostConfig struct {
// Run a custom init inside the container, if null, use the daemon's configured settings
Init *bool `json:",omitempty"`
}
// containerID splits "container:<ID|name>" values. It returns the container
// ID or name, and whether an ID/name was found. It returns an empty string and
// a "false" if the value does not have a "container:" prefix. Further validation
// of the returned, including checking if the value is empty, should be handled
// by the caller.
func containerID(val string) (idOrName string, ok bool) {
k, v, hasSep := strings.Cut(val, ":")
if !hasSep || k != "container" {
return "", false
}
return v, true
}
// validContainer checks if the given value is a "container:" mode with
// a non-empty name/ID.
func validContainer(val string) bool {
id, ok := containerID(val)
return ok && id != ""
}

@ -0,0 +1,12 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// WaitExitError container waiting error, if any
// swagger:model WaitExitError
type WaitExitError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}

@ -0,0 +1,18 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// WaitResponse ContainerWaitResponse
//
// OK response to ContainerWait operation
// swagger:model WaitResponse
type WaitResponse struct {
// error
Error *WaitExitError `json:"Error,omitempty"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

@ -1,33 +1,26 @@
package events // import "github.com/docker/docker/api/types/events"
// Type is used for event-types.
type Type = string
// List of known event types.
const (
// BuilderEventType is the event type that the builder generates
BuilderEventType = "builder"
// ContainerEventType is the event type that containers generate
ContainerEventType = "container"
// DaemonEventType is the event type that daemon generate
DaemonEventType = "daemon"
// ImageEventType is the event type that images generate
ImageEventType = "image"
// NetworkEventType is the event type that networks generate
NetworkEventType = "network"
// PluginEventType is the event type that plugins generate
PluginEventType = "plugin"
// VolumeEventType is the event type that volumes generate
VolumeEventType = "volume"
// ServiceEventType is the event type that services generate
ServiceEventType = "service"
// NodeEventType is the event type that nodes generate
NodeEventType = "node"
// SecretEventType is the event type that secrets generate
SecretEventType = "secret"
// ConfigEventType is the event type that configs generate
ConfigEventType = "config"
BuilderEventType Type = "builder" // BuilderEventType is the event type that the builder generates.
ConfigEventType Type = "config" // ConfigEventType is the event type that configs generate.
ContainerEventType Type = "container" // ContainerEventType is the event type that containers generate.
DaemonEventType Type = "daemon" // DaemonEventType is the event type that daemon generate.
ImageEventType Type = "image" // ImageEventType is the event type that images generate.
NetworkEventType Type = "network" // NetworkEventType is the event type that networks generate.
NodeEventType Type = "node" // NodeEventType is the event type that nodes generate.
PluginEventType Type = "plugin" // PluginEventType is the event type that plugins generate.
SecretEventType Type = "secret" // SecretEventType is the event type that secrets generate.
ServiceEventType Type = "service" // ServiceEventType is the event type that services generate.
VolumeEventType Type = "volume" // VolumeEventType is the event type that volumes generate.
)
// Actor describes something that generates events,
// like a container, or a network, or a volume.
// It has a defined name and a set or attributes.
// It has a defined name and a set of attributes.
// The container attributes are its labels, other actors
// can generate these attributes from other properties.
type Actor struct {
@ -39,11 +32,11 @@ type Actor struct {
type Message struct {
// Deprecated information from JSONMessage.
// With data only in container events.
Status string `json:"status,omitempty"`
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
Status string `json:"status,omitempty"` // Deprecated: use Action instead.
ID string `json:"id,omitempty"` // Deprecated: use Actor.ID instead.
From string `json:"from,omitempty"` // Deprecated: use Actor.Attributes["image"] instead.
Type string
Type Type
Action string
Actor Actor
// Engine events are local scope. Cluster events are swarm scope.

@ -0,0 +1,37 @@
package filters
import "fmt"
// invalidFilter indicates that the provided filter or its value is invalid
type invalidFilter struct {
Filter string
Value []string
}
func (e invalidFilter) Error() string {
msg := "invalid filter"
if e.Filter != "" {
msg += " '" + e.Filter
if e.Value != nil {
msg = fmt.Sprintf("%s=%s", msg, e.Value)
}
msg += "'"
}
return msg
}
// InvalidParameter marks this error as ErrInvalidParameter
func (e invalidFilter) InvalidParameter() {}
// unreachableCode is an error indicating that the code path was not expected to be reached.
type unreachableCode struct {
Filter string
Value []string
}
// System marks this error as ErrSystem
func (e unreachableCode) System() {}
func (e unreachableCode) Error() string {
return fmt.Sprintf("unreachable code reached for filter: %q with values: %s", e.Filter, e.Value)
}

@ -98,7 +98,7 @@ func FromJSON(p string) (Args, error) {
// Fallback to parsing arguments in the legacy slice format
deprecated := map[string][]string{}
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
return args, err
return args, &invalidFilter{}
}
args.fields = deprecatedArgs(deprecated)
@ -162,13 +162,13 @@ func (args Args) MatchKVList(key string, sources map[string]string) bool {
}
for value := range fieldValues {
testKV := strings.SplitN(value, "=", 2)
testK, testV, hasValue := strings.Cut(value, "=")
v, ok := sources[testKV[0]]
v, ok := sources[testK]
if !ok {
return false
}
if len(testKV) == 2 && testKV[1] != v {
if hasValue && testV != v {
return false
}
}
@ -195,6 +195,38 @@ func (args Args) Match(field, source string) bool {
return false
}
// GetBoolOrDefault returns a boolean value of the key if the key is present
// and is intepretable as a boolean value. Otherwise the default value is returned.
// Error is not nil only if the filter values are not valid boolean or are conflicting.
func (args Args) GetBoolOrDefault(key string, defaultValue bool) (bool, error) {
fieldValues, ok := args.fields[key]
if !ok {
return defaultValue, nil
}
if len(fieldValues) == 0 {
return defaultValue, &invalidFilter{key, nil}
}
isFalse := fieldValues["0"] || fieldValues["false"]
isTrue := fieldValues["1"] || fieldValues["true"]
conflicting := isFalse && isTrue
invalid := !isFalse && !isTrue
if conflicting || invalid {
return defaultValue, &invalidFilter{key, args.Get(key)}
} else if isFalse {
return false, nil
} else if isTrue {
return true, nil
}
// This code shouldn't be reached.
return defaultValue, &unreachableCode{Filter: key, Value: args.Get(key)}
}
// ExactMatch returns true if the source matches exactly one of the values.
func (args Args) ExactMatch(key, source string) bool {
fieldValues, ok := args.fields[key]
@ -245,20 +277,12 @@ func (args Args) Contains(field string) bool {
return ok
}
type invalidFilter string
func (e invalidFilter) Error() string {
return "Invalid filter '" + string(e) + "'"
}
func (invalidFilter) InvalidParameter() {}
// Validate compared the set of accepted keys against the keys in the mapping.
// An error is returned if any mapping keys are not in the accepted set.
func (args Args) Validate(accepted map[string]bool) error {
for name := range args.fields {
if !accepted[name] {
return invalidFilter(name)
return &invalidFilter{name, nil}
}
}
return nil

@ -3,15 +3,21 @@ package types
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// GraphDriverData Information about a container's graph driver.
// GraphDriverData Information about the storage driver used to store the container's and
// image's filesystem.
//
// swagger:model GraphDriverData
type GraphDriverData struct {
// data
// Low-level storage metadata, provided as key/value pairs.
//
// This information is driver-specific, and depends on the storage-driver
// in use, and should be used for informational purposes only.
//
// Required: true
Data map[string]string `json:"Data"`
// name
// Name of the storage driver.
// Required: true
Name string `json:"Name"`
}

@ -0,0 +1,9 @@
package image
import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
// GetImageOpts holds parameters to inspect an image.
type GetImageOpts struct {
Platform *ocispec.Platform
Details bool
}

@ -7,43 +7,88 @@ package types
// swagger:model ImageSummary
type ImageSummary struct {
// containers
// Number of containers using this image. Includes both stopped and running
// containers.
//
// This size is not calculated by default, and depends on which API endpoint
// is used. `-1` indicates that the value has not been set / calculated.
//
// Required: true
Containers int64 `json:"Containers"`
// created
// Date and time at which the image was created as a Unix timestamp
// (number of seconds sinds EPOCH).
//
// Required: true
Created int64 `json:"Created"`
// Id
// ID is the content-addressable ID of an image.
//
// This identifier is a content-addressable digest calculated from the
// image's configuration (which includes the digests of layers used by
// the image).
//
// Note that this digest differs from the `RepoDigests` below, which
// holds digests of image manifests that reference the image.
//
// Required: true
ID string `json:"Id"`
// labels
// User-defined key/value metadata.
// Required: true
Labels map[string]string `json:"Labels"`
// parent Id
// ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is empty if the image was pulled from an image registry.
//
// Required: true
ParentID string `json:"ParentId"`
// repo digests
// List of content-addressable digests of locally available image manifests
// that the image is referenced from. Multiple manifests can refer to the
// same image.
//
// These digests are usually only available if the image was either pulled
// from a registry, or if the image was pushed to a registry, which is when
// the manifest is generated and its digest calculated.
//
// Required: true
RepoDigests []string `json:"RepoDigests"`
// repo tags
// List of image names/tags in the local image cache that reference this
// image.
//
// Multiple image tags can refer to the same image, and this list may be
// empty if no tags reference the image, in which case the image is
// "untagged", in which case it can still be referenced by its ID.
//
// Required: true
RepoTags []string `json:"RepoTags"`
// shared size
// Total size of image layers that are shared between this image and other
// images.
//
// This size is not calculated by default. `-1` indicates that the value
// has not been set / calculated.
//
// Required: true
SharedSize int64 `json:"SharedSize"`
// size
// Total size of the image including all layers it is composed of.
//
// Required: true
Size int64 `json:"Size"`
// virtual size
// Required: true
VirtualSize int64 `json:"VirtualSize"`
// Total size of the image including all layers it is composed of.
//
// In versions of Docker before v1.10, this field was calculated from
// the image itself and all of its parent images. Images are now stored
// self-contained, and no longer use a parent-chain, making this field
// an equivalent of the Size field.
//
// Deprecated: this field is kept for backward compatibility, and will be removed in API v1.44.
VirtualSize int64 `json:"VirtualSize,omitempty"`
}

@ -17,6 +17,8 @@ const (
TypeTmpfs Type = "tmpfs"
// TypeNamedPipe is the type for mounting Windows named pipes
TypeNamedPipe Type = "npipe"
// TypeCluster is the type for Swarm Cluster Volumes.
TypeCluster Type = "cluster"
)
// Mount represents a mount (volume).
@ -30,9 +32,10 @@ type Mount struct {
ReadOnly bool `json:",omitempty"`
Consistency Consistency `json:",omitempty"`
BindOptions *BindOptions `json:",omitempty"`
VolumeOptions *VolumeOptions `json:",omitempty"`
TmpfsOptions *TmpfsOptions `json:",omitempty"`
BindOptions *BindOptions `json:",omitempty"`
VolumeOptions *VolumeOptions `json:",omitempty"`
TmpfsOptions *TmpfsOptions `json:",omitempty"`
ClusterOptions *ClusterOptions `json:",omitempty"`
}
// Propagation represents the propagation of a mount.
@ -79,8 +82,9 @@ const (
// BindOptions defines options specific to mounts of type "bind".
type BindOptions struct {
Propagation Propagation `json:",omitempty"`
NonRecursive bool `json:",omitempty"`
Propagation Propagation `json:",omitempty"`
NonRecursive bool `json:",omitempty"`
CreateMountpoint bool `json:",omitempty"`
}
// VolumeOptions represents the options for a mount of type volume.
@ -129,3 +133,8 @@ type TmpfsOptions struct {
// Some of these may be straightforward to add, but others, such as
// uid/gid have implications in a clustered system.
}
// ClusterOptions specifies options for a Cluster volume.
type ClusterOptions struct {
// intentionally empty
}

@ -0,0 +1,99 @@
package registry // import "github.com/docker/docker/api/types/registry"
import (
"encoding/base64"
"encoding/json"
"io"
"strings"
"github.com/pkg/errors"
)
// AuthHeader is the name of the header used to send encoded registry
// authorization credentials for registry operations (push/pull).
const AuthHeader = "X-Registry-Auth"
// AuthConfig contains authorization information for connecting to a Registry.
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
// Email is an optional value associated with the username.
// This field is deprecated and will be removed in a later
// version of docker.
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
// IdentityToken is used to authenticate the user and get
// an access token for the registry.
IdentityToken string `json:"identitytoken,omitempty"`
// RegistryToken is a bearer token to be sent to a registry
RegistryToken string `json:"registrytoken,omitempty"`
}
// EncodeAuthConfig serializes the auth configuration as a base64url encoded
// RFC4648, section 5) JSON string for sending through the X-Registry-Auth header.
//
// For details on base64url encoding, see:
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
func EncodeAuthConfig(authConfig AuthConfig) (string, error) {
buf, err := json.Marshal(authConfig)
if err != nil {
return "", errInvalidParameter{err}
}
return base64.URLEncoding.EncodeToString(buf), nil
}
// DecodeAuthConfig decodes base64url encoded (RFC4648, section 5) JSON
// authentication information as sent through the X-Registry-Auth header.
//
// This function always returns an AuthConfig, even if an error occurs. It is up
// to the caller to decide if authentication is required, and if the error can
// be ignored.
//
// For details on base64url encoding, see:
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
func DecodeAuthConfig(authEncoded string) (*AuthConfig, error) {
if authEncoded == "" {
return &AuthConfig{}, nil
}
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
return decodeAuthConfigFromReader(authJSON)
}
// DecodeAuthConfigBody decodes authentication information as sent as JSON in the
// body of a request. This function is to provide backward compatibility with old
// clients and API versions. Current clients and API versions expect authentication
// to be provided through the X-Registry-Auth header.
//
// Like DecodeAuthConfig, this function always returns an AuthConfig, even if an
// error occurs. It is up to the caller to decide if authentication is required,
// and if the error can be ignored.
func DecodeAuthConfigBody(rdr io.ReadCloser) (*AuthConfig, error) {
return decodeAuthConfigFromReader(rdr)
}
func decodeAuthConfigFromReader(rdr io.Reader) (*AuthConfig, error) {
authConfig := &AuthConfig{}
if err := json.NewDecoder(rdr).Decode(authConfig); err != nil {
// always return an (empty) AuthConfig to increase compatibility with
// the existing API.
return &AuthConfig{}, invalid(err)
}
return authConfig, nil
}
func invalid(err error) error {
return errInvalidParameter{errors.Wrap(err, "invalid X-Registry-Auth header")}
}
type errInvalidParameter struct{ error }
func (errInvalidParameter) InvalidParameter() {}
func (e errInvalidParameter) Cause() error { return e.error }
func (e errInvalidParameter) Unwrap() error { return e.error }

@ -4,7 +4,7 @@ import (
"encoding/json"
"net"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ServiceConfig stores daemon registry services configuration.
@ -113,8 +113,8 @@ type SearchResults struct {
type DistributionInspect struct {
// Descriptor contains information about the manifest, including
// the content addressable digest
Descriptor v1.Descriptor
Descriptor ocispec.Descriptor
// Platforms contains the list of platforms supported by the image,
// obtained by parsing the manifest
Platforms []v1.Platform
Platforms []ocispec.Platform
}

@ -1,12 +1,20 @@
package swarm // import "github.com/docker/docker/api/types/swarm"
import "time"
import (
"strconv"
"time"
)
// Version represents the internal object version.
type Version struct {
Index uint64 `json:",omitempty"`
}
// String implements fmt.Stringer interface.
func (v Version) String() string {
return strconv.FormatUint(v.Index, 10)
}
// Meta is a base object inherited by most of the other once.
type Meta struct {
Version Version `json:",omitempty"`

@ -53,6 +53,7 @@ type NodeDescription struct {
Resources Resources `json:",omitempty"`
Engine EngineDescription `json:",omitempty"`
TLSInfo TLSInfo `json:",omitempty"`
CSIInfo []NodeCSIInfo `json:",omitempty"`
}
// Platform represents the platform (Arch/OS).
@ -68,6 +69,21 @@ type EngineDescription struct {
Plugins []PluginDescription `json:",omitempty"`
}
// NodeCSIInfo represents information about a CSI plugin available on the node
type NodeCSIInfo struct {
// PluginName is the name of the CSI plugin.
PluginName string `json:",omitempty"`
// NodeID is the ID of the node as reported by the CSI plugin. This is
// different from the swarm node ID.
NodeID string `json:",omitempty"`
// MaxVolumesPerNode is the maximum number of volumes that may be published
// to this node
MaxVolumesPerNode int64 `json:",omitempty"`
// AccessibleTopology indicates the location of this node in the CSI
// plugin's topology
AccessibleTopology *Topology `json:",omitempty"`
}
// PluginDescription represents the description of an engine plugin.
type PluginDescription struct {
Type string `json:",omitempty"`
@ -113,3 +129,11 @@ const (
// NodeStateDisconnected DISCONNECTED
NodeStateDisconnected NodeState = "disconnected"
)
// Topology defines the CSI topology of this node. This type is a duplicate of
// github.com/docker/docker/api/types.Topology. Because the type definition
// is so simple and to avoid complicated structure or circular imports, we just
// duplicate it here. See that type for full documentation
type Topology struct {
Segments map[string]string `json:",omitempty"`
}

@ -213,6 +213,16 @@ type Info struct {
Warnings []string `json:",omitempty"`
}
// Status provides information about the current swarm status and role,
// obtained from the "Swarm" header in the API response.
type Status struct {
// NodeState represents the state of the node.
NodeState LocalNodeState
// ControlAvailable indicates if the node is a swarm manager.
ControlAvailable bool
}
// Peer represents a peer.
type Peer struct {
NodeID string

@ -62,6 +62,11 @@ type Task struct {
// used to determine which Tasks belong to which run of the job. This field
// is absent if the Service mode is Replicated or Global.
JobIteration *Version `json:",omitempty"`
// Volumes is the list of VolumeAttachments for this task. It specifies
// which particular volumes are to be used by this particular task, and
// fulfilling what mounts in the spec.
Volumes []VolumeAttachment
}
// TaskSpec represents the spec of a task.
@ -204,3 +209,17 @@ type ContainerStatus struct {
type PortStatus struct {
Ports []PortConfig `json:",omitempty"`
}
// VolumeAttachment contains the associating a Volume to a Task.
type VolumeAttachment struct {
// ID is the Swarmkit ID of the Volume. This is not the CSI VolumeId.
ID string `json:",omitempty"`
// Source, together with Target, indicates the Mount, as specified in the
// ContainerSpec, that this volume fulfills.
Source string `json:",omitempty"`
// Target, together with Source, indicates the Mount, as specified
// in the ContainerSpec, that this volume fulfills.
Target string `json:",omitempty"`
}

@ -1,12 +0,0 @@
package time // import "github.com/docker/docker/api/types/time"
import (
"strconv"
"time"
)
// DurationToSecondsString converts the specified duration to the number
// seconds it represents, formatted as a string.
func DurationToSecondsString(duration time.Duration) string {
return strconv.FormatFloat(duration.Seconds(), 'f', 0, 64)
}

@ -95,37 +95,37 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
}
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
// converted to nanoseconds. The expectation is that the seconds and
// seconds will be used to create a time variable. For example:
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has
// the format ("%d.%09d", time.Unix(), int64(time.Nanosecond())).
// If the incoming nanosecond portion is longer than 9 digits it is truncated.
// The expectation is that the seconds and nanoseconds will be used to create a
// time variable. For example:
//
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
// if err == nil since := time.Unix(seconds, nanoseconds)
// seconds, nanoseconds, _ := ParseTimestamp("1136073600.000000001",0)
// since := time.Unix(seconds, nanoseconds)
//
// returns seconds as def(aultSeconds) if value == ""
func ParseTimestamps(value string, def int64) (int64, int64, error) {
// returns seconds as defaultSeconds if value == ""
func ParseTimestamps(value string, defaultSeconds int64) (seconds int64, nanoseconds int64, err error) {
if value == "" {
return def, 0, nil
return defaultSeconds, 0, nil
}
return parseTimestamp(value)
}
func parseTimestamp(value string) (int64, int64, error) {
sa := strings.SplitN(value, ".", 2)
s, err := strconv.ParseInt(sa[0], 10, 64)
func parseTimestamp(value string) (sec int64, nsec int64, err error) {
s, n, ok := strings.Cut(value, ".")
sec, err = strconv.ParseInt(s, 10, 64)
if err != nil {
return s, 0, err
return sec, 0, err
}
if len(sa) != 2 {
return s, 0, nil
if !ok {
return sec, 0, nil
}
n, err := strconv.ParseInt(sa[1], 10, 64)
nsec, err = strconv.ParseInt(n, 10, 64)
if err != nil {
return s, n, err
return sec, nsec, err
}
// should already be in nanoseconds but just in case convert n to nanoseconds
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
return s, n, nil
nsec = int64(float64(nsec) * math.Pow(float64(10), float64(9-len(n))))
return sec, nsec, nil
}

@ -14,43 +14,135 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/volume"
"github.com/docker/go-connections/nat"
)
const (
// MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams
MediaTypeRawStream = "application/vnd.docker.raw-stream"
// MediaTypeMultiplexedStream is vendor specific MIME-Type set for stdin/stdout/stderr multiplexed streams
MediaTypeMultiplexedStream = "application/vnd.docker.multiplexed-stream"
)
// RootFS returns Image's RootFS description including the layer IDs.
type RootFS struct {
Type string
Layers []string `json:",omitempty"`
BaseLayer string `json:",omitempty"`
Type string `json:",omitempty"`
Layers []string `json:",omitempty"`
}
// ImageInspect contains response of Engine API:
// GET "/images/{name:.*}/json"
type ImageInspect struct {
ID string `json:"Id"`
RepoTags []string
RepoDigests []string
Parent string
Comment string
Created string
Container string
// ID is the content-addressable ID of an image.
//
// This identifier is a content-addressable digest calculated from the
// image's configuration (which includes the digests of layers used by
// the image).
//
// Note that this digest differs from the `RepoDigests` below, which
// holds digests of image manifests that reference the image.
ID string `json:"Id"`
// RepoTags is a list of image names/tags in the local image cache that
// reference this image.
//
// Multiple image tags can refer to the same image, and this list may be
// empty if no tags reference the image, in which case the image is
// "untagged", in which case it can still be referenced by its ID.
RepoTags []string
// RepoDigests is a list of content-addressable digests of locally available
// image manifests that the image is referenced from. Multiple manifests can
// refer to the same image.
//
// These digests are usually only available if the image was either pulled
// from a registry, or if the image was pushed to a registry, which is when
// the manifest is generated and its digest calculated.
RepoDigests []string
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is empty if the image was pulled from an image registry.
Parent string
// Comment is an optional message that can be set when committing or
// importing the image.
Comment string
// Created is the date and time at which the image was created, formatted in
// RFC 3339 nano-seconds (time.RFC3339Nano).
Created string
// Container is the ID of the container that was used to create the image.
//
// Depending on how the image was created, this field may be empty.
Container string
// ContainerConfig is an optional field containing the configuration of the
// container that was last committed when creating the image.
//
// Previous versions of Docker builder used this field to store build cache,
// and it is not in active use anymore.
ContainerConfig *container.Config
DockerVersion string
Author string
Config *container.Config
Architecture string
Variant string `json:",omitempty"`
Os string
OsVersion string `json:",omitempty"`
Size int64
VirtualSize int64
GraphDriver GraphDriverData
RootFS RootFS
Metadata ImageMetadata
// DockerVersion is the version of Docker that was used to build the image.
//
// Depending on how the image was created, this field may be empty.
DockerVersion string
// Author is the name of the author that was specified when committing the
// image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
Author string
Config *container.Config
// Architecture is the hardware CPU architecture that the image runs on.
Architecture string
// Variant is the CPU architecture variant (presently ARM-only).
Variant string `json:",omitempty"`
// OS is the Operating System the image is built to run on.
Os string
// OsVersion is the version of the Operating System the image is built to
// run on (especially for Windows).
OsVersion string `json:",omitempty"`
// Size is the total size of the image including all layers it is composed of.
Size int64
// VirtualSize is the total size of the image including all layers it is
// composed of.
//
// In versions of Docker before v1.10, this field was calculated from
// the image itself and all of its parent images. Docker v1.10 and up
// store images self-contained, and no longer use a parent-chain, making
// this field an equivalent of the Size field.
//
// Deprecated: Unused in API 1.43 and up, but kept for backward compatibility with older API versions.
VirtualSize int64 `json:"VirtualSize,omitempty"`
// GraphDriver holds information about the storage driver used to store the
// container's and image's filesystem.
GraphDriver GraphDriverData
// RootFS contains information about the image's RootFS, including the
// layer IDs.
RootFS RootFS
// Metadata of the image in the local cache.
//
// This information is local to the daemon, and not part of the image itself.
Metadata ImageMetadata
}
// ImageMetadata contains engine-local data about the image
type ImageMetadata struct {
// LastTagTime is the date and time at which the image was last tagged.
LastTagTime time.Time `json:",omitempty"`
}
@ -107,6 +199,15 @@ type Ping struct {
OSType string
Experimental bool
BuilderVersion BuilderVersion
// SwarmStatus provides information about the current swarm status of the
// engine, obtained from the "Swarm" header in the API response.
//
// It can be a nil struct if the API version does not provide this header
// in the ping response, or if an error occurred, in which case the client
// should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
}
// ComponentVersion describes the version information for a specific component.
@ -158,8 +259,8 @@ type Info struct {
Plugins PluginsInfo
MemoryLimit bool
SwapLimit bool
KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP bool
KernelMemory bool `json:",omitempty"` // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP bool `json:",omitempty"` // KernelMemoryTCP is not supported on cgroups v2.
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
@ -195,8 +296,6 @@ type Info struct {
Labels []string
ExperimentalBuild bool
ServerVersion string
ClusterStore string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
ClusterAdvertise string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
Runtimes map[string]Runtime
DefaultRuntime string
Swarm swarm.Info
@ -212,7 +311,12 @@ type Info struct {
SecurityOptions []string
ProductLicense string `json:",omitempty"`
DefaultAddressPools []NetworkAddressPool `json:",omitempty"`
Warnings []string
// Warnings contains a slice of warnings that occurred while collecting
// system information. These warnings are intended to be informational
// messages for the user, and are not intended to be parsed / used for
// other purposes, as they do not have a fixed format.
Warnings []string
}
// KeyValue holds a key/value pair
@ -243,20 +347,19 @@ func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) {
continue
}
secopt := SecurityOpt{}
split := strings.Split(opt, ",")
for _, s := range split {
kv := strings.SplitN(s, "=", 2)
if len(kv) != 2 {
for _, s := range strings.Split(opt, ",") {
k, v, ok := strings.Cut(s, "=")
if !ok {
return nil, fmt.Errorf("invalid security option %q", s)
}
if kv[0] == "" || kv[1] == "" {
if k == "" || v == "" {
return nil, errors.New("invalid empty security option")
}
if kv[0] == "name" {
secopt.Name = kv[1]
if k == "name" {
secopt.Name = v
continue
}
secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]})
secopt.Options = append(secopt.Options, KeyValue{Key: k, Value: v})
}
so = append(so, secopt)
}
@ -283,6 +386,8 @@ type ExecStartCheck struct {
Detach bool
// Check if there's a tty
Tty bool
// Terminal size [height, width], unused if Tty == false
ConsoleSize *[2]uint `json:",omitempty"`
}
// HealthcheckResult stores information about a single run of a healthcheck probe
@ -416,13 +521,44 @@ type DefaultNetworkSettings struct {
// MountPoint represents a mount point configuration inside the container.
// This is used for reporting the mountpoints in use by a container.
type MountPoint struct {
Type mount.Type `json:",omitempty"`
Name string `json:",omitempty"`
Source string
// Type is the type of mount, see `Type<foo>` definitions in
// github.com/docker/docker/api/types/mount.Type
Type mount.Type `json:",omitempty"`
// Name is the name reference to the underlying data defined by `Source`
// e.g., the volume name.
Name string `json:",omitempty"`
// Source is the source location of the mount.
//
// For volumes, this contains the storage location of the volume (within
// `/var/lib/docker/volumes/`). For bind-mounts, and `npipe`, this contains
// the source (host) part of the bind-mount. For `tmpfs` mount points, this
// field is empty.
Source string
// Destination is the path relative to the container root (`/`) where the
// Source is mounted inside the container.
Destination string
Driver string `json:",omitempty"`
Mode string
RW bool
// Driver is the volume driver used to create the volume (if it is a volume).
Driver string `json:",omitempty"`
// Mode is a comma separated list of options supplied by the user when
// creating the bind/volume mount.
//
// The default is platform-specific (`"z"` on Linux, empty on Windows).
Mode string
// RW indicates whether the mount is mounted writable (read-write).
RW bool
// Propagation describes how mounts are propagated from the host into the
// mount point, and vice-versa. Refer to the Linux kernel documentation
// for details:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
//
// This field is not used on Windows.
Propagation mount.Propagation
}
@ -516,12 +652,18 @@ type Checkpoint struct {
// Runtime describes an OCI runtime
type Runtime struct {
Path string `json:"path"`
// "Legacy" runtime configuration for runc-compatible runtimes.
Path string `json:"path,omitempty"`
Args []string `json:"runtimeArgs,omitempty"`
// Shimv2 runtime configuration. Mutually exclusive with the legacy config above.
Type string `json:"runtimeType,omitempty"`
Options map[string]interface{} `json:"options,omitempty"`
// This is exposed here only for internal use
// It is not currently supported to specify custom shim configs
Shim *ShimConfig `json:"-"`
ShimConfig *ShimConfig `json:"-"`
}
// ShimConfig is used by runtime to configure containerd shims
@ -530,15 +672,36 @@ type ShimConfig struct {
Opts interface{}
}
// DiskUsageObject represents an object type used for disk usage query filtering.
type DiskUsageObject string
const (
// ContainerObject represents a container DiskUsageObject.
ContainerObject DiskUsageObject = "container"
// ImageObject represents an image DiskUsageObject.
ImageObject DiskUsageObject = "image"
// VolumeObject represents a volume DiskUsageObject.
VolumeObject DiskUsageObject = "volume"
// BuildCacheObject represents a build-cache DiskUsageObject.
BuildCacheObject DiskUsageObject = "build-cache"
)
// DiskUsageOptions holds parameters for system disk usage query.
type DiskUsageOptions struct {
// Types specifies what object types to include in the response. If empty,
// all object types are returned.
Types []DiskUsageObject
}
// DiskUsage contains response of Engine API:
// GET "/system/df"
type DiskUsage struct {
LayersSize int64
Images []*ImageSummary
Containers []*Container
Volumes []*Volume
Volumes []*volume.Volume
BuildCache []*BuildCache
BuilderSize int64 // deprecated
BuilderSize int64 `json:",omitempty"` // Deprecated: deprecated in API 1.38, and no longer used since API 1.40.
}
// ContainersPruneReport contains the response for Engine API:
@ -613,18 +776,31 @@ type BuildResult struct {
ID string
}
// BuildCache contains information about a build cache record
// BuildCache contains information about a build cache record.
type BuildCache struct {
ID string
Parent string
Type string
// ID is the unique ID of the build cache record.
ID string
// Parent is the ID of the parent build cache record.
//
// Deprecated: deprecated in API v1.42 and up, as it was deprecated in BuildKit; use Parents instead.
Parent string `json:"Parent,omitempty"`
// Parents is the list of parent build cache record IDs.
Parents []string `json:" Parents,omitempty"`
// Type is the cache record type.
Type string
// Description is a description of the build-step that produced the build cache.
Description string
InUse bool
Shared bool
Size int64
CreatedAt time.Time
LastUsedAt *time.Time
UsageCount int
// InUse indicates if the build cache is in use.
InUse bool
// Shared indicates if the build cache is shared.
Shared bool
// Size is the amount of disk space used by the build cache (in bytes).
Size int64
// CreatedAt is the date and time at which the build cache was created.
CreatedAt time.Time
// LastUsedAt is the date and time at which the build cache was last used.
LastUsedAt *time.Time
UsageCount int
}
// BuildCachePruneOptions hold parameters to prune the build cache

@ -8,16 +8,19 @@ import (
// compare compares two version strings
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
func compare(v1, v2 string) int {
if v1 == v2 {
return 0
}
var (
currTab = strings.Split(v1, ".")
otherTab = strings.Split(v2, ".")
)
max := len(currTab)
if len(otherTab) > max {
max = len(otherTab)
maxVer := len(currTab)
if len(otherTab) > maxVer {
maxVer = len(otherTab)
}
for i := 0; i < max; i++ {
for i := 0; i < maxVer; i++ {
var currInt, otherInt int
if len(currTab) > i {

@ -0,0 +1,420 @@
package volume
import (
"github.com/docker/docker/api/types/swarm"
)
// ClusterVolume contains options and information specific to, and only present
// on, Swarm CSI cluster volumes.
type ClusterVolume struct {
// ID is the Swarm ID of the volume. Because cluster volumes are Swarm
// objects, they have an ID, unlike non-cluster volumes, which only have a
// Name. This ID can be used to refer to the cluster volume.
ID string
// Meta is the swarm metadata about this volume.
swarm.Meta
// Spec is the cluster-specific options from which this volume is derived.
Spec ClusterVolumeSpec
// PublishStatus contains the status of the volume as it pertains to its
// publishing on Nodes.
PublishStatus []*PublishStatus `json:",omitempty"`
// Info is information about the global status of the volume.
Info *Info `json:",omitempty"`
}
// ClusterVolumeSpec contains the spec used to create this volume.
type ClusterVolumeSpec struct {
// Group defines the volume group of this volume. Volumes belonging to the
// same group can be referred to by group name when creating Services.
// Referring to a volume by group instructs swarm to treat volumes in that
// group interchangeably for the purpose of scheduling. Volumes with an
// empty string for a group technically all belong to the same, emptystring
// group.
Group string `json:",omitempty"`
// AccessMode defines how the volume is used by tasks.
AccessMode *AccessMode `json:",omitempty"`
// AccessibilityRequirements specifies where in the cluster a volume must
// be accessible from.
//
// This field must be empty if the plugin does not support
// VOLUME_ACCESSIBILITY_CONSTRAINTS capabilities. If it is present but the
// plugin does not support it, volume will not be created.
//
// If AccessibilityRequirements is empty, but the plugin does support
// VOLUME_ACCESSIBILITY_CONSTRAINTS, then Swarmkit will assume the entire
// cluster is a valid target for the volume.
AccessibilityRequirements *TopologyRequirement `json:",omitempty"`
// CapacityRange defines the desired capacity that the volume should be
// created with. If nil, the plugin will decide the capacity.
CapacityRange *CapacityRange `json:",omitempty"`
// Secrets defines Swarm Secrets that are passed to the CSI storage plugin
// when operating on this volume.
Secrets []Secret `json:",omitempty"`
// Availability is the Volume's desired availability. Analogous to Node
// Availability, this allows the user to take volumes offline in order to
// update or delete them.
Availability Availability `json:",omitempty"`
}
// Availability specifies the availability of the volume.
type Availability string
const (
// AvailabilityActive indicates that the volume is active and fully
// schedulable on the cluster.
AvailabilityActive Availability = "active"
// AvailabilityPause indicates that no new workloads should use the
// volume, but existing workloads can continue to use it.
AvailabilityPause Availability = "pause"
// AvailabilityDrain indicates that all workloads using this volume
// should be rescheduled, and the volume unpublished from all nodes.
AvailabilityDrain Availability = "drain"
)
// AccessMode defines the access mode of a volume.
type AccessMode struct {
// Scope defines the set of nodes this volume can be used on at one time.
Scope Scope `json:",omitempty"`
// Sharing defines the number and way that different tasks can use this
// volume at one time.
Sharing SharingMode `json:",omitempty"`
// MountVolume defines options for using this volume as a Mount-type
// volume.
//
// Either BlockVolume or MountVolume, but not both, must be present.
MountVolume *TypeMount `json:",omitempty"`
// BlockVolume defines options for using this volume as a Block-type
// volume.
//
// Either BlockVolume or MountVolume, but not both, must be present.
BlockVolume *TypeBlock `json:",omitempty"`
}
// Scope defines the Scope of a Cluster Volume. This is how many nodes a
// Volume can be accessed simultaneously on.
type Scope string
const (
// ScopeSingleNode indicates the volume can be used on one node at a
// time.
ScopeSingleNode Scope = "single"
// ScopeMultiNode indicates the volume can be used on many nodes at
// the same time.
ScopeMultiNode Scope = "multi"
)
// SharingMode defines the Sharing of a Cluster Volume. This is how Tasks using a
// Volume at the same time can use it.
type SharingMode string
const (
// SharingNone indicates that only one Task may use the Volume at a
// time.
SharingNone SharingMode = "none"
// SharingReadOnly indicates that the Volume may be shared by any
// number of Tasks, but they must be read-only.
SharingReadOnly SharingMode = "readonly"
// SharingOneWriter indicates that the Volume may be shared by any
// number of Tasks, but all after the first must be read-only.
SharingOneWriter SharingMode = "onewriter"
// SharingAll means that the Volume may be shared by any number of
// Tasks, as readers or writers.
SharingAll SharingMode = "all"
)
// TypeBlock defines options for using a volume as a block-type volume.
//
// Intentionally empty.
type TypeBlock struct{}
// TypeMount contains options for using a volume as a Mount-type
// volume.
type TypeMount struct {
// FsType specifies the filesystem type for the mount volume. Optional.
FsType string `json:",omitempty"`
// MountFlags defines flags to pass when mounting the volume. Optional.
MountFlags []string `json:",omitempty"`
}
// TopologyRequirement expresses the user's requirements for a volume's
// accessible topology.
type TopologyRequirement struct {
// Requisite specifies a list of Topologies, at least one of which the
// volume must be accessible from.
//
// Taken verbatim from the CSI Spec:
//
// Specifies the list of topologies the provisioned volume MUST be
// accessible from.
// This field is OPTIONAL. If TopologyRequirement is specified either
// requisite or preferred or both MUST be specified.
//
// If requisite is specified, the provisioned volume MUST be
// accessible from at least one of the requisite topologies.
//
// Given
// x = number of topologies provisioned volume is accessible from
// n = number of requisite topologies
// The CO MUST ensure n >= 1. The SP MUST ensure x >= 1
// If x==n, then the SP MUST make the provisioned volume available to
// all topologies from the list of requisite topologies. If it is
// unable to do so, the SP MUST fail the CreateVolume call.
// For example, if a volume should be accessible from a single zone,
// and requisite =
// {"region": "R1", "zone": "Z2"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and the "zone" "Z2".
// Similarly, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and both "zone" "Z2" and "zone" "Z3".
//
// If x<n, then the SP SHALL choose x unique topologies from the list
// of requisite topologies. If it is unable to do so, the SP MUST fail
// the CreateVolume call.
// For example, if a volume should be accessible from a single zone,
// and requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// then the SP may choose to make the provisioned volume available in
// either the "zone" "Z2" or the "zone" "Z3" in the "region" "R1".
// Similarly, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"}
// then the provisioned volume MUST be accessible from any combination
// of two unique topologies: e.g. "R1/Z2" and "R1/Z3", or "R1/Z2" and
// "R1/Z4", or "R1/Z3" and "R1/Z4".
//
// If x>n, then the SP MUST make the provisioned volume available from
// all topologies from the list of requisite topologies and MAY choose
// the remaining x-n unique topologies from the list of all possible
// topologies. If it is unable to do so, the SP MUST fail the
// CreateVolume call.
// For example, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and the "zone" "Z2" and the SP may select the second zone
// independently, e.g. "R1/Z4".
Requisite []Topology `json:",omitempty"`
// Preferred is a list of Topologies that the volume should attempt to be
// provisioned in.
//
// Taken from the CSI spec:
//
// Specifies the list of topologies the CO would prefer the volume to
// be provisioned in.
//
// This field is OPTIONAL. If TopologyRequirement is specified either
// requisite or preferred or both MUST be specified.
//
// An SP MUST attempt to make the provisioned volume available using
// the preferred topologies in order from first to last.
//
// If requisite is specified, all topologies in preferred list MUST
// also be present in the list of requisite topologies.
//
// If the SP is unable to to make the provisioned volume available
// from any of the preferred topologies, the SP MAY choose a topology
// from the list of requisite topologies.
// If the list of requisite topologies is not specified, then the SP
// MAY choose from the list of all possible topologies.
// If the list of requisite topologies is specified and the SP is
// unable to to make the provisioned volume available from any of the
// requisite topologies it MUST fail the CreateVolume call.
//
// Example 1:
// Given a volume should be accessible from a single zone, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// preferred =
// {"region": "R1", "zone": "Z3"}
// then the the SP SHOULD first attempt to make the provisioned volume
// available from "zone" "Z3" in the "region" "R1" and fall back to
// "zone" "Z2" in the "region" "R1" if that is not possible.
//
// Example 2:
// Given a volume should be accessible from a single zone, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z5"}
// preferred =
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z2"}
// then the the SP SHOULD first attempt to make the provisioned volume
// accessible from "zone" "Z4" in the "region" "R1" and fall back to
// "zone" "Z2" in the "region" "R1" if that is not possible. If that
// is not possible, the SP may choose between either the "zone"
// "Z3" or "Z5" in the "region" "R1".
//
// Example 3:
// Given a volume should be accessible from TWO zones (because an
// opaque parameter in CreateVolumeRequest, for example, specifies
// the volume is accessible from two zones, aka synchronously
// replicated), and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z5"}
// preferred =
// {"region": "R1", "zone": "Z5"},
// {"region": "R1", "zone": "Z3"}
// then the the SP SHOULD first attempt to make the provisioned volume
// accessible from the combination of the two "zones" "Z5" and "Z3" in
// the "region" "R1". If that's not possible, it should fall back to
// a combination of "Z5" and other possibilities from the list of
// requisite. If that's not possible, it should fall back to a
// combination of "Z3" and other possibilities from the list of
// requisite. If that's not possible, it should fall back to a
// combination of other possibilities from the list of requisite.
Preferred []Topology `json:",omitempty"`
}
// Topology is a map of topological domains to topological segments.
//
// This description is taken verbatim from the CSI Spec:
//
// A topological domain is a sub-division of a cluster, like "region",
// "zone", "rack", etc.
// A topological segment is a specific instance of a topological domain,
// like "zone3", "rack3", etc.
// For example {"com.company/zone": "Z1", "com.company/rack": "R3"}
// Valid keys have two segments: an OPTIONAL prefix and name, separated
// by a slash (/), for example: "com.company.example/zone".
// The key name segment is REQUIRED. The prefix is OPTIONAL.
// The key name MUST be 63 characters or less, begin and end with an
// alphanumeric character ([a-z0-9A-Z]), and contain only dashes (-),
// underscores (_), dots (.), or alphanumerics in between, for example
// "zone".
// The key prefix MUST be 63 characters or less, begin and end with a
// lower-case alphanumeric character ([a-z0-9]), contain only
// dashes (-), dots (.), or lower-case alphanumerics in between, and
// follow domain name notation format
// (https://tools.ietf.org/html/rfc1035#section-2.3.1).
// The key prefix SHOULD include the plugin's host company name and/or
// the plugin name, to minimize the possibility of collisions with keys
// from other plugins.
// If a key prefix is specified, it MUST be identical across all
// topology keys returned by the SP (across all RPCs).
// Keys MUST be case-insensitive. Meaning the keys "Zone" and "zone"
// MUST not both exist.
// Each value (topological segment) MUST contain 1 or more strings.
// Each string MUST be 63 characters or less and begin and end with an
// alphanumeric character with '-', '_', '.', or alphanumerics in
// between.
type Topology struct {
Segments map[string]string `json:",omitempty"`
}
// CapacityRange describes the minimum and maximum capacity a volume should be
// created with
type CapacityRange struct {
// RequiredBytes specifies that a volume must be at least this big. The
// value of 0 indicates an unspecified minimum.
RequiredBytes int64
// LimitBytes specifies that a volume must not be bigger than this. The
// value of 0 indicates an unspecified maximum
LimitBytes int64
}
// Secret represents a Swarm Secret value that must be passed to the CSI
// storage plugin when operating on this Volume. It represents one key-value
// pair of possibly many.
type Secret struct {
// Key is the name of the key of the key-value pair passed to the plugin.
Key string
// Secret is the swarm Secret object from which to read data. This can be a
// Secret name or ID. The Secret data is retrieved by Swarm and used as the
// value of the key-value pair passed to the plugin.
Secret string
}
// PublishState represents the state of a Volume as it pertains to its
// use on a particular Node.
type PublishState string
const (
// StatePending indicates that the volume should be published on
// this node, but the call to ControllerPublishVolume has not been
// successfully completed yet and the result recorded by swarmkit.
StatePending PublishState = "pending-publish"
// StatePublished means the volume is published successfully to the node.
StatePublished PublishState = "published"
// StatePendingNodeUnpublish indicates that the Volume should be
// unpublished on the Node, and we're waiting for confirmation that it has
// done so. After the Node has confirmed that the Volume has been
// unpublished, the state will move to StatePendingUnpublish.
StatePendingNodeUnpublish PublishState = "pending-node-unpublish"
// StatePendingUnpublish means the volume is still published to the node
// by the controller, awaiting the operation to unpublish it.
StatePendingUnpublish PublishState = "pending-controller-unpublish"
)
// PublishStatus represents the status of the volume as published to an
// individual node
type PublishStatus struct {
// NodeID is the ID of the swarm node this Volume is published to.
NodeID string `json:",omitempty"`
// State is the publish state of the volume.
State PublishState `json:",omitempty"`
// PublishContext is the PublishContext returned by the CSI plugin when
// a volume is published.
PublishContext map[string]string `json:",omitempty"`
}
// Info contains information about the Volume as a whole as provided by
// the CSI storage plugin.
type Info struct {
// CapacityBytes is the capacity of the volume in bytes. A value of 0
// indicates that the capacity is unknown.
CapacityBytes int64 `json:",omitempty"`
// VolumeContext is the context originating from the CSI storage plugin
// when the Volume is created.
VolumeContext map[string]string `json:",omitempty"`
// VolumeID is the ID of the Volume as seen by the CSI storage plugin. This
// is distinct from the Volume's Swarm ID, which is the ID used by all of
// the Docker Engine to refer to the Volume. If this field is blank, then
// the Volume has not been successfully created yet.
VolumeID string `json:",omitempty"`
// AccessibleTopolgoy is the topology this volume is actually accessible
// from.
AccessibleTopology []Topology `json:",omitempty"`
}

@ -0,0 +1,29 @@
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// CreateOptions VolumeConfig
//
// Volume configuration
// swagger:model CreateOptions
type CreateOptions struct {
// cluster volume spec
ClusterVolumeSpec *ClusterVolumeSpec `json:"ClusterVolumeSpec,omitempty"`
// Name of the volume driver to use.
Driver string `json:"Driver,omitempty"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
DriverOpts map[string]string `json:"DriverOpts,omitempty"`
// User-defined key/value metadata.
Labels map[string]string `json:"Labels,omitempty"`
// The new volume's name. If not specified, Docker generates a name.
//
Name string `json:"Name,omitempty"`
}

@ -0,0 +1,18 @@
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ListResponse VolumeListResponse
//
// Volume list response
// swagger:model ListResponse
type ListResponse struct {
// List of volumes
Volumes []*Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes.
//
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,8 @@
package volume // import "github.com/docker/docker/api/types/volume"
import "github.com/docker/docker/api/types/filters"
// ListOptions holds parameters to list volumes.
type ListOptions struct {
Filters filters.Args
}

@ -1,4 +1,4 @@
package types
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
@ -7,6 +7,9 @@ package types
// swagger:model Volume
type Volume struct {
// cluster volume
ClusterVolume *ClusterVolume `json:"ClusterVolume,omitempty"`
// Date/Time the volume was created.
CreatedAt string `json:"CreatedAt,omitempty"`
@ -47,14 +50,14 @@ type Volume struct {
Status map[string]interface{} `json:"Status,omitempty"`
// usage data
UsageData *VolumeUsageData `json:"UsageData,omitempty"`
UsageData *UsageData `json:"UsageData,omitempty"`
}
// VolumeUsageData Usage details about the volume. This information is used by the
// UsageData Usage details about the volume. This information is used by the
// `GET /system/df` endpoint, and omitted in other endpoints.
//
// swagger:model VolumeUsageData
type VolumeUsageData struct {
// swagger:model UsageData
type UsageData struct {
// The number of containers referencing this volume. This field
// is set to `-1` if the reference-count is not available.

@ -1,31 +0,0 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// VolumeCreateBody Volume configuration
// swagger:model VolumeCreateBody
type VolumeCreateBody struct {
// Name of the volume driver to use.
// Required: true
Driver string `json:"Driver"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
// Required: true
DriverOpts map[string]string `json:"DriverOpts"`
// User-defined key/value metadata.
// Required: true
Labels map[string]string `json:"Labels"`
// The new volume's name. If not specified, Docker generates a name.
//
// Required: true
Name string `json:"Name"`
}

@ -1,23 +0,0 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
import "github.com/docker/docker/api/types"
// VolumeListOKBody Volume list response
// swagger:model VolumeListOKBody
type VolumeListOKBody struct {
// List of volumes
// Required: true
Volumes []*types.Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes.
//
// Required: true
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,7 @@
package volume // import "github.com/docker/docker/api/types/volume"
// UpdateOptions is configuration to update a Volume with.
type UpdateOptions struct {
// Spec is the ClusterVolumeSpec to update the volume to.
Spec *ClusterVolumeSpec `json:"Spec,omitempty"`
}

@ -5,7 +5,7 @@ import (
"net/url"
)
// BuildCancel requests the daemon to cancel ongoing build request
// BuildCancel requests the daemon to cancel the ongoing build request.
func (cli *Client) BuildCancel(ctx context.Context, id string) error {
query := url.Values{}
query.Set("id", id)

@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -23,12 +23,12 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
if opts.All {
query.Set("all", "1")
}
query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage))
filters, err := filters.ToJSON(opts.Filters)
query.Set("keep-storage", strconv.Itoa(int(opts.KeepStorage)))
f, err := filters.ToJSON(opts.Filters)
if err != nil {
return nil, errors.Wrap(err, "prune could not marshal filters option")
}
query.Set("filters", filters)
query.Set("filters", f)
serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
defer ensureReaderClosed(serverResp)
@ -38,7 +38,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
}
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
return nil, fmt.Errorf("Error retrieving disk usage: %v", err)
return nil, errors.Wrap(err, "error retrieving disk usage")
}
return &report, nil

@ -20,7 +20,7 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return checkpoints, wrapResponseError(err, resp, "container", container)
return checkpoints, err
}
err = json.NewDecoder(resp.body).Decode(&checkpoints)

@ -6,9 +6,10 @@ https://docs.docker.com/engine/api/
# Usage
You use the library by creating a client object and calling methods on it. The
client can be created either from environment variables with NewClientWithOpts(client.FromEnv),
or configured manually with NewClient().
You use the library by constructing a client object using [NewClientWithOpts]
and calling methods on it. The client can be configured from environment
variables by passing the [FromEnv] option, or configured manually by passing any
of the other available [Opts].
For example, to list running containers (the equivalent of "docker ps"):
@ -42,7 +43,6 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"fmt"
"net"
"net/http"
"net/url"
@ -122,15 +122,18 @@ type Client struct {
}
// CheckRedirect specifies the policy for dealing with redirect responses:
// If the request is non-GET return `ErrRedirect`. Otherwise use the last response.
// If the request is non-GET return ErrRedirect, otherwise use the last response.
//
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client .
// The Docker client (and by extension docker API client) can be made to send a request
// like POST /containers//start where what would normally be in the name section of the URL is empty.
// This triggers an HTTP 301 from the daemon.
// In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon.
// This behavior change manifests in the client in that before the 301 was not followed and
// the client did not generate an error, but now results in a message like Error response from daemon: page not found.
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308)
// in the client. The Docker client (and by extension docker API client) can be
// made to send a request like POST /containers//start where what would normally
// be in the name section of the URL is empty. This triggers an HTTP 301 from
// the daemon.
//
// In go 1.8 this 301 will be converted to a GET request, and ends up getting
// a 404 from the daemon. This behavior change manifests in the client in that
// before, the 301 was not followed and the client did not generate an error,
// but now results in a message like Error response from daemon: page not found.
func CheckRedirect(req *http.Request, via []*http.Request) error {
if via[0].Method == http.MethodGet {
return http.ErrUseLastResponse
@ -138,15 +141,27 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
return ErrRedirect
}
// NewClientWithOpts initializes a new API client with default values. It takes functors
// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))`
// It also initializes the custom http headers to add to each request.
// NewClientWithOpts initializes a new API client with a default HTTPClient, and
// default API host and version. It also initializes the custom HTTP headers to
// add to each request.
//
// It takes an optional list of Opt functional arguments, which are applied in
// the order they're provided, which allows modifying the defaults when creating
// the client. For example, the following initializes a client that configures
// itself with values from environment variables (client.FromEnv), and has
// automatic API version negotiation enabled (client.WithAPIVersionNegotiation()).
//
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
// cli, err := client.NewClientWithOpts(
// client.FromEnv,
// client.WithAPIVersionNegotiation(),
// )
func NewClientWithOpts(ops ...Opt) (*Client, error) {
client, err := defaultHTTPClient(DefaultDockerHost)
hostURL, err := ParseHostURL(DefaultDockerHost)
if err != nil {
return nil, err
}
client, err := defaultHTTPClient(hostURL)
if err != nil {
return nil, err
}
@ -154,8 +169,8 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
host: DefaultDockerHost,
version: api.DefaultVersion,
client: client,
proto: defaultProto,
addr: defaultAddr,
proto: hostURL.Scheme,
addr: hostURL.Host,
}
for _, op := range ops {
@ -181,13 +196,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
return c, nil
}
func defaultHTTPClient(host string) (*http.Client, error) {
url, err := ParseHostURL(host)
func defaultHTTPClient(hostURL *url.URL) (*http.Client, error) {
transport := &http.Transport{}
err := sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
if err != nil {
return nil, err
}
transport := new(http.Transport)
sockets.ConfigureTransport(transport, url.Scheme, url.Host)
return &http.Client{
Transport: transport,
CheckRedirect: CheckRedirect,
@ -223,11 +237,21 @@ func (cli *Client) ClientVersion() string {
return cli.version
}
// NegotiateAPIVersion queries the API and updates the version to match the
// API version. Any errors are silently ignored. If a manual override is in place,
// either through the `DOCKER_API_VERSION` environment variable, or if the client
// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation
// will be performed.
// NegotiateAPIVersion queries the API and updates the version to match the API
// version. NegotiateAPIVersion downgrades the client's API version to match the
// APIVersion if the ping version is lower than the default version. If the API
// version reported by the server is higher than the maximum version supported
// by the client, it uses the client's maximum version.
//
// If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, or if the
// client did not get a successful ping response, it assumes it is connected with
// an old daemon that does not support API version negotiation, in which case it
// downgrades to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
if !cli.manualOverride {
ping, _ := cli.Ping(ctx)
@ -235,23 +259,31 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
}
}
// NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion
// if the ping version is less than the default version. If a manual override is
// in place, either through the `DOCKER_API_VERSION` environment variable, or if
// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no
// negotiation is performed.
func (cli *Client) NegotiateAPIVersionPing(p types.Ping) {
// NegotiateAPIVersionPing downgrades the client's API version to match the
// APIVersion in the ping response. If the API version in pingResponse is higher
// than the maximum version supported by the client, it uses the client's maximum
// version.
//
// If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, we assume
// we are connected with an old daemon without API version negotiation support,
// and downgrade to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
if !cli.manualOverride {
cli.negotiateAPIVersionPing(p)
cli.negotiateAPIVersionPing(pingResponse)
}
}
// negotiateAPIVersionPing queries the API and updates the version to match the
// API version. Any errors are silently ignored.
func (cli *Client) negotiateAPIVersionPing(p types.Ping) {
// try the latest version before versioning headers existed
if p.APIVersion == "" {
p.APIVersion = "1.24"
// API version from the ping response.
func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) {
// default to the latest version before versioning headers existed
if pingResponse.APIVersion == "" {
pingResponse.APIVersion = "1.24"
}
// if the client is not initialized with a version, start with the latest supported version
@ -260,8 +292,8 @@ func (cli *Client) negotiateAPIVersionPing(p types.Ping) {
}
// if server version is lower than the client version, downgrade
if versions.LessThan(p.APIVersion, cli.version) {
cli.version = p.APIVersion
if versions.LessThan(pingResponse.APIVersion, cli.version) {
cli.version = pingResponse.APIVersion
}
// Store the results, so that automatic API version negotiation (if enabled)
@ -285,13 +317,12 @@ func (cli *Client) HTTPClient() *http.Client {
// ParseHostURL parses a url string, validates the string is a host url, and
// returns the parsed URL
func ParseHostURL(host string) (*url.URL, error) {
protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 {
return nil, fmt.Errorf("unable to parse docker host `%s`", host)
proto, addr, ok := strings.Cut(host, "://")
if !ok || addr == "" {
return nil, errors.Errorf("unable to parse docker host `%s`", host)
}
var basePath string
proto, addr := protoAddrParts[0], protoAddrParts[1]
if proto == "tcp" {
parsed, err := url.Parse("tcp://" + addr)
if err != nil {
@ -307,22 +338,9 @@ func ParseHostURL(host string) (*url.URL, error) {
}, nil
}
// CustomHTTPHeaders returns the custom http headers stored by the client.
func (cli *Client) CustomHTTPHeaders() map[string]string {
m := make(map[string]string)
for k, v := range cli.customHTTPHeaders {
m[k] = v
}
return m
}
// SetCustomHTTPHeaders that will be set on every HTTP request made by the client.
// Deprecated: use WithHTTPHeaders when creating the client.
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
cli.customHTTPHeaders = headers
}
// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection.
// Dialer returns a dialer for a raw stream connection, with an HTTP/1.1 header,
// that can be used for proxying the daemon connection.
//
// Used by `docker dial-stdio` (docker/cli#889).
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {
return func(ctx context.Context) (net.Conn, error) {

@ -9,7 +9,11 @@ import "net/http"
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
// Deprecated: use NewClientWithOpts
//
// Deprecated: use [NewClientWithOpts] passing the [WithHost], [WithVersion],
// [WithHTTPClient] and [WithHTTPHeaders] options. We recommend enabling API
// version negotiation by passing the [WithAPIVersionNegotiation] option instead
// of WithVersion.
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders))
}
@ -17,7 +21,7 @@ func NewClient(host string, version string, client *http.Client, httpHeaders map
// NewEnvClient initializes a new API client based on environment variables.
// See FromEnv for a list of support environment variables.
//
// Deprecated: use NewClientWithOpts(FromEnv)
// Deprecated: use [NewClientWithOpts] passing the [FromEnv] option.
func NewEnvClient() (*Client, error) {
return NewClientWithOpts(FromEnv)
}

@ -1,10 +1,8 @@
//go:build linux || freebsd || openbsd || netbsd || darwin || solaris || illumos || dragonfly
// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly
//go:build !windows
// +build !windows
package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "unix:///var/run/docker.sock"
const defaultProto = "unix"
const defaultAddr = "/var/run/docker.sock"

@ -1,7 +1,5 @@
package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "npipe:////./pipe/docker_engine"
const defaultProto = "npipe"
const defaultAddr = "//./pipe/docker_engine"

@ -8,7 +8,7 @@ import (
"github.com/docker/docker/api/types/swarm"
)
// ConfigCreate creates a new Config.
// ConfigCreate creates a new config.
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
var response types.ConfigCreateResponse
if err := cli.NewVersionError("1.30", "config create"); err != nil {

@ -20,7 +20,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id)
return swarm.Config{}, nil, err
}
body, err := io.ReadAll(resp.body)

@ -2,12 +2,12 @@ package client // import "github.com/docker/docker/client"
import "context"
// ConfigRemove removes a Config.
// ConfigRemove removes a config.
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
if err := cli.NewVersionError("1.30", "config remove"); err != nil {
return err
}
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "config", id)
return err
}

@ -3,18 +3,17 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"net/url"
"strconv"
"github.com/docker/docker/api/types/swarm"
)
// ConfigUpdate attempts to update a Config
// ConfigUpdate attempts to update a config
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
if err := cli.NewVersionError("1.30", "config update"); err != nil {
return err
}
query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10))
query.Set("version", version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil)
ensureReaderClosed(resp)
return err

@ -52,6 +52,8 @@ func (cli *Client) ContainerAttach(ctx context.Context, container string, option
query.Set("logs", "1")
}
headers := map[string][]string{"Content-Type": {"text/plain"}}
headers := map[string][]string{
"Content-Type": {"text/plain"},
}
return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, headers)
}

@ -10,7 +10,7 @@ import (
"github.com/docker/docker/api/types"
)
// ContainerCommit applies changes into a container and creates a new tagged image.
// ContainerCommit applies changes to a container and creates a new tagged image.
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
var repository, tag string
if options.Reference != "" {

@ -14,7 +14,7 @@ import (
"github.com/docker/docker/api/types"
)
// ContainerStatPath returns Stat information about a path inside the container filesystem.
// ContainerStatPath returns stat information about a path inside the container filesystem.
func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) {
query := url.Values{}
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
@ -23,7 +23,7 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
response, err := cli.head(ctx, urlStr, query, nil)
defer ensureReaderClosed(response)
if err != nil {
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path)
return types.ContainerPathStat{}, err
}
return getContainerPathStatFromHeader(response.header)
}
@ -47,12 +47,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
response, err := cli.putRaw(ctx, apiPath, query, content, nil)
defer ensureReaderClosed(response)
if err != nil {
return wrapResponseError(err, response, "container:path", containerID+":"+dstPath)
}
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK {
return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
return err
}
return nil
@ -67,12 +62,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
apiPath := "/containers/" + containerID + "/archive"
response, err := cli.get(ctx, apiPath, query, nil)
if err != nil {
return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath)
}
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK {
return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
return nil, types.ContainerPathStat{}, err
}
// In order to get the copy behavior right, we need to know information

@ -9,7 +9,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/versions"
specs "github.com/opencontainers/image-spec/specs-go/v1"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type configWrapper struct {
@ -18,24 +18,33 @@ type configWrapper struct {
NetworkingConfig *network.NetworkingConfig
}
// ContainerCreate creates a new container based in the given configuration.
// ContainerCreate creates a new container based on the given configuration.
// It can be associated with a name, but it's not mandatory.
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
var response container.ContainerCreateCreatedBody
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
var response container.CreateResponse
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
return response, err
}
// When using API 1.24 and under, the client is responsible for removing the container
if hostConfig != nil && versions.LessThan(cli.ClientVersion(), "1.25") {
hostConfig.AutoRemove = false
}
if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
return response, err
}
if hostConfig != nil {
if versions.LessThan(cli.ClientVersion(), "1.25") {
// When using API 1.24 and under, the client is responsible for removing the container
hostConfig.AutoRemove = false
}
if versions.GreaterThanOrEqualTo(cli.ClientVersion(), "1.42") || versions.LessThan(cli.ClientVersion(), "1.40") {
// KernelMemory was added in API 1.40, and deprecated in API 1.42
hostConfig.KernelMemory = 0
}
if platform != nil && platform.OS == "linux" && versions.LessThan(cli.ClientVersion(), "1.42") {
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
hostConfig.ConsoleSize = [2]uint{0, 0}
}
}
query := url.Values{}
if p := formatPlatform(platform); p != "" {
query.Set("platform", p)
@ -66,7 +75,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
// Similar to containerd's platforms.Format(), but does allow components to be
// omitted (e.g. pass "architecture" only, without "os":
// https://github.com/containerd/containerd/blob/v1.5.2/platforms/platforms.go#L243-L263
func formatPlatform(platform *specs.Platform) string {
func formatPlatform(platform *ocispec.Platform) string {
if platform == nil {
return ""
}

@ -9,8 +9,8 @@ import (
)
// ContainerDiff shows differences in a container filesystem since it was started.
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.ContainerChangeResponseItem, error) {
var changes []container.ContainerChangeResponseItem
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
var changes []container.FilesystemChange
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
defer ensureReaderClosed(serverResp)

@ -5,6 +5,7 @@ import (
"encoding/json"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
)
// ContainerExecCreate creates a new exec configuration to run an exec process.
@ -14,6 +15,9 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co
if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil {
return response, err
}
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
defer ensureReaderClosed(resp)
@ -26,6 +30,9 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co
// ContainerExecStart starts an exec process already created in the docker host.
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil)
ensureReaderClosed(resp)
return err
@ -36,7 +43,12 @@ func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config
// and the a reader to get output. It's up to the called to close
// the hijacked connection by calling types.HijackedResponse.Close.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) {
headers := map[string][]string{"Content-Type": {"application/json"}}
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
headers := map[string][]string{
"Content-Type": {"application/json"},
}
return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers)
}

@ -18,7 +18,7 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID)
return types.ContainerJSON{}, err
}
var response types.ContainerJSON
@ -38,7 +38,7 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID)
return types.ContainerJSON{}, nil, err
}
body, err := io.ReadAll(serverResp.body)

@ -8,7 +8,9 @@ import (
// ContainerKill terminates the container process but does not remove the container from the docker host.
func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error {
query := url.Values{}
query.Set("signal", signal)
if signal != "" {
query.Set("signal", signal)
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/kill", query, nil, nil)
ensureReaderClosed(resp)

@ -18,7 +18,7 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
query.Set("all", "1")
}
if options.Limit != -1 {
if options.Limit > 0 {
query.Set("limit", strconv.Itoa(options.Limit))
}

@ -74,7 +74,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
if err != nil {
return nil, wrapResponseError(err, resp, "container", container)
return nil, err
}
return resp.body, nil
}

@ -23,5 +23,5 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "container", containerID)
return err
}

@ -3,18 +3,22 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"net/url"
"time"
"strconv"
timetypes "github.com/docker/docker/api/types/time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
)
// ContainerRestart stops and starts a container again.
// It makes the daemon to wait for the container to be up again for
// It makes the daemon wait for the container to be up again for
// a specific amount of time, given the timeout.
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
query := url.Values{}
if timeout != nil {
query.Set("t", timetypes.DurationToSecondsString(*timeout))
if options.Timeout != nil {
query.Set("t", strconv.Itoa(*options.Timeout))
}
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
query.Set("signal", options.Signal)
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
ensureReaderClosed(resp)

@ -3,9 +3,10 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"net/url"
"time"
"strconv"
timetypes "github.com/docker/docker/api/types/time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
)
// ContainerStop stops a container. In case the container fails to stop
@ -15,10 +16,13 @@ import (
// If the timeout is nil, the container's StopTimeout value is used, if set,
// otherwise the engine default. A negative timeout value can be specified,
// meaning no timeout, i.e. no forceful termination is performed.
func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
query := url.Values{}
if timeout != nil {
query.Set("t", timetypes.DurationToSecondsString(*timeout))
if options.Timeout != nil {
query.Set("t", strconv.Itoa(*options.Timeout))
}
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
query.Set("signal", options.Signal)
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
ensureReaderClosed(resp)

@ -7,7 +7,7 @@ import (
"github.com/docker/docker/api/types/container"
)
// ContainerUpdate updates resources of a container
// ContainerUpdate updates the resources of a container.
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
var response container.ContainerUpdateOKBody
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)

@ -1,14 +1,19 @@
package client // import "github.com/docker/docker/client"
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/url"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
)
const containerWaitErrorMsgLimit = 2 * 1024 /* Max: 2KiB */
// ContainerWait waits until the specified container is in a certain state
// indicated by the given condition, either "not-running" (default),
// "next-exit", or "removed".
@ -24,16 +29,18 @@ import (
// wait request or in getting the response. This allows the caller to
// synchronize ContainerWait with other calls, such as specifying a
// "next-exit" condition before issuing a ContainerStart request.
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) {
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
if versions.LessThan(cli.ClientVersion(), "1.30") {
return cli.legacyContainerWait(ctx, containerID)
}
resultC := make(chan container.ContainerWaitOKBody)
resultC := make(chan container.WaitResponse)
errC := make(chan error, 1)
query := url.Values{}
query.Set("condition", string(condition))
if condition != "" {
query.Set("condition", string(condition))
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", query, nil, nil)
if err != nil {
@ -44,9 +51,23 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
go func() {
defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody
if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
errC <- err
body := resp.body
responseText := bytes.NewBuffer(nil)
stream := io.TeeReader(body, responseText)
var res container.WaitResponse
if err := json.NewDecoder(stream).Decode(&res); err != nil {
// NOTE(nicks): The /wait API does not work well with HTTP proxies.
// At any time, the proxy could cut off the response stream.
//
// But because the HTTP status has already been written, the proxy's
// only option is to write a plaintext error message.
//
// If there's a JSON parsing error, read the real error message
// off the body and send it to the client.
_, _ = io.ReadAll(io.LimitReader(stream, containerWaitErrorMsgLimit))
errC <- errors.New(responseText.String())
return
}
@ -58,8 +79,8 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
// legacyContainerWait returns immediately and doesn't have an option to wait
// until the container is removed.
func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.ContainerWaitOKBody, <-chan error) {
resultC := make(chan container.ContainerWaitOKBody)
func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.WaitResponse, <-chan error) {
resultC := make(chan container.WaitResponse)
errC := make(chan error)
go func() {
@ -70,7 +91,7 @@ func (cli *Client) legacyContainerWait(ctx context.Context, containerID string)
}
defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody
var res container.WaitResponse
if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
errC <- err
return

@ -4,23 +4,30 @@ import (
"context"
"encoding/json"
"fmt"
"net/url"
"github.com/docker/docker/api/types"
)
// DiskUsage requests the current data usage from the daemon
func (cli *Client) DiskUsage(ctx context.Context) (types.DiskUsage, error) {
var du types.DiskUsage
func (cli *Client) DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error) {
var query url.Values
if len(options.Types) > 0 {
query = url.Values{}
for _, t := range options.Types {
query.Add("type", string(t))
}
}
serverResp, err := cli.get(ctx, "/system/df", nil, nil)
serverResp, err := cli.get(ctx, "/system/df", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return du, err
return types.DiskUsage{}, err
}
var du types.DiskUsage
if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil {
return du, fmt.Errorf("Error retrieving disk usage: %v", err)
return types.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err)
}
return du, nil
}

@ -5,13 +5,13 @@ import (
"encoding/json"
"net/url"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/registry"
)
// DistributionInspect returns the image digest with full Manifest
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
// DistributionInspect returns the image digest with the full manifest.
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) {
// Contact the registry to retrieve digest and platform information
var distributionInspect registrytypes.DistributionInspect
var distributionInspect registry.DistributionInspect
if image == "" {
return distributionInspect, objectNotFoundError{object: "distribution", id: image}
}
@ -23,7 +23,7 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist
if encodedRegistryAuth != "" {
headers = map[string][]string{
"X-Registry-Auth": {encodedRegistryAuth},
registry.AuthHeader: {encodedRegistryAuth},
}
}

@ -0,0 +1,90 @@
package client // import "github.com/docker/docker/client"
const (
// EnvOverrideHost is the name of the environment variable that can be used
// to override the default host to connect to (DefaultDockerHost).
//
// This env-var is read by FromEnv and WithHostFromEnv and when set to a
// non-empty value, takes precedence over the default host (which is platform
// specific), or any host already set.
EnvOverrideHost = "DOCKER_HOST"
// EnvOverrideAPIVersion is the name of the environment variable that can
// be used to override the API version to use. Value should be
// formatted as MAJOR.MINOR, for example, "1.19".
//
// This env-var is read by FromEnv and WithVersionFromEnv and when set to a
// non-empty value, takes precedence over API version negotiation.
//
// This environment variable should be used for debugging purposes only, as
// it can set the client to use an incompatible (or invalid) API version.
EnvOverrideAPIVersion = "DOCKER_API_VERSION"
// EnvOverrideCertPath is the name of the environment variable that can be
// used to specify the directory from which to load the TLS certificates
// (ca.pem, cert.pem, key.pem) from. These certificates are used to configure
// the Client for a TCP connection protected by TLS client authentication.
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection. Refer to EnvTLSVerify below to learn how to
// disable verification for testing purposes.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// For local access to the API, it is recommended to connect with the daemon
// using the default local socket connection (on Linux), or the named pipe
// (on Windows).
//
// If you need to access the API of a remote daemon, consider using an SSH
// (ssh://) connection, which is easier to set up, and requires no additional
// configuration if the host is accessible using ssh.
//
// If you cannot use the alternatives above, and you must expose the API over
// a TCP connection, refer to https://docs.docker.com/engine/security/protect-access/
// to learn how to configure the daemon and client to use a TCP connection
// with TLS client authentication. Make sure you know the differences between
// a regular TLS connection and a TLS connection protected by TLS client
// authentication, and verify that the API cannot be accessed by other clients.
EnvOverrideCertPath = "DOCKER_CERT_PATH"
// EnvTLSVerify is the name of the environment variable that can be used to
// enable or disable TLS certificate verification. When set to a non-empty
// value, TLS certificate verification is enabled, and the client is configured
// to use a TLS connection, using certificates from the default directories
// (within `~/.docker`); refer to EnvOverrideCertPath above for additional
// details.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// Before setting up your client and daemon to use a TCP connection with TLS
// client authentication, consider using one of the alternatives mentioned
// in EnvOverrideCertPath above.
//
// Disabling TLS certificate verification (for testing purposes)
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection, and it is highly recommended to keep verification
// enabled to prevent machine-in-the-middle attacks. Refer to the documentation
// at https://docs.docker.com/engine/security/protect-access/ and pages linked
// from that page to learn how to configure the daemon and client to use a
// TCP connection with TLS client authentication enabled.
//
// Set the "DOCKER_TLS_VERIFY" environment to an empty string ("") to
// disable TLS certificate verification. Disabling verification is insecure,
// so should only be done for testing purposes. From the Go documentation
// (https://pkg.go.dev/crypto/tls#Config):
//
// InsecureSkipVerify controls whether a client verifies the server's
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
// accepts any certificate presented by the server and any host name in that
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
// attacks unless custom verification is used. This should be used only for
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
EnvTLSVerify = "DOCKER_TLS_VERIFY"
)

@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
import (
"fmt"
"net/http"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs"
@ -41,11 +40,11 @@ type notFound interface {
// IsErrNotFound returns true if the error is a NotFound error, which is returned
// by the API when some object is not found.
func IsErrNotFound(err error) bool {
var e notFound
if errors.As(err, &e) {
if errdefs.IsNotFound(err) {
return true
}
return errdefs.IsNotFound(err)
var e notFound
return errors.As(err, &e)
}
type objectNotFoundError struct {
@ -59,75 +58,6 @@ func (e objectNotFoundError) Error() string {
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
}
func wrapResponseError(err error, resp serverResponse, object, id string) error {
switch {
case err == nil:
return nil
case resp.statusCode == http.StatusNotFound:
return objectNotFoundError{object: object, id: id}
case resp.statusCode == http.StatusNotImplemented:
return errdefs.NotImplemented(err)
default:
return err
}
}
// unauthorizedError represents an authorization error in a remote registry.
type unauthorizedError struct {
cause error
}
// Error returns a string representation of an unauthorizedError
func (u unauthorizedError) Error() string {
return u.cause.Error()
}
// IsErrUnauthorized returns true if the error is caused
// when a remote registry authentication fails
func IsErrUnauthorized(err error) bool {
if _, ok := err.(unauthorizedError); ok {
return ok
}
return errdefs.IsUnauthorized(err)
}
type pluginPermissionDenied struct {
name string
}
func (e pluginPermissionDenied) Error() string {
return "Permission denied while installing plugin " + e.name
}
// IsErrPluginPermissionDenied returns true if the error is caused
// when a user denies a plugin's permissions
func IsErrPluginPermissionDenied(err error) bool {
_, ok := err.(pluginPermissionDenied)
return ok
}
type notImplementedError struct {
message string
}
func (e notImplementedError) Error() string {
return e.message
}
func (e notImplementedError) NotImplemented() bool {
return true
}
// IsErrNotImplemented returns true if the error is a NotImplemented error.
// This is returned by the API when a requested feature has not been
// implemented.
func IsErrNotImplemented(err error) bool {
if _, ok := err.(notImplementedError); ok {
return ok
}
return errdefs.IsNotImplemented(err)
}
// NewVersionError returns an error if the APIVersion required
// if less than the current supported version
func (cli *Client) NewVersionError(APIrequired, feature string) error {

@ -17,7 +17,6 @@ import (
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
// to the caller to reopen the stream in the event of an error by reinvoking this method.
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) {
messages := make(chan events.Message)
errs := make(chan error, 1)

@ -12,6 +12,7 @@ import (
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
"github.com/docker/go-connections/sockets"
"github.com/pkg/errors"
)
@ -22,20 +23,16 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
if err != nil {
return types.HijackedResponse{}, err
}
apiPath := cli.getAPIPath(ctx, path, query)
req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded)
req, err := cli.buildRequest(http.MethodPost, cli.getAPIPath(ctx, path, query), bodyEncoded, headers)
if err != nil {
return types.HijackedResponse{}, err
}
req = cli.addHeaders(req, headers)
conn, err := cli.setupHijackConn(ctx, req, "tcp")
conn, mediaType, err := cli.setupHijackConn(ctx, req, "tcp")
if err != nil {
return types.HijackedResponse{}, err
}
return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err
return types.NewHijackedResponse(conn, mediaType), err
}
// DialHijack returns a hijacked connection with negotiated protocol proto.
@ -46,7 +43,8 @@ func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[s
}
req = cli.addHeaders(req, meta)
return cli.setupHijackConn(ctx, req, proto)
conn, _, err := cli.setupHijackConn(ctx, req, proto)
return conn, err
}
// fallbackDial is used when WithDialer() was not called.
@ -61,19 +59,14 @@ func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
return net.Dial(proto, addr)
}
func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, error) {
req.URL.Host = cli.addr
if cli.proto == "unix" || cli.proto == "npipe" {
// Override host header for non-tcp connections.
req.Host = DummyHost
}
func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, string, error) {
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", proto)
dialer := cli.Dialer()
conn, err := dialer(ctx)
if err != nil {
return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
return nil, "", errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
}
// When we set up a TCP connection for hijack, there could be long periods
@ -82,8 +75,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
// state. Setting TCP KeepAlive on the socket connection will prohibit
// ECONNTIMEOUT unless the socket connection truly is broken
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(30 * time.Second)
_ = tcpConn.SetKeepAlive(true)
_ = tcpConn.SetKeepAlivePeriod(30 * time.Second)
}
clientconn := httputil.NewClientConn(conn, nil)
@ -95,18 +88,18 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
//nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons
if err != httputil.ErrPersistEOF {
if err != nil {
return nil, err
return nil, "", err
}
if resp.StatusCode != http.StatusSwitchingProtocols {
resp.Body.Close()
return nil, fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
_ = resp.Body.Close()
return nil, "", fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
}
}
c, br := clientconn.Hijack()
if br.Buffered() > 0 {
// If there is buffered content, wrap the connection. We return an
// object that implements CloseWrite iff the underlying connection
// object that implements CloseWrite if the underlying connection
// implements it.
if _, ok := c.(types.CloseWriter); ok {
c = &hijackedConnCloseWriter{&hijackedConn{c, br}}
@ -117,7 +110,13 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
br.Reset(nil)
}
return c, nil
var mediaType string
if versions.GreaterThanOrEqualTo(cli.ClientVersion(), "1.42") {
// Prior to 1.42, Content-Type is always set to raw-stream and not relevant
mediaType = resp.Header.Get("Content-Type")
}
return c, mediaType, nil
}
// hijackedConn wraps a net.Conn and is returned by setupHijackConn in the case

@ -14,8 +14,8 @@ import (
"github.com/docker/docker/api/types/container"
)
// ImageBuild sends request to the daemon to build images.
// The Body in the response implement an io.ReadCloser and it's up to the caller to
// ImageBuild sends a request to the daemon to build images.
// The Body in the response implements an io.ReadCloser and it's up to the caller to
// close it.
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
query, err := cli.imageBuildOptionsToQuery(options)

@ -8,9 +8,10 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
)
// ImageCreate creates a new image based in the parent options.
// ImageCreate creates a new image based on the parent options.
// It returns the JSON content in the response body.
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
ref, err := reference.ParseNormalizedNamed(parentReference)
@ -32,6 +33,6 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
}
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/images/create", query, nil, headers)
}

@ -10,7 +10,7 @@ import (
"github.com/docker/docker/api/types"
)
// ImageImport creates a new image based in the source options.
// ImageImport creates a new image based on the source options.
// It returns the JSON content in the response body.
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
if ref != "" {

@ -17,7 +17,7 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID)
return types.ImageInspect{}, nil, err
}
body, err := io.ReadAll(serverResp.body)

@ -34,6 +34,9 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
if options.All {
query.Set("all", "1")
}
if options.SharedSize && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
query.Set("shared-size", "1")
}
serverResp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(serverResp)

@ -8,6 +8,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs"
)
@ -49,6 +50,6 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
}
func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/images/"+imageID+"/push", query, nil, headers)
}

@ -23,7 +23,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return dels, wrapResponseError(err, resp, "image", imageID)
return dels, err
}
err = json.NewDecoder(resp.body).Decode(&dels)

@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -12,13 +12,15 @@ import (
"github.com/docker/docker/errdefs"
)
// ImageSearch makes the docker host to search by a term in a remote registry.
// ImageSearch makes the docker host search by a term in a remote registry.
// The list of results is not sorted in any fashion.
func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) {
var results []registry.SearchResult
query := url.Values{}
query.Set("term", term)
query.Set("limit", fmt.Sprintf("%d", options.Limit))
if options.Limit > 0 {
query.Set("limit", strconv.Itoa(options.Limit))
}
if options.Filters.Len() > 0 {
filterJSON, err := filters.ToJSON(options.Filters)
@ -46,6 +48,6 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
}
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.get(ctx, "/images/search", query, headers)
}

@ -5,18 +5,17 @@ import (
"io"
"net"
"net/http"
"time"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
volumetypes "github.com/docker/docker/api/types/volume"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/docker/docker/api/types/volume"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// CommonAPIClient is the common methods between stable and experimental versions of APIClient.
@ -48,8 +47,8 @@ type CommonAPIClient interface {
type ContainerAPIClient interface {
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error)
ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error)
ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error)
@ -65,16 +64,16 @@ type ContainerAPIClient interface {
ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
ContainerRename(ctx context.Context, container, newContainerName string) error
ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error
ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error
ContainerRestart(ctx context.Context, container string, options container.StopOptions) error
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error)
ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error)
ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
ContainerStop(ctx context.Context, container string, timeout *time.Duration) error
ContainerTop(ctx context.Context, container string, arguments []string) (containertypes.ContainerTopOKBody, error)
ContainerStop(ctx context.Context, container string, options container.StopOptions) error
ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error)
ContainerUnpause(ctx context.Context, container string) error
ContainerUpdate(ctx context.Context, container string, updateConfig containertypes.UpdateConfig) (containertypes.ContainerUpdateOKBody, error)
ContainerWait(ctx context.Context, container string, condition containertypes.WaitCondition) (<-chan containertypes.ContainerWaitOKBody, <-chan error)
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error)
ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error
ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error)
@ -107,7 +106,7 @@ type ImageAPIClient interface {
// NetworkAPIClient defines API client methods for the networks
type NetworkAPIClient interface {
NetworkConnect(ctx context.Context, network, container string, config *networktypes.EndpointSettings) error
NetworkConnect(ctx context.Context, network, container string, config *network.EndpointSettings) error
NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
NetworkDisconnect(ctx context.Context, network, container string, force bool) error
NetworkInspect(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, error)
@ -167,19 +166,20 @@ type SwarmAPIClient interface {
type SystemAPIClient interface {
Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error)
Info(ctx context.Context) (types.Info, error)
RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error)
DiskUsage(ctx context.Context) (types.DiskUsage, error)
RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error)
DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error)
Ping(ctx context.Context) (types.Ping, error)
}
// VolumeAPIClient defines API client methods for the volumes
type VolumeAPIClient interface {
VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error)
VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error)
VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error)
VolumeInspect(ctx context.Context, volumeID string) (volume.Volume, error)
VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error)
VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error)
VolumeRemove(ctx context.Context, volumeID string, force bool) error
VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error)
VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error
}
// SecretAPIClient defines API client methods for secrets

@ -5,13 +5,12 @@ import (
"encoding/json"
"net/url"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
)
// RegistryLogin authenticates the docker server with a given docker registry.
// It returns unauthorizedError when the authentication fails.
func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) {
func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
defer ensureReaderClosed(resp)

@ -36,7 +36,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return networkResource, nil, wrapResponseError(err, resp, "network", networkID)
return networkResource, nil, err
}
body, err := io.ReadAll(resp.body)

@ -6,5 +6,5 @@ import "context"
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "network", networkID)
return err
}

@ -17,7 +17,7 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID)
return swarm.Node{}, nil, err
}
body, err := io.ReadAll(serverResp.body)

@ -16,5 +16,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "node", nodeID)
return err
}

@ -3,7 +3,6 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"net/url"
"strconv"
"github.com/docker/docker/api/types/swarm"
)
@ -11,7 +10,7 @@ import (
// NodeUpdate updates a Node.
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10))
query.Set("version", version.String())
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil)
ensureReaderClosed(resp)
return err

@ -18,51 +18,32 @@ type Opt func(*Client) error
// FromEnv configures the client with values from environment variables.
//
// Supported environment variables:
// DOCKER_HOST to set the url to the docker server.
// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// DOCKER_CERT_PATH to load the TLS certificates from.
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
// FromEnv uses the following environment variables:
//
// DOCKER_HOST (EnvOverrideHost) to set the URL to the docker server.
//
// DOCKER_API_VERSION (EnvOverrideAPIVersion) to set the version of the API to
// use, leave empty for latest.
//
// DOCKER_CERT_PATH (EnvOverrideCertPath) to specify the directory from which to
// load the TLS certificates (ca.pem, cert.pem, key.pem).
//
// DOCKER_TLS_VERIFY (EnvTLSVerify) to enable or disable TLS verification (off by
// default).
func FromEnv(c *Client) error {
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
options := tlsconfig.Options{
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
CertFile: filepath.Join(dockerCertPath, "cert.pem"),
KeyFile: filepath.Join(dockerCertPath, "key.pem"),
InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "",
}
tlsc, err := tlsconfig.Client(options)
if err != nil {
return err
}
c.client = &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsc},
CheckRedirect: CheckRedirect,
}
}
if host := os.Getenv("DOCKER_HOST"); host != "" {
if err := WithHost(host)(c); err != nil {
return err
}
ops := []Opt{
WithTLSClientConfigFromEnv(),
WithHostFromEnv(),
WithVersionFromEnv(),
}
if version := os.Getenv("DOCKER_API_VERSION"); version != "" {
if err := WithVersion(version)(c); err != nil {
for _, op := range ops {
if err := op(c); err != nil {
return err
}
}
return nil
}
// WithDialer applies the dialer.DialContext to the client transport. This can be
// used to set the Timeout and KeepAlive settings of the client.
// Deprecated: use WithDialContext
func WithDialer(dialer *net.Dialer) Opt {
return WithDialContext(dialer.DialContext)
}
// WithDialContext applies the dialer to the client transport. This can be
// used to set the Timeout and KeepAlive settings of the client.
func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) Opt {
@ -93,6 +74,18 @@ func WithHost(host string) Opt {
}
}
// WithHostFromEnv overrides the client host with the host specified in the
// DOCKER_HOST (EnvOverrideHost) environment variable. If DOCKER_HOST is not set,
// or set to an empty value, the host is not modified.
func WithHostFromEnv() Opt {
return func(c *Client) error {
if host := os.Getenv(EnvOverrideHost); host != "" {
return WithHost(host)(c)
}
return nil
}
}
// WithHTTPClient overrides the client http client with the specified one
func WithHTTPClient(client *http.Client) Opt {
return func(c *Client) error {
@ -148,6 +141,42 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
}
}
// WithTLSClientConfigFromEnv configures the client's TLS settings with the
// settings in the DOCKER_CERT_PATH and DOCKER_TLS_VERIFY environment variables.
// If DOCKER_CERT_PATH is not set or empty, TLS configuration is not modified.
//
// WithTLSClientConfigFromEnv uses the following environment variables:
//
// DOCKER_CERT_PATH (EnvOverrideCertPath) to specify the directory from which to
// load the TLS certificates (ca.pem, cert.pem, key.pem).
//
// DOCKER_TLS_VERIFY (EnvTLSVerify) to enable or disable TLS verification (off by
// default).
func WithTLSClientConfigFromEnv() Opt {
return func(c *Client) error {
dockerCertPath := os.Getenv(EnvOverrideCertPath)
if dockerCertPath == "" {
return nil
}
options := tlsconfig.Options{
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
CertFile: filepath.Join(dockerCertPath, "cert.pem"),
KeyFile: filepath.Join(dockerCertPath, "key.pem"),
InsecureSkipVerify: os.Getenv(EnvTLSVerify) == "",
}
tlsc, err := tlsconfig.Client(options)
if err != nil {
return err
}
c.client = &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsc},
CheckRedirect: CheckRedirect,
}
return nil
}
}
// WithVersion overrides the client version with the specified one. If an empty
// version is specified, the value will be ignored to allow version negotiation.
func WithVersion(version string) Opt {
@ -160,6 +189,15 @@ func WithVersion(version string) Opt {
}
}
// WithVersionFromEnv overrides the client version with the version specified in
// the DOCKER_API_VERSION environment variable. If DOCKER_API_VERSION is not set,
// the version is not modified.
func WithVersionFromEnv() Opt {
return func(c *Client) error {
return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c)
}
}
// WithAPIVersionNegotiation enables automatic API version negotiation for the client.
// With this option enabled, the client automatically negotiates the API version
// to use when making requests. API version negotiation is performed on the first

@ -4,8 +4,10 @@ import (
"context"
"net/http"
"path"
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/errdefs"
)
@ -61,6 +63,13 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
if bv := resp.header.Get("Builder-Version"); bv != "" {
ping.BuilderVersion = types.BuilderVersion(bv)
}
if si := resp.header.Get("Swarm"); si != "" {
state, role, _ := strings.Cut(si, "/")
ping.SwarmStatus = &swarm.Status{
NodeState: swarm.LocalNodeState(state),
ControlAvailable: role == "manager",
}
}
err := cli.checkResponseErr(resp)
return ping, errdefs.FromStatusCode(err, resp.statusCode)
}

@ -17,7 +17,7 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, nil, wrapResponseError(err, resp, "plugin", name)
return nil, nil, err
}
body, err := io.ReadAll(resp.body)

@ -8,6 +8,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
)
@ -67,12 +68,12 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
}
func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.get(ctx, "/plugins/privileges", query, headers)
}
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/plugins/pull", query, privileges, headers)
}
@ -106,7 +107,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
return nil, err
}
if !accept {
return nil, pluginPermissionDenied{options.RemoteRef}
return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef)
}
}
return privileges, nil

@ -25,7 +25,7 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P
resp, err := cli.get(ctx, "/plugins", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return plugins, wrapResponseError(err, resp, "plugin", "")
return plugins, err
}
err = json.NewDecoder(resp.body).Decode(&plugins)

@ -3,11 +3,13 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"io"
"github.com/docker/docker/api/types/registry"
)
// PluginPush pushes a plugin to a registry
func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, headers)
if err != nil {
return nil, err

@ -16,5 +16,5 @@ func (cli *Client) PluginRemove(ctx context.Context, name string, options types.
resp, err := cli.delete(ctx, "/plugins/"+name, query, nil)
defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "plugin", name)
return err
}

@ -7,6 +7,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/pkg/errors"
)
@ -34,6 +35,6 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types
}
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, headers)
}

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

Loading…
Cancel
Save