mirror of https://github.com/docker/cli.git
vendor: github.com/docker/docker d3afa80b96bf (v25.0.0-dev)
full diff: 06499c52e2...d3afa80b96
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
f90890fb48
commit
3e2187b4cb
|
@ -101,7 +101,6 @@ func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||||
Config: ipamCfg,
|
Config: ipamCfg,
|
||||||
Options: options.ipamOpt.GetAll(),
|
Options: options.ipamOpt.GetAll(),
|
||||||
},
|
},
|
||||||
CheckDuplicate: true,
|
|
||||||
Internal: options.internal,
|
Internal: options.internal,
|
||||||
EnableIPv6: options.ipv6,
|
EnableIPv6: options.ipv6,
|
||||||
Attachable: options.attachable,
|
Attachable: options.attachable,
|
||||||
|
|
|
@ -13,7 +13,7 @@ require (
|
||||||
github.com/creack/pty v1.1.18
|
github.com/creack/pty v1.1.18
|
||||||
github.com/distribution/reference v0.0.0-20230830145923-e42074f83a9c
|
github.com/distribution/reference v0.0.0-20230830145923-e42074f83a9c
|
||||||
github.com/docker/distribution v2.8.2+incompatible
|
github.com/docker/distribution v2.8.2+incompatible
|
||||||
github.com/docker/docker v24.0.0-rc.2.0.20230907222536-06499c52e2b1+incompatible // master (v25.0.0-dev)
|
github.com/docker/docker v24.0.0-rc.2.0.20230921123131-d3afa80b96bf+incompatible // master (v25.0.0-dev)
|
||||||
github.com/docker/docker-credential-helpers v0.8.0
|
github.com/docker/docker-credential-helpers v0.8.0
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
|
|
|
@ -54,8 +54,8 @@ github.com/distribution/reference v0.0.0-20230830145923-e42074f83a9c/go.mod h1:B
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
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/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v24.0.0-rc.2.0.20230907222536-06499c52e2b1+incompatible h1:wSeDxA5kbar3It3D8VVHH50TVWwEs7sAT+UO3UtFtlU=
|
github.com/docker/docker v24.0.0-rc.2.0.20230921123131-d3afa80b96bf+incompatible h1:4TeclqMx5eFwUUQiHqXCB3fMBPxTWVdrILSKISy3IKc=
|
||||||
github.com/docker/docker v24.0.0-rc.2.0.20230907222536-06499c52e2b1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.0-rc.2.0.20230921123131-d3afa80b96bf+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 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
|
||||||
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
|
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
|
||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||||
|
|
|
@ -1363,16 +1363,16 @@ definitions:
|
||||||
EndpointsConfig:
|
EndpointsConfig:
|
||||||
description: |
|
description: |
|
||||||
A mapping of network name to endpoint configuration for that network.
|
A mapping of network name to endpoint configuration for that network.
|
||||||
|
The endpoint configuration can be left empty to connect to that
|
||||||
|
network with no particular endpoint configuration.
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
$ref: "#/definitions/EndpointSettings"
|
$ref: "#/definitions/EndpointSettings"
|
||||||
example:
|
example:
|
||||||
# putting an example here, instead of using the example values from
|
# putting an example here, instead of using the example values from
|
||||||
# /definitions/EndpointSettings, because containers/create currently
|
# /definitions/EndpointSettings, because EndpointSettings contains
|
||||||
# does not support attaching to multiple networks, so the example request
|
# operational data returned when inspecting a container that we don't
|
||||||
# would be confusing if it showed that multiple networks can be contained
|
# accept here.
|
||||||
# in the EndpointsConfig.
|
|
||||||
# TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323)
|
|
||||||
EndpointsConfig:
|
EndpointsConfig:
|
||||||
isolated_nw:
|
isolated_nw:
|
||||||
IPAMConfig:
|
IPAMConfig:
|
||||||
|
@ -1387,6 +1387,7 @@ definitions:
|
||||||
Aliases:
|
Aliases:
|
||||||
- "server_x"
|
- "server_x"
|
||||||
- "server_y"
|
- "server_y"
|
||||||
|
database_nw: {}
|
||||||
|
|
||||||
NetworkSettings:
|
NetworkSettings:
|
||||||
description: "NetworkSettings exposes the network settings in the API"
|
description: "NetworkSettings exposes the network settings in the API"
|
||||||
|
@ -6439,6 +6440,7 @@ paths:
|
||||||
Aliases:
|
Aliases:
|
||||||
- "server_x"
|
- "server_x"
|
||||||
- "server_y"
|
- "server_y"
|
||||||
|
database_nw: {}
|
||||||
|
|
||||||
required: true
|
required: true
|
||||||
responses:
|
responses:
|
||||||
|
@ -9962,13 +9964,7 @@ paths:
|
||||||
type: "string"
|
type: "string"
|
||||||
CheckDuplicate:
|
CheckDuplicate:
|
||||||
description: |
|
description: |
|
||||||
Check for networks with duplicate names. Since Network is
|
Deprecated: CheckDuplicate is now always enabled.
|
||||||
primarily keyed based on a random ID and not on the name, and
|
|
||||||
network name is strictly a user-friendly alias to the network
|
|
||||||
which is uniquely identified using ID, there is no guaranteed
|
|
||||||
way to check for duplicates. CheckDuplicate is there to provide
|
|
||||||
a best effort checking of any networks which has the same name
|
|
||||||
but it is not guaranteed to catch all name collisions.
|
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
Driver:
|
Driver:
|
||||||
description: "Name of the network driver plugin to use."
|
description: "Name of the network driver plugin to use."
|
||||||
|
@ -10042,6 +10038,10 @@ paths:
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "No error"
|
description: "No error"
|
||||||
|
400:
|
||||||
|
description: "bad parameter"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/ErrorResponse"
|
||||||
403:
|
403:
|
||||||
description: "Operation not supported for swarm scoped networks"
|
description: "Operation not supported for swarm scoped networks"
|
||||||
schema:
|
schema:
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/docker/docker/internal/multierror"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EndpointSettings stores the network endpoint details
|
||||||
|
type EndpointSettings struct {
|
||||||
|
// Configurations
|
||||||
|
IPAMConfig *EndpointIPAMConfig
|
||||||
|
Links []string
|
||||||
|
Aliases []string
|
||||||
|
// Operational data
|
||||||
|
NetworkID string
|
||||||
|
EndpointID string
|
||||||
|
Gateway string
|
||||||
|
IPAddress string
|
||||||
|
IPPrefixLen int
|
||||||
|
IPv6Gateway string
|
||||||
|
GlobalIPv6Address string
|
||||||
|
GlobalIPv6PrefixLen int
|
||||||
|
MacAddress string
|
||||||
|
DriverOpts map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of `EndpointSettings`
|
||||||
|
func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||||
|
epCopy := *es
|
||||||
|
if es.IPAMConfig != nil {
|
||||||
|
epCopy.IPAMConfig = es.IPAMConfig.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
if es.Links != nil {
|
||||||
|
links := make([]string, 0, len(es.Links))
|
||||||
|
epCopy.Links = append(links, es.Links...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if es.Aliases != nil {
|
||||||
|
aliases := make([]string, 0, len(es.Aliases))
|
||||||
|
epCopy.Aliases = append(aliases, es.Aliases...)
|
||||||
|
}
|
||||||
|
return &epCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||||
|
type EndpointIPAMConfig struct {
|
||||||
|
IPv4Address string `json:",omitempty"`
|
||||||
|
IPv6Address string `json:",omitempty"`
|
||||||
|
LinkLocalIPs []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a copy of the endpoint ipam config
|
||||||
|
func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig {
|
||||||
|
cfgCopy := *cfg
|
||||||
|
cfgCopy.LinkLocalIPs = make([]string, 0, len(cfg.LinkLocalIPs))
|
||||||
|
cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...)
|
||||||
|
return &cfgCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkSubnet describes a user-defined subnet for a specific network. It's only used to validate if an
|
||||||
|
// EndpointIPAMConfig is valid for a specific network.
|
||||||
|
type NetworkSubnet interface {
|
||||||
|
// Contains checks whether the NetworkSubnet contains [addr].
|
||||||
|
Contains(addr net.IP) bool
|
||||||
|
// IsStatic checks whether the subnet was statically allocated (ie. user-defined).
|
||||||
|
IsStatic() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInRange checks whether static IP addresses are valid in a specific network.
|
||||||
|
func (cfg *EndpointIPAMConfig) IsInRange(v4Subnets []NetworkSubnet, v6Subnets []NetworkSubnet) error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if err := validateEndpointIPAddress(cfg.IPv4Address, v4Subnets); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
if err := validateEndpointIPAddress(cfg.IPv6Address, v6Subnets); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return multierror.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEndpointIPAddress(epAddr string, ipamSubnets []NetworkSubnet) error {
|
||||||
|
if epAddr == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var staticSubnet bool
|
||||||
|
parsedAddr := net.ParseIP(epAddr)
|
||||||
|
for _, subnet := range ipamSubnets {
|
||||||
|
if subnet.IsStatic() {
|
||||||
|
staticSubnet = true
|
||||||
|
if subnet.Contains(parsedAddr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticSubnet {
|
||||||
|
return fmt.Errorf("no configured subnet or ip-range contain the IP address %s", epAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("user specified IP address is supported only when connecting to networks with user configured subnets")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks whether cfg is valid.
|
||||||
|
func (cfg *EndpointIPAMConfig) Validate() error {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if cfg.IPv4Address != "" {
|
||||||
|
if addr := net.ParseIP(cfg.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
|
||||||
|
errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", cfg.IPv4Address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cfg.IPv6Address != "" {
|
||||||
|
if addr := net.ParseIP(cfg.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
|
||||||
|
errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", cfg.IPv6Address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, addr := range cfg.LinkLocalIPs {
|
||||||
|
if parsed := net.ParseIP(addr); parsed == nil || parsed.IsUnspecified() {
|
||||||
|
errs = append(errs, fmt.Errorf("invalid link-local IP address: %s", addr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multierror.Join(errs...)
|
||||||
|
}
|
|
@ -30,7 +30,30 @@ const (
|
||||||
ip6 ipFamily = "IPv6"
|
ip6 ipFamily = "IPv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ValidateIPAM(ipam *IPAM, enableIPv6 bool) error {
|
// HasIPv6Subnets checks whether there's any IPv6 subnets in the ipam parameter. It ignores any invalid Subnet and nil
|
||||||
|
// ipam.
|
||||||
|
func HasIPv6Subnets(ipam *IPAM) bool {
|
||||||
|
if ipam == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cfg := range ipam.Config {
|
||||||
|
subnet, err := netip.ParsePrefix(cfg.Subnet)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if subnet.Addr().Is6() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateIPAM checks whether the network's IPAM passed as argument is valid. It returns a joinError of the list of
|
||||||
|
// errors found.
|
||||||
|
func ValidateIPAM(ipam *IPAM) error {
|
||||||
if ipam == nil {
|
if ipam == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -51,10 +74,6 @@ func ValidateIPAM(ipam *IPAM, enableIPv6 bool) error {
|
||||||
errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked()))
|
errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enableIPv6 && subnetFamily == ip6 {
|
|
||||||
errs = append(errs, fmt.Errorf("invalid subnet %s: IPv6 has not been enabled for this network", subnet))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 {
|
if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 {
|
||||||
errs = append(errs, ipRangeErrs...)
|
errs = append(errs, ipRangeErrs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,46 +9,12 @@ type Address struct {
|
||||||
PrefixLen int
|
PrefixLen int
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
|
||||||
type EndpointIPAMConfig struct {
|
|
||||||
IPv4Address string `json:",omitempty"`
|
|
||||||
IPv6Address string `json:",omitempty"`
|
|
||||||
LinkLocalIPs []string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a copy of the endpoint ipam config
|
|
||||||
func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig {
|
|
||||||
cfgCopy := *cfg
|
|
||||||
cfgCopy.LinkLocalIPs = make([]string, 0, len(cfg.LinkLocalIPs))
|
|
||||||
cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...)
|
|
||||||
return &cfgCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeerInfo represents one peer of an overlay network
|
// PeerInfo represents one peer of an overlay network
|
||||||
type PeerInfo struct {
|
type PeerInfo struct {
|
||||||
Name string
|
Name string
|
||||||
IP string
|
IP string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointSettings stores the network endpoint details
|
|
||||||
type EndpointSettings struct {
|
|
||||||
// Configurations
|
|
||||||
IPAMConfig *EndpointIPAMConfig
|
|
||||||
Links []string
|
|
||||||
Aliases []string
|
|
||||||
// Operational data
|
|
||||||
NetworkID string
|
|
||||||
EndpointID string
|
|
||||||
Gateway string
|
|
||||||
IPAddress string
|
|
||||||
IPPrefixLen int
|
|
||||||
IPv6Gateway string
|
|
||||||
GlobalIPv6Address string
|
|
||||||
GlobalIPv6PrefixLen int
|
|
||||||
MacAddress string
|
|
||||||
DriverOpts map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Task carries the information about one backend task
|
// Task carries the information about one backend task
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -65,25 +31,6 @@ type ServiceInfo struct {
|
||||||
Tasks []Task
|
Tasks []Task
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy makes a deep copy of `EndpointSettings`
|
|
||||||
func (es *EndpointSettings) Copy() *EndpointSettings {
|
|
||||||
epCopy := *es
|
|
||||||
if es.IPAMConfig != nil {
|
|
||||||
epCopy.IPAMConfig = es.IPAMConfig.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
if es.Links != nil {
|
|
||||||
links := make([]string, 0, len(es.Links))
|
|
||||||
epCopy.Links = append(links, es.Links...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if es.Aliases != nil {
|
|
||||||
aliases := make([]string, 0, len(es.Aliases))
|
|
||||||
epCopy.Aliases = append(aliases, es.Aliases...)
|
|
||||||
}
|
|
||||||
return &epCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkingConfig represents the container's networking configuration for each of its interfaces
|
// NetworkingConfig represents the container's networking configuration for each of its interfaces
|
||||||
// Carries the networking configs specified in the `docker run` and `docker network connect` commands
|
// Carries the networking configs specified in the `docker run` and `docker network connect` commands
|
||||||
type NetworkingConfig struct {
|
type NetworkingConfig struct {
|
||||||
|
|
|
@ -443,14 +443,9 @@ type EndpointResource struct {
|
||||||
|
|
||||||
// NetworkCreate is the expected body of the "create network" http request message
|
// NetworkCreate is the expected body of the "create network" http request message
|
||||||
type NetworkCreate struct {
|
type NetworkCreate struct {
|
||||||
// Check for networks with duplicate names.
|
// Deprecated: CheckDuplicate is deprecated since API v1.44, but it defaults to true when sent by the client
|
||||||
// Network is primarily keyed based on a random ID and not on the name.
|
// package to older daemons.
|
||||||
// Network name is strictly a user-friendly alias to the network
|
CheckDuplicate bool `json:",omitempty"`
|
||||||
// which is uniquely identified using ID.
|
|
||||||
// And there is no guaranteed way to check for duplicates.
|
|
||||||
// Option CheckDuplicate is there to provide a best effort checking of any networks
|
|
||||||
// which has the same name but it is not guaranteed to catch all name collisions.
|
|
||||||
CheckDuplicate bool
|
|
||||||
Driver string
|
Driver string
|
||||||
Scope string
|
Scope string
|
||||||
EnableIPv6 bool
|
EnableIPv6 bool
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
// BuildCachePrune requests the daemon to delete unused cache data
|
// BuildCachePrune requests the daemon to delete unused cache data
|
||||||
func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
|
func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
|
||||||
if err := cli.NewVersionError("1.31", "build prune"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.31", "build prune"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -254,13 +254,21 @@ func (cli *Client) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkVersion manually triggers API version negotiation (if configured).
|
||||||
|
// This allows for version-dependent code to use the same version as will
|
||||||
|
// be negotiated when making the actual requests, and for which cases
|
||||||
|
// we cannot do the negotiation lazily.
|
||||||
|
func (cli *Client) checkVersion(ctx context.Context) {
|
||||||
|
if cli.negotiateVersion && !cli.negotiated {
|
||||||
|
cli.NegotiateAPIVersion(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getAPIPath returns the versioned request path to call the API.
|
// getAPIPath returns the versioned request path to call the API.
|
||||||
// It appends the query parameters to the path if they are not empty.
|
// It appends the query parameters to the path if they are not empty.
|
||||||
func (cli *Client) getAPIPath(ctx context.Context, p string, query url.Values) string {
|
func (cli *Client) getAPIPath(ctx context.Context, p string, query url.Values) string {
|
||||||
var apiPath string
|
var apiPath string
|
||||||
if cli.negotiateVersion && !cli.negotiated {
|
cli.checkVersion(ctx)
|
||||||
cli.NegotiateAPIVersion(ctx)
|
|
||||||
}
|
|
||||||
if cli.version != "" {
|
if cli.version != "" {
|
||||||
v := strings.TrimPrefix(cli.version, "v")
|
v := strings.TrimPrefix(cli.version, "v")
|
||||||
apiPath = path.Join(cli.basePath, "/v"+v, p)
|
apiPath = path.Join(cli.basePath, "/v"+v, p)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// ConfigCreate creates a new config.
|
// ConfigCreate creates a new config.
|
||||||
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||||
var response types.ConfigCreateResponse
|
var response types.ConfigCreateResponse
|
||||||
if err := cli.NewVersionError("1.30", "config create"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config create"); err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
resp, err := cli.post(ctx, "/configs/create", nil, config, nil)
|
resp, err := cli.post(ctx, "/configs/create", nil, config, nil)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id}
|
return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id}
|
||||||
}
|
}
|
||||||
if err := cli.NewVersionError("1.30", "config inspect"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config inspect"); err != nil {
|
||||||
return swarm.Config{}, nil, err
|
return swarm.Config{}, nil, err
|
||||||
}
|
}
|
||||||
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
|
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// ConfigList returns the list of configs.
|
// ConfigList returns the list of configs.
|
||||||
func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||||
if err := cli.NewVersionError("1.30", "config list"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config list"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "context"
|
||||||
|
|
||||||
// ConfigRemove removes a config.
|
// ConfigRemove removes a config.
|
||||||
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
||||||
if err := cli.NewVersionError("1.30", "config remove"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
|
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
// 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 {
|
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 {
|
if err := cli.NewVersionError(ctx, "1.30", "config update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
@ -23,13 +23,20 @@ type configWrapper struct {
|
||||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
|
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
|
var response container.CreateResponse
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
|
if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
|
if err := cli.NewVersionError(ctx, "1.41", "specify container image platform"); platform != nil && err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
if err := cli.NewVersionError("1.44", "specify health-check start interval"); config != nil && config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil {
|
if err := cli.NewVersionError(ctx, "1.44", "specify health-check start interval"); config != nil && config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,14 @@ import (
|
||||||
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) {
|
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) {
|
||||||
var response types.IDResponse
|
var response types.IDResponse
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil {
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
|
if err := cli.NewVersionError(ctx, "1.25", "env"); len(config.Env) != 0 && err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
if versions.LessThan(cli.ClientVersion(), "1.42") {
|
if versions.LessThan(cli.ClientVersion(), "1.42") {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
|
func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
|
||||||
var report types.ContainersPruneReport
|
var report types.ContainersPruneReport
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "container prune"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "container prune"); err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,17 @@ func (cli *Client) ContainerRestart(ctx context.Context, containerID string, opt
|
||||||
if options.Timeout != nil {
|
if options.Timeout != nil {
|
||||||
query.Set("t", strconv.Itoa(*options.Timeout))
|
query.Set("t", strconv.Itoa(*options.Timeout))
|
||||||
}
|
}
|
||||||
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
if options.Signal != "" {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
if versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
||||||
query.Set("signal", options.Signal)
|
query.Set("signal", options.Signal)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -21,9 +21,17 @@ func (cli *Client) ContainerStop(ctx context.Context, containerID string, option
|
||||||
if options.Timeout != nil {
|
if options.Timeout != nil {
|
||||||
query.Set("t", strconv.Itoa(*options.Timeout))
|
query.Set("t", strconv.Itoa(*options.Timeout))
|
||||||
}
|
}
|
||||||
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
if options.Signal != "" {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
if versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
||||||
query.Set("signal", options.Signal)
|
query.Set("signal", options.Signal)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -30,6 +30,12 @@ const containerWaitErrorMsgLimit = 2 * 1024 /* Max: 2KiB */
|
||||||
// synchronize ContainerWait with other calls, such as specifying a
|
// synchronize ContainerWait with other calls, such as specifying a
|
||||||
// "next-exit" condition before issuing a ContainerStart request.
|
// "next-exit" condition before issuing a ContainerStart request.
|
||||||
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
|
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
if versions.LessThan(cli.ClientVersion(), "1.30") {
|
if versions.LessThan(cli.ClientVersion(), "1.30") {
|
||||||
return cli.legacyContainerWait(ctx, containerID)
|
return cli.legacyContainerWait(ctx, containerID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist
|
||||||
return distributionInspect, objectNotFoundError{object: "distribution", id: image}
|
return distributionInspect, objectNotFoundError{object: "distribution", id: image}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.30", "distribution inspect"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "distribution inspect"); err != nil {
|
||||||
return distributionInspect, err
|
return distributionInspect, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package client // import "github.com/docker/docker/client"
|
package client // import "github.com/docker/docker/client"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
@ -48,9 +49,18 @@ func (e objectNotFoundError) Error() string {
|
||||||
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
|
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVersionError returns an error if the APIVersion required
|
// NewVersionError returns an error if the APIVersion required is less than the
|
||||||
// if less than the current supported version
|
// current supported version.
|
||||||
func (cli *Client) NewVersionError(APIrequired, feature string) error {
|
//
|
||||||
|
// It performs API-version negotiation if the Client is configured with this
|
||||||
|
// option, otherwise it assumes the latest API version is used.
|
||||||
|
func (cli *Client) NewVersionError(ctx context.Context, APIrequired, feature string) error {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
if cli.version != "" && versions.LessThan(cli.version, APIrequired) {
|
if cli.version != "" && versions.LessThan(cli.version, APIrequired) {
|
||||||
return fmt.Errorf("%q requires API version %s, but the Docker daemon API version is %s", feature, APIrequired, cli.version)
|
return fmt.Errorf("%q requires API version %s, but the Docker daemon API version is %s", feature, APIrequired, cli.version)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
// The Body in the response implements an io.ReadCloser and it's up to the caller to
|
// The Body in the response implements an io.ReadCloser and it's up to the caller to
|
||||||
// close it.
|
// close it.
|
||||||
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||||
query, err := cli.imageBuildOptionsToQuery(options)
|
query, err := cli.imageBuildOptionsToQuery(ctx, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.ImageBuildResponse{}, err
|
return types.ImageBuildResponse{}, err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) {
|
func (cli *Client) imageBuildOptionsToQuery(ctx context.Context, options types.ImageBuildOptions) (url.Values, error) {
|
||||||
query := url.Values{
|
query := url.Values{
|
||||||
"t": options.Tags,
|
"t": options.Tags,
|
||||||
"securityopt": options.SecurityOpt,
|
"securityopt": options.SecurityOpt,
|
||||||
|
@ -73,7 +73,7 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Squash {
|
if options.Squash {
|
||||||
if err := cli.NewVersionError("1.25", "squash"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "squash"); err != nil {
|
||||||
return query, err
|
return query, err
|
||||||
}
|
}
|
||||||
query.Set("squash", "1")
|
query.Set("squash", "1")
|
||||||
|
@ -123,7 +123,7 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
|
||||||
query.Set("session", options.SessionID)
|
query.Set("session", options.SessionID)
|
||||||
}
|
}
|
||||||
if options.Platform != "" {
|
if options.Platform != "" {
|
||||||
if err := cli.NewVersionError("1.32", "platform"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.32", "platform"); err != nil {
|
||||||
return query, err
|
return query, err
|
||||||
}
|
}
|
||||||
query.Set("platform", strings.ToLower(options.Platform))
|
query.Set("platform", strings.ToLower(options.Platform))
|
||||||
|
|
|
@ -12,6 +12,13 @@ import (
|
||||||
|
|
||||||
// ImageList returns a list of images in the docker host.
|
// ImageList returns a list of images in the docker host.
|
||||||
func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
|
func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
var images []types.ImageSummary
|
var images []types.ImageSummary
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (types.ImagesPruneReport, error) {
|
func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (types.ImagesPruneReport, error) {
|
||||||
var report types.ImagesPruneReport
|
var report types.ImagesPruneReport
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "image prune"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "image prune"); err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,26 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetworkCreate creates a new network in the docker host.
|
// NetworkCreate creates a new network in the docker host.
|
||||||
func (cli *Client) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
func (cli *Client) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
networkCreateRequest := types.NetworkCreateRequest{
|
networkCreateRequest := types.NetworkCreateRequest{
|
||||||
NetworkCreate: options,
|
NetworkCreate: options,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
if versions.LessThan(cli.version, "1.44") {
|
||||||
|
networkCreateRequest.CheckDuplicate = true //nolint:staticcheck // ignore SA1019: CheckDuplicate is deprecated since API v1.44.
|
||||||
|
}
|
||||||
|
|
||||||
var response types.NetworkCreateResponse
|
var response types.NetworkCreateResponse
|
||||||
serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil)
|
serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) {
|
func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) {
|
||||||
var report types.NetworksPruneReport
|
var report types.NetworksPruneReport
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "network prune"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "network prune"); err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
// PluginUpgrade upgrades a plugin
|
// PluginUpgrade upgrades a plugin
|
||||||
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
|
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
|
||||||
if err := cli.NewVersionError("1.26", "plugin upgrade"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.26", "plugin upgrade"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// SecretCreate creates a new secret.
|
// SecretCreate creates a new secret.
|
||||||
func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||||
var response types.SecretCreateResponse
|
var response types.SecretCreateResponse
|
||||||
if err := cli.NewVersionError("1.25", "secret create"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret create"); err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil)
|
resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
// SecretInspectWithRaw returns the secret information with raw data
|
// SecretInspectWithRaw returns the secret information with raw data
|
||||||
func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
|
func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
|
||||||
if err := cli.NewVersionError("1.25", "secret inspect"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret inspect"); err != nil {
|
||||||
return swarm.Secret{}, nil, err
|
return swarm.Secret{}, nil, err
|
||||||
}
|
}
|
||||||
if id == "" {
|
if id == "" {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// SecretList returns the list of secrets.
|
// SecretList returns the list of secrets.
|
||||||
func (cli *Client) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
func (cli *Client) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||||
if err := cli.NewVersionError("1.25", "secret list"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret list"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "context"
|
||||||
|
|
||||||
// SecretRemove removes a secret.
|
// SecretRemove removes a secret.
|
||||||
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
||||||
if err := cli.NewVersionError("1.25", "secret remove"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
|
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
// SecretUpdate attempts to update a secret.
|
// SecretUpdate attempts to update a secret.
|
||||||
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
||||||
if err := cli.NewVersionError("1.25", "secret update"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
@ -20,6 +20,13 @@ import (
|
||||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
|
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
|
||||||
var response types.ServiceCreateResponse
|
var response types.ServiceCreateResponse
|
||||||
|
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
||||||
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
||||||
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||||
|
|
|
@ -16,6 +16,13 @@ import (
|
||||||
// It should be the value as set *before* the update. You can find this value in the Meta field
|
// It should be the value as set *before* the update. You can find this value in the Meta field
|
||||||
// of swarm.Service, which can be found using ServiceInspectWithRaw.
|
// of swarm.Service, which can be found using ServiceInspectWithRaw.
|
||||||
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
query = url.Values{}
|
query = url.Values{}
|
||||||
response = types.ServiceUpdateResponse{}
|
response = types.ServiceUpdateResponse{}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (types.VolumesPruneReport, error) {
|
func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (types.VolumesPruneReport, error) {
|
||||||
var report types.VolumesPruneReport
|
var report types.VolumesPruneReport
|
||||||
|
|
||||||
if err := cli.NewVersionError("1.25", "volume prune"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "volume prune"); err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,14 @@ import (
|
||||||
// VolumeRemove removes a volume from the docker host.
|
// VolumeRemove removes a volume from the docker host.
|
||||||
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
|
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if versions.GreaterThanOrEqualTo(cli.version, "1.25") {
|
|
||||||
if force {
|
if force {
|
||||||
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
// as code below contains API-version specific handling of options.
|
||||||
|
//
|
||||||
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
|
// the API request is made.
|
||||||
|
cli.checkVersion(ctx)
|
||||||
|
if versions.GreaterThanOrEqualTo(cli.version, "1.25") {
|
||||||
query.Set("force", "1")
|
query.Set("force", "1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// VolumeUpdate updates a volume. This only works for Cluster Volumes, and
|
// VolumeUpdate updates a volume. This only works for Cluster Volumes, and
|
||||||
// only some fields can be updated.
|
// only some fields can be updated.
|
||||||
func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error {
|
func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error {
|
||||||
if err := cli.NewVersionError("1.42", "volume update"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.42", "volume update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,12 +14,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// v1PingResult contains the information returned when pinging a registry. It
|
// v1PingResult contains the information returned when pinging a registry. It
|
||||||
// indicates the registry's version and whether the registry claims to be a
|
// indicates whether the registry claims to be a standalone registry.
|
||||||
// standalone registry.
|
|
||||||
type v1PingResult struct {
|
type v1PingResult struct {
|
||||||
// Version is the registry version supplied by the registry in an HTTP
|
|
||||||
// header
|
|
||||||
Version string `json:"version"`
|
|
||||||
// Standalone is set to true if the registry indicates it is a
|
// Standalone is set to true if the registry indicates it is a
|
||||||
// standalone registry in the X-Docker-Registry-Standalone
|
// standalone registry in the X-Docker-Registry-Standalone
|
||||||
// header
|
// header
|
||||||
|
@ -47,39 +42,30 @@ func newV1Endpoint(index *registry.IndexInfo, headers http.Header) (*v1Endpoint,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateEndpoint(endpoint)
|
if endpoint.String() == IndexServer {
|
||||||
if err != nil {
|
// Skip the check, we know this one is valid
|
||||||
return nil, err
|
// (and we never want to fall back to http in case of error)
|
||||||
}
|
|
||||||
|
|
||||||
return endpoint, nil
|
return endpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateEndpoint(endpoint *v1Endpoint) error {
|
|
||||||
log.G(context.TODO()).Debugf("pinging registry endpoint %s", endpoint)
|
|
||||||
|
|
||||||
// Try HTTPS ping to registry
|
// Try HTTPS ping to registry
|
||||||
endpoint.URL.Scheme = "https"
|
endpoint.URL.Scheme = "https"
|
||||||
if _, err := endpoint.ping(); err != nil {
|
if _, err := endpoint.ping(); err != nil {
|
||||||
if endpoint.IsSecure {
|
if endpoint.IsSecure {
|
||||||
// If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry`
|
// If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry`
|
||||||
// in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fall back to HTTP.
|
// in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fall back to HTTP.
|
||||||
return invalidParamf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host)
|
return nil, invalidParamf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If registry is insecure and HTTPS failed, fallback to HTTP.
|
// registry is insecure and HTTPS failed, fallback to HTTP.
|
||||||
log.G(context.TODO()).WithError(err).Debugf("error from registry %q marked as insecure - insecurely falling back to HTTP", endpoint)
|
log.G(context.TODO()).WithError(err).Debugf("error from registry %q marked as insecure - insecurely falling back to HTTP", endpoint)
|
||||||
endpoint.URL.Scheme = "http"
|
endpoint.URL.Scheme = "http"
|
||||||
|
if _, err2 := endpoint.ping(); err2 != nil {
|
||||||
var err2 error
|
return nil, invalidParamf("invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2)
|
||||||
if _, err2 = endpoint.ping(); err2 == nil {
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidParamf("invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2)
|
return endpoint, nil
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trimV1Address trims the "v1" version suffix off the address and returns
|
// trimV1Address trims the "v1" version suffix off the address and returns
|
||||||
|
@ -130,8 +116,8 @@ func (e *v1Endpoint) ping() (v1PingResult, error) {
|
||||||
return v1PingResult{}, nil
|
return v1PingResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.G(context.TODO()).Debugf("attempting v1 ping for registry endpoint %s", e)
|
|
||||||
pingURL := e.String() + "_ping"
|
pingURL := e.String() + "_ping"
|
||||||
|
log.G(context.TODO()).WithField("url", pingURL).Debug("attempting v1 ping for registry endpoint")
|
||||||
req, err := http.NewRequest(http.MethodGet, pingURL, nil)
|
req, err := http.NewRequest(http.MethodGet, pingURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v1PingResult{}, invalidParam(err)
|
return v1PingResult{}, invalidParam(err)
|
||||||
|
@ -144,9 +130,14 @@ func (e *v1Endpoint) ping() (v1PingResult, error) {
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
jsonString, err := io.ReadAll(resp.Body)
|
if v := resp.Header.Get("X-Docker-Registry-Standalone"); v != "" {
|
||||||
if err != nil {
|
info := v1PingResult{}
|
||||||
return v1PingResult{}, invalidParamWrapf(err, "error while reading response from %s", pingURL)
|
// Accepted values are "1", and "true" (case-insensitive).
|
||||||
|
if v == "1" || strings.EqualFold(v, "true") {
|
||||||
|
info.Standalone = true
|
||||||
|
}
|
||||||
|
log.G(context.TODO()).Debugf("v1PingResult.Standalone (from X-Docker-Registry-Standalone header): %t", info.Standalone)
|
||||||
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the header is absent, we assume true for compatibility with earlier
|
// If the header is absent, we assume true for compatibility with earlier
|
||||||
|
@ -154,24 +145,11 @@ func (e *v1Endpoint) ping() (v1PingResult, error) {
|
||||||
info := v1PingResult{
|
info := v1PingResult{
|
||||||
Standalone: true,
|
Standalone: true,
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(jsonString, &info); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||||
log.G(context.TODO()).WithError(err).Debug("error unmarshaling _ping response")
|
log.G(context.TODO()).WithError(err).Debug("error unmarshaling _ping response")
|
||||||
// don't stop here. Just assume sane defaults
|
// don't stop here. Just assume sane defaults
|
||||||
}
|
}
|
||||||
if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
|
|
||||||
info.Version = hdr
|
|
||||||
}
|
|
||||||
log.G(context.TODO()).Debugf("v1PingResult.Version: %q", info.Version)
|
|
||||||
|
|
||||||
standalone := resp.Header.Get("X-Docker-Registry-Standalone")
|
|
||||||
|
|
||||||
// Accepted values are "true" (case-insensitive) and "1".
|
|
||||||
if strings.EqualFold(standalone, "true") || standalone == "1" {
|
|
||||||
info.Standalone = true
|
|
||||||
} else if len(standalone) > 0 {
|
|
||||||
// there is a header set, and it is not "true" or "1", so assume fails
|
|
||||||
info.Standalone = false
|
|
||||||
}
|
|
||||||
log.G(context.TODO()).Debugf("v1PingResult.Standalone: %t", info.Standalone)
|
log.G(context.TODO()).Debugf("v1PingResult.Standalone: %t", info.Standalone)
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ import (
|
||||||
"github.com/docker/docker/api/types/registry"
|
"github.com/docker/docker/api/types/registry"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -208,10 +206,8 @@ func (r *session) searchRepositories(term string, limit int) (*registry.SearchRe
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
return nil, errdefs.Unknown(&jsonmessage.JSONError{
|
// TODO(thaJeztah): return upstream response body for errors (see https://github.com/moby/moby/issues/27286).
|
||||||
Message: "Unexpected status code " + strconv.Itoa(res.StatusCode),
|
return nil, errdefs.Unknown(fmt.Errorf("Unexpected status code %d", res.StatusCode))
|
||||||
Code: res.StatusCode,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
result := ®istry.SearchResults{}
|
result := ®istry.SearchResults{}
|
||||||
err = json.NewDecoder(res.Body).Decode(result)
|
err = json.NewDecoder(res.Body).Decode(result)
|
||||||
|
|
|
@ -55,7 +55,7 @@ github.com/docker/distribution/registry/client/transport
|
||||||
github.com/docker/distribution/registry/storage/cache
|
github.com/docker/distribution/registry/storage/cache
|
||||||
github.com/docker/distribution/registry/storage/cache/memory
|
github.com/docker/distribution/registry/storage/cache/memory
|
||||||
github.com/docker/distribution/uuid
|
github.com/docker/distribution/uuid
|
||||||
# github.com/docker/docker v24.0.0-rc.2.0.20230907222536-06499c52e2b1+incompatible
|
# github.com/docker/docker v24.0.0-rc.2.0.20230921123131-d3afa80b96bf+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
github.com/docker/docker/api
|
github.com/docker/docker/api
|
||||||
github.com/docker/docker/api/types
|
github.com/docker/docker/api/types
|
||||||
|
|
Loading…
Reference in New Issue