mirror of
https://github.com/moby/moby.git
synced 2026-01-12 03:01:38 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71fa3ab079 | ||
|
|
5295e88ceb | ||
|
|
6eef840b8a | ||
|
|
e2ab4718c8 | ||
|
|
3de920a0b1 | ||
|
|
a445aa95e5 | ||
|
|
cb77e48229 | ||
|
|
e8801fbe26 | ||
|
|
613b6a12c1 | ||
|
|
1b6738369f | ||
|
|
b8cc2e8c66 | ||
|
|
fcccfeb811 | ||
|
|
f8eaa14a18 | ||
|
|
ac76925ff2 | ||
|
|
c7a1d928c0 | ||
|
|
2672baefd7 | ||
|
|
ff15b49b47 | ||
|
|
c0573b133f | ||
|
|
c7466c0b52 | ||
|
|
dde33d0dfe | ||
|
|
39fedb254b | ||
|
|
f0f5fc974a | ||
|
|
7c185a1e40 | ||
|
|
2b036fb1da | ||
|
|
1f24da70d8 | ||
|
|
358fecb566 | ||
|
|
f030b25770 | ||
|
|
e07aed0f77 | ||
|
|
cdf3611cff | ||
|
|
05267e9e8c | ||
|
|
e5edf62bca | ||
|
|
e14d121d49 | ||
|
|
e0acf1cd70 | ||
|
|
c2847b2eb2 | ||
|
|
0894f7fe69 | ||
|
|
d25aa32c21 | ||
|
|
1e335cfa74 | ||
|
|
4d287e9267 | ||
|
|
0240f5675b | ||
|
|
13964248f1 |
@@ -8,12 +8,12 @@ ARG XX_VERSION=1.2.1
|
||||
ARG VPNKIT_VERSION=0.5.0
|
||||
|
||||
ARG DOCKERCLI_REPOSITORY="https://github.com/docker/cli.git"
|
||||
ARG DOCKERCLI_VERSION=v24.0.2
|
||||
ARG DOCKERCLI_VERSION=v25.0.0
|
||||
# cli version used for integration-cli tests
|
||||
ARG DOCKERCLI_INTEGRATION_REPOSITORY="https://github.com/docker/cli.git"
|
||||
ARG DOCKERCLI_INTEGRATION_VERSION=v17.06.2-ce
|
||||
ARG BUILDX_VERSION=0.12.1
|
||||
ARG COMPOSE_VERSION=v2.24.0
|
||||
ARG COMPOSE_VERSION=v2.24.2
|
||||
|
||||
ARG SYSTEMD="false"
|
||||
ARG DOCKER_STATIC=1
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/moby/sys/symlink"
|
||||
"github.com/pkg/errors"
|
||||
@@ -24,9 +25,11 @@ func (c *archiveContext) Close() error {
|
||||
}
|
||||
|
||||
func convertPathError(err error, cleanpath string) error {
|
||||
if err, ok := err.(*os.PathError); ok {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
err.Path = cleanpath
|
||||
case *system.XattrError:
|
||||
err.Path = cleanpath
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// urlPathWithFragmentSuffix matches fragments to use as Git reference and build
|
||||
// context from the Git repository. See IsGitURL for details.
|
||||
var urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$")
|
||||
var urlPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
|
||||
|
||||
// IsURL returns true if the provided str is an HTTP(S) URL by checking if it
|
||||
// has a http:// or https:// scheme. No validation is performed to verify if the
|
||||
|
||||
@@ -17,6 +17,7 @@ var (
|
||||
}
|
||||
invalidGitUrls = []string{
|
||||
"http://github.com/docker/docker.git:#branch",
|
||||
"https://github.com/docker/dgit",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -301,7 +301,13 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
|
||||
routerCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
routerOptions, err := newRouterOptions(routerCtx, cli.Config, d)
|
||||
// Get a the current daemon config, because the daemon sets up config
|
||||
// during initialization. We cannot user the cli.Config for that reason,
|
||||
// as that only holds the config that was set by the user.
|
||||
//
|
||||
// FIXME(thaJeztah): better separate runtime and config data?
|
||||
daemonCfg := d.Config()
|
||||
routerOptions, err := newRouterOptions(routerCtx, &daemonCfg, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -377,12 +377,14 @@ func (c *containerConfig) healthcheck() *enginecontainer.HealthConfig {
|
||||
interval, _ := gogotypes.DurationFromProto(hcSpec.Interval)
|
||||
timeout, _ := gogotypes.DurationFromProto(hcSpec.Timeout)
|
||||
startPeriod, _ := gogotypes.DurationFromProto(hcSpec.StartPeriod)
|
||||
startInterval, _ := gogotypes.DurationFromProto(hcSpec.StartInterval)
|
||||
return &enginecontainer.HealthConfig{
|
||||
Test: hcSpec.Test,
|
||||
Interval: interval,
|
||||
Timeout: timeout,
|
||||
Retries: int(hcSpec.Retries),
|
||||
StartPeriod: startPeriod,
|
||||
Test: hcSpec.Test,
|
||||
Interval: interval,
|
||||
Timeout: timeout,
|
||||
Retries: int(hcSpec.Retries),
|
||||
StartPeriod: startPeriod,
|
||||
StartInterval: startInterval,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -637,6 +637,9 @@ func cleanOperationalData(es *network.EndpointSettings) {
|
||||
if es.IPAMOperational {
|
||||
es.IPAMConfig = nil
|
||||
}
|
||||
if es.MACOperational {
|
||||
es.MacAddress = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (daemon *Daemon) updateNetworkConfig(container *container.Container, n *libnetwork.Network, endpointConfig *networktypes.EndpointSettings, updateSettings bool) error {
|
||||
@@ -645,7 +648,7 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, n *lib
|
||||
}
|
||||
|
||||
if err := validateEndpointSettings(n, n.Name(), endpointConfig); err != nil {
|
||||
return err
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
if updateSettings {
|
||||
@@ -708,6 +711,7 @@ func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.
|
||||
}
|
||||
|
||||
var operIPAM bool
|
||||
operMAC := true
|
||||
if nwCfg != nil {
|
||||
if epConfig, ok := nwCfg.EndpointsConfig[nwName]; ok {
|
||||
if endpointConfig.IPAMConfig == nil || (endpointConfig.IPAMConfig.IPv4Address == "" && endpointConfig.IPAMConfig.IPv6Address == "" && len(endpointConfig.IPAMConfig.LinkLocalIPs) == 0) {
|
||||
@@ -717,6 +721,11 @@ func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.
|
||||
// copy IPAMConfig and NetworkID from epConfig via AttachNetwork
|
||||
endpointConfig.IPAMConfig = epConfig.IPAMConfig
|
||||
endpointConfig.NetworkID = epConfig.NetworkID
|
||||
|
||||
// Work out whether the MAC address is user-configured.
|
||||
operMAC = endpointConfig.MacAddress == ""
|
||||
// Copy the configured MAC address (which may be empty).
|
||||
endpointConfig.MacAddress = epConfig.MacAddress
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,6 +755,7 @@ func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.
|
||||
container.NetworkSettings.Networks[nwName] = &network.EndpointSettings{
|
||||
EndpointSettings: endpointConfig,
|
||||
IPAMOperational: operIPAM,
|
||||
MACOperational: operMAC,
|
||||
}
|
||||
|
||||
delete(container.NetworkSettings.Networks, n.ID())
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
cerrdefs "github.com/containerd/containerd/errdefs"
|
||||
containerdimages "github.com/containerd/containerd/images"
|
||||
cplatforms "github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/log"
|
||||
"github.com/distribution/reference"
|
||||
imagetype "github.com/docker/docker/api/types/image"
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/image"
|
||||
imagespec "github.com/docker/docker/image/spec/specs-go/v1"
|
||||
"github.com/docker/docker/pkg/platforms"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
@@ -38,9 +37,9 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
|
||||
return nil, err
|
||||
}
|
||||
|
||||
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
|
||||
platform := matchAllWithPreference(platforms.Default())
|
||||
if options.Platform != nil {
|
||||
platform = cplatforms.OnlyStrict(*options.Platform)
|
||||
platform = platforms.OnlyStrict(*options.Platform)
|
||||
}
|
||||
|
||||
var presentImages []imagespec.DockerOCIImage
|
||||
@@ -158,9 +157,9 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
|
||||
}
|
||||
|
||||
func (i *ImageService) GetImageManifest(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*ocispec.Descriptor, error) {
|
||||
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
|
||||
platform := matchAllWithPreference(platforms.Default())
|
||||
if options.Platform != nil {
|
||||
platform = cplatforms.Only(*options.Platform)
|
||||
platform = platforms.Only(*options.Platform)
|
||||
}
|
||||
|
||||
cs := i.client.ContentStore()
|
||||
@@ -188,9 +187,9 @@ func (i *ImageService) GetImageManifest(ctx context.Context, refOrID string, opt
|
||||
|
||||
if options.Platform != nil {
|
||||
if plat == nil {
|
||||
return nil, errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform: wanted %s, actual: nil", refOrID, cplatforms.Format(*options.Platform)))
|
||||
return nil, errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform: wanted %s, actual: nil", refOrID, platforms.Format(*options.Platform)))
|
||||
} else if !platform.Match(*plat) {
|
||||
return nil, errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform: wanted %s, actual: %s", refOrID, cplatforms.Format(*options.Platform), cplatforms.Format(*plat)))
|
||||
return nil, errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform: wanted %s, actual: %s", refOrID, platforms.Format(*options.Platform), platforms.Format(*plat)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +218,7 @@ func (i *ImageService) GetImageManifest(ctx context.Context, refOrID string, opt
|
||||
}
|
||||
|
||||
// size returns the total size of the image's packed resources.
|
||||
func (i *ImageService) size(ctx context.Context, desc ocispec.Descriptor, platform cplatforms.MatchComparer) (int64, error) {
|
||||
func (i *ImageService) size(ctx context.Context, desc ocispec.Descriptor, platform platforms.MatchComparer) (int64, error) {
|
||||
var size int64
|
||||
|
||||
cs := i.client.ContentStore()
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
containerdimages "github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/images/archive"
|
||||
"github.com/containerd/containerd/leases"
|
||||
cplatforms "github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/log"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/docker/docker/daemon/images"
|
||||
"github.com/docker/docker/errdefs"
|
||||
dockerarchive "github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/platforms"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
@@ -49,7 +48,7 @@ func (i *ImageService) PerformWithBaseFS(ctx context.Context, c *container.Conta
|
||||
//
|
||||
// TODO(thaJeztah): produce JSON stream progress response and image events; see https://github.com/moby/moby/issues/43910
|
||||
func (i *ImageService) ExportImage(ctx context.Context, names []string, outStream io.Writer) error {
|
||||
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
|
||||
platform := matchAllWithPreference(platforms.Default())
|
||||
opts := []archive.ExportOpt{
|
||||
archive.WithSkipNonDistributableBlobs(),
|
||||
|
||||
@@ -236,7 +235,7 @@ func (i *ImageService) LoadImage(ctx context.Context, inTar io.ReadCloser, outSt
|
||||
|
||||
opts := []containerd.ImportOpt{
|
||||
// TODO(vvoland): Allow user to pass platform
|
||||
containerd.WithImportPlatform(cplatforms.All),
|
||||
containerd.WithImportPlatform(platforms.All),
|
||||
|
||||
containerd.WithSkipMissing(),
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
|
||||
"github.com/containerd/containerd/images"
|
||||
containerdimages "github.com/containerd/containerd/images"
|
||||
cplatforms "github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/log"
|
||||
"github.com/distribution/reference"
|
||||
imagetype "github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/platforms"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/identity"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@@ -28,7 +27,7 @@ func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imaget
|
||||
|
||||
cs := i.client.ContentStore()
|
||||
// TODO: pass platform in from the CLI
|
||||
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
|
||||
platform := matchAllWithPreference(platforms.Default())
|
||||
|
||||
var presentImages []ocispec.Image
|
||||
err = i.walkImageManifests(ctx, img, func(img *ImageManifest) error {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/images"
|
||||
containerdimages "github.com/containerd/containerd/images"
|
||||
cplatforms "github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/moby/buildkit/util/attestation"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@@ -65,7 +65,7 @@ func (i *ImageService) NewImageManifest(ctx context.Context, img containerdimage
|
||||
parent := img.Target
|
||||
img.Target = manifestDesc
|
||||
|
||||
c8dImg := containerd.NewImageWithPlatform(i.client, img, cplatforms.All)
|
||||
c8dImg := containerd.NewImageWithPlatform(i.client, img, platforms.All)
|
||||
return &ImageManifest{
|
||||
Image: c8dImg,
|
||||
RealTarget: parent,
|
||||
@@ -122,7 +122,7 @@ func (im *ImageManifest) Manifest(ctx context.Context) (ocispec.Manifest, error)
|
||||
|
||||
func (im *ImageManifest) CheckContentAvailable(ctx context.Context) (bool, error) {
|
||||
// The target is already a platform-specific manifest, so no need to match platform.
|
||||
pm := cplatforms.All
|
||||
pm := platforms.All
|
||||
|
||||
available, _, _, missing, err := containerdimages.Check(ctx, im.ContentStore(), im.Target(), pm)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,7 +28,7 @@ func (i *ImageService) PrepareSnapshot(ctx context.Context, id string, parentIma
|
||||
|
||||
cs := i.client.ContentStore()
|
||||
|
||||
matcher := platforms.Default()
|
||||
matcher := matchAllWithPreference(platforms.Default())
|
||||
if platform != nil {
|
||||
matcher = platforms.Only(*platform)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package platforms
|
||||
package containerd
|
||||
|
||||
import (
|
||||
cplatforms "github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type allPlatformsWithPreferenceMatcher struct {
|
||||
preferred cplatforms.MatchComparer
|
||||
preferred platforms.MatchComparer
|
||||
}
|
||||
|
||||
// AllPlatformsWithPreference will return a platform matcher that matches all
|
||||
// matchAllWithPreference will return a platform matcher that matches all
|
||||
// platforms but will order platforms matching the preferred matcher first.
|
||||
func AllPlatformsWithPreference(preferred cplatforms.MatchComparer) cplatforms.MatchComparer {
|
||||
func matchAllWithPreference(preferred platforms.MatchComparer) platforms.MatchComparer {
|
||||
return allPlatformsWithPreferenceMatcher{
|
||||
preferred: preferred,
|
||||
}
|
||||
@@ -180,6 +180,11 @@ func (daemon *Daemon) config() *configStore {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Config returns daemon's config.
|
||||
func (daemon *Daemon) Config() config.Config {
|
||||
return daemon.config().Config
|
||||
}
|
||||
|
||||
// HasExperimental returns whether the experimental features of the daemon are enabled or not
|
||||
func (daemon *Daemon) HasExperimental() bool {
|
||||
return daemon.config().Experimental
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
@@ -36,8 +37,10 @@ func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bo
|
||||
}
|
||||
|
||||
shortCID := stringid.TruncateID(ctr.ID)
|
||||
for _, ep := range ctr.NetworkSettings.Networks {
|
||||
ep.Aliases = sliceutil.Dedup(append(ep.Aliases, shortCID, ctr.Config.Hostname))
|
||||
for nwName, ep := range ctr.NetworkSettings.Networks {
|
||||
if containertypes.NetworkMode(nwName).IsUserDefined() {
|
||||
ep.Aliases = sliceutil.Dedup(append(ep.Aliases, shortCID, ctr.Config.Hostname))
|
||||
}
|
||||
}
|
||||
|
||||
return ctr, nil
|
||||
|
||||
@@ -33,6 +33,8 @@ type Settings struct {
|
||||
type EndpointSettings struct {
|
||||
*networktypes.EndpointSettings
|
||||
IPAMOperational bool
|
||||
// MACOperational is false if EndpointSettings.MacAddress is a user-configured value.
|
||||
MACOperational bool
|
||||
}
|
||||
|
||||
// AttachmentStore stores the load balancer IP address for a network id.
|
||||
|
||||
@@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@@ -127,9 +128,9 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
ep, err := nw.EndpointByID(epConfig.EndpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
ep := sb.GetEndpoint(epConfig.EndpointID)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("no endpoint attached to network %s found", nw.Name())
|
||||
}
|
||||
|
||||
oldDNSNames := make([]string, len(epConfig.DNSNames))
|
||||
|
||||
2173
docs/api/v1.18.md
2173
docs/api/v1.18.md
File diff suppressed because it is too large
Load Diff
2253
docs/api/v1.19.md
2253
docs/api/v1.19.md
File diff suppressed because it is too large
Load Diff
2408
docs/api/v1.20.md
2408
docs/api/v1.20.md
File diff suppressed because it is too large
Load Diff
2997
docs/api/v1.21.md
2997
docs/api/v1.21.md
File diff suppressed because it is too large
Load Diff
3336
docs/api/v1.22.md
3336
docs/api/v1.22.md
File diff suppressed because it is too large
Load Diff
3452
docs/api/v1.23.md
3452
docs/api/v1.23.md
File diff suppressed because it is too large
Load Diff
@@ -52,18 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.25 of the API, which was introduced with Docker 1.13. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,19 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.26 of the API, which was introduced with Docker 1.13.1. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,20 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.27 of the API, which was introduced with Docker 17.03.1. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,21 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.28 of the API, which was introduced with Docker 17.04. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,22 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.29 of the API, which was introduced with Docker 17.05. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,23 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.30 of the API, which was introduced with Docker 17.06. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,24 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.31 of the API. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes)
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,25 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.32 of the API. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes)
|
||||
17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes)
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -52,26 +52,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.33 of the API. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.09.x | [1.31](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes)
|
||||
17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes)
|
||||
17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes)
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -54,27 +54,6 @@ info:
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.34 of the API. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.10.x | [1.33](https://docs.docker.com/engine/api/v1.33/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-33-api-changes)
|
||||
17.09.x | [1.32](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes)
|
||||
17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes)
|
||||
17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes)
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
type imageDescriptor struct {
|
||||
refs []reference.NamedTagged
|
||||
layers []digest.Digest
|
||||
layers []layer.DiffID
|
||||
image *image.Image
|
||||
layerRef layer.Layer
|
||||
}
|
||||
@@ -210,7 +210,9 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||
foreign = make([]ocispec.Descriptor, 0, len(foreignSrcs))
|
||||
)
|
||||
|
||||
for _, desc := range foreignSrcs {
|
||||
// Layers in manifest must follow the actual layer order from config.
|
||||
for _, l := range imageDescr.layers {
|
||||
desc := foreignSrcs[l]
|
||||
foreign = append(foreign, ocispec.Descriptor{
|
||||
MediaType: desc.MediaType,
|
||||
Digest: desc.Digest,
|
||||
@@ -263,7 +265,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||
if _, ok := reposLegacy[familiarName]; !ok {
|
||||
reposLegacy[familiarName] = make(map[string]string)
|
||||
}
|
||||
reposLegacy[familiarName][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1].Encoded()
|
||||
reposLegacy[familiarName][ref.Tag()] = digest.Digest(imageDescr.layers[len(imageDescr.layers)-1]).Encoded()
|
||||
repoTags = append(repoTags, reference.FamiliarString(ref))
|
||||
|
||||
manifestDescriptors = append(manifestDescriptors, ocispec.Descriptor{
|
||||
@@ -281,7 +283,8 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||
for _, l := range imageDescr.layers {
|
||||
// IMPORTANT: We use path, not filepath here to ensure the layers
|
||||
// in the manifest use Unix-style forward-slashes.
|
||||
layers = append(layers, path.Join(ocispec.ImageBlobsDir, l.Algorithm().String(), l.Encoded()))
|
||||
lDgst := digest.Digest(l)
|
||||
layers = append(layers, path.Join(ocispec.ImageBlobsDir, lDgst.Algorithm().String(), lDgst.Encoded()))
|
||||
}
|
||||
|
||||
manifest = append(manifest, manifestItem{
|
||||
@@ -380,7 +383,7 @@ func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]distribution.Desc
|
||||
}
|
||||
|
||||
var parent digest.Digest
|
||||
var layers []digest.Digest
|
||||
var layers []layer.DiffID
|
||||
var foreignSrcs map[layer.DiffID]distribution.Descriptor
|
||||
for i, diffID := range img.RootFS.DiffIDs {
|
||||
v1ImgCreated := time.Unix(0, 0)
|
||||
@@ -410,7 +413,7 @@ func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]distribution.Desc
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layers = append(layers, digest.Digest(diffID))
|
||||
layers = append(layers, diffID)
|
||||
parent = v1ID
|
||||
if src.Digest != "" {
|
||||
if foreignSrcs == nil {
|
||||
|
||||
@@ -1287,7 +1287,7 @@ func (s *DockerAPISuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRoot
|
||||
// Attempt to extract to a symlink in the volume which points to a
|
||||
// directory outside the volume. This should cause an error because the
|
||||
// rootfs is read-only.
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.20"))
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
||||
assert.NilError(c, err)
|
||||
|
||||
err = apiClient.CopyToContainer(testutil.GetContext(c), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{})
|
||||
|
||||
@@ -159,6 +159,11 @@ func (s *DockerAPISuite) TestInspectAPIBridgeNetworkSettings120(c *testing.T) {
|
||||
assert.Assert(c, len(settings.IPAddress) != 0)
|
||||
}
|
||||
|
||||
// Inspect for API v1.21 and up; see
|
||||
//
|
||||
// - https://github.com/moby/moby/issues/17131
|
||||
// - https://github.com/moby/moby/issues/17139
|
||||
// - https://github.com/moby/moby/issues/17173
|
||||
func (s *DockerAPISuite) TestInspectAPIBridgeNetworkSettings121(c *testing.T) {
|
||||
// Windows doesn't have any bridge network settings
|
||||
testRequires(c, DaemonIsLinux)
|
||||
@@ -166,7 +171,7 @@ func (s *DockerAPISuite) TestInspectAPIBridgeNetworkSettings121(c *testing.T) {
|
||||
containerID := strings.TrimSpace(out)
|
||||
cli.WaitRun(c, containerID)
|
||||
|
||||
body := getInspectBody(c, "v1.21", containerID)
|
||||
body := getInspectBody(c, "", containerID)
|
||||
|
||||
var inspectJSON types.ContainerJSON
|
||||
err := json.Unmarshal(body, &inspectJSON)
|
||||
|
||||
@@ -1022,15 +1022,16 @@ func (s *DockerCLINetworkSuite) TestInspectAPIMultipleNetworks(c *testing.T) {
|
||||
|
||||
versionedIP := inspect120.NetworkSettings.IPAddress
|
||||
|
||||
body = getInspectBody(c, "v1.21", id)
|
||||
var inspect121 types.ContainerJSON
|
||||
err = json.Unmarshal(body, &inspect121)
|
||||
// Current API version (API v1.21 and up)
|
||||
body = getInspectBody(c, "", id)
|
||||
var inspectCurrent types.ContainerJSON
|
||||
err = json.Unmarshal(body, &inspectCurrent)
|
||||
assert.NilError(c, err)
|
||||
assert.Equal(c, len(inspect121.NetworkSettings.Networks), 3)
|
||||
assert.Equal(c, len(inspectCurrent.NetworkSettings.Networks), 3)
|
||||
|
||||
bridge := inspect121.NetworkSettings.Networks["bridge"]
|
||||
bridge := inspectCurrent.NetworkSettings.Networks["bridge"]
|
||||
assert.Equal(c, bridge.IPAddress, versionedIP)
|
||||
assert.Equal(c, bridge.IPAddress, inspect121.NetworkSettings.IPAddress)
|
||||
assert.Equal(c, bridge.IPAddress, inspectCurrent.NetworkSettings.IPAddress)
|
||||
}
|
||||
|
||||
func connectContainerToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) {
|
||||
|
||||
@@ -2,10 +2,12 @@ package container // import "github.com/docker/docker/integration/container"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/testutil/request"
|
||||
@@ -68,3 +70,33 @@ func TestInspectAnnotations(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(inspect.HostConfig.Annotations, annotations))
|
||||
}
|
||||
|
||||
// TestNetworkAliasesAreEmpty verifies that network-scoped aliases are not set
|
||||
// for non-custom networks (network-scoped aliases are only supported for
|
||||
// custom networks, except for the "Default Switch" network on Windows).
|
||||
func TestNetworkAliasesAreEmpty(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
apiClient := request.NewAPIClient(t)
|
||||
|
||||
netModes := []string{"host", "bridge", "none"}
|
||||
if runtime.GOOS == "windows" {
|
||||
netModes = []string{"nat", "none"}
|
||||
}
|
||||
|
||||
for _, nwMode := range netModes {
|
||||
t.Run(nwMode, func(t *testing.T) {
|
||||
ctr := container.Create(ctx, t, apiClient,
|
||||
container.WithName("ctr-"+nwMode),
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithNetworkMode(nwMode))
|
||||
defer apiClient.ContainerRemove(ctx, ctr, containertypes.RemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
|
||||
inspect := container.Inspect(ctx, t, apiClient, ctr)
|
||||
netAliases := inspect.NetworkSettings.Networks[nwMode].Aliases
|
||||
|
||||
assert.Check(t, is.Nil(netAliases))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,3 +192,25 @@ func TestRenameContainerWithLinkedContainer(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(db1ID, inspect.ID))
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/moby/moby/issues/47186
|
||||
func TestRenameContainerTwice(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
ctrName := "c0"
|
||||
container.Run(ctx, t, apiClient, container.WithName("c0"))
|
||||
defer func() {
|
||||
container.Remove(ctx, t, apiClient, ctrName, containertypes.RemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
}()
|
||||
|
||||
err := apiClient.ContainerRename(ctx, "c0", "c1")
|
||||
assert.NilError(t, err)
|
||||
ctrName = "c1"
|
||||
|
||||
err = apiClient.ContainerRename(ctx, "c1", "c2")
|
||||
assert.NilError(t, err)
|
||||
ctrName = "c2"
|
||||
}
|
||||
|
||||
@@ -289,3 +289,40 @@ func TestMacAddressIsAppliedToMainNetworkWithShortID(t *testing.T) {
|
||||
c := container.Inspect(ctx, t, apiClient, cid)
|
||||
assert.Equal(t, c.NetworkSettings.Networks["testnet"].MacAddress, "02:42:08:26:a9:55")
|
||||
}
|
||||
|
||||
func TestStaticIPOutsideSubpool(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.43"))
|
||||
assert.NilError(t, err)
|
||||
|
||||
const netname = "subnet-range"
|
||||
n := net.CreateNoError(ctx, t, apiClient, netname, net.WithIPAMRange("10.42.0.0/16", "10.42.128.0/24", "10.42.0.1"))
|
||||
defer net.RemoveNoError(ctx, t, apiClient, n)
|
||||
|
||||
cID := container.Run(ctx, t, apiClient,
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithCmd("sh", "-c", `ip -4 -oneline addr show eth0`),
|
||||
container.WithNetworkMode(netname),
|
||||
container.WithIPv4(netname, "10.42.1.3"),
|
||||
)
|
||||
|
||||
poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond))
|
||||
|
||||
out, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true})
|
||||
assert.NilError(t, err)
|
||||
defer out.Close()
|
||||
|
||||
var b bytes.Buffer
|
||||
_, err = io.Copy(&b, out)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, is.Contains(b.String(), "inet 10.42.1.3/16"))
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ func WithOption(key, value string) func(*types.NetworkCreate) {
|
||||
|
||||
// WithIPAM adds an IPAM with the specified Subnet and Gateway to the network
|
||||
func WithIPAM(subnet, gateway string) func(*types.NetworkCreate) {
|
||||
return WithIPAMRange(subnet, "", gateway)
|
||||
}
|
||||
|
||||
// WithIPAM adds an IPAM with the specified Subnet, IPRange and Gateway to the network
|
||||
func WithIPAMRange(subnet, iprange, gateway string) func(*types.NetworkCreate) {
|
||||
return func(n *types.NetworkCreate) {
|
||||
if n.IPAM == nil {
|
||||
n.IPAM = &network.IPAM{}
|
||||
@@ -80,6 +85,7 @@ func WithIPAM(subnet, gateway string) func(*types.NetworkCreate) {
|
||||
|
||||
n.IPAM.Config = append(n.IPAM.Config, network.IPAMConfig{
|
||||
Subnet: subnet,
|
||||
IPRange: iprange,
|
||||
Gateway: gateway,
|
||||
AuxAddress: map[string]string{},
|
||||
})
|
||||
|
||||
79
integration/networking/mac_addr_test.go
Normal file
79
integration/networking/mac_addr_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// TestMACAddrOnRestart is a regression test for https://github.com/moby/moby/issues/47146
|
||||
// - Start a container, let it use a generated MAC address.
|
||||
// - Stop that container.
|
||||
// - Start a second container, it'll also use a generated MAC address.
|
||||
// (It's likely to recycle the first container's MAC address.)
|
||||
// - Restart the first container.
|
||||
// (The bug was that it kept its original MAC address, now already in-use.)
|
||||
// - Check that the two containers have different MAC addresses.
|
||||
func TestMACAddrOnRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
const netName = "testmacaddrs"
|
||||
network.CreateNoError(ctx, t, c, netName,
|
||||
network.WithDriver("bridge"),
|
||||
network.WithOption("com.docker.network.bridge.name", netName))
|
||||
defer network.RemoveNoError(ctx, t, c, netName)
|
||||
|
||||
const ctr1Name = "ctr1"
|
||||
id1 := container.Run(ctx, t, c,
|
||||
container.WithName(ctr1Name),
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithCmd("top"),
|
||||
container.WithNetworkMode(netName))
|
||||
defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
err := c.ContainerStop(ctx, ctr1Name, containertypes.StopOptions{})
|
||||
assert.Assert(t, is.Nil(err))
|
||||
|
||||
// Start a second container, giving the daemon a chance to recycle the first container's
|
||||
// IP and MAC addresses.
|
||||
const ctr2Name = "ctr2"
|
||||
id2 := container.Run(ctx, t, c,
|
||||
container.WithName(ctr2Name),
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithCmd("top"),
|
||||
container.WithNetworkMode(netName))
|
||||
defer c.ContainerRemove(ctx, id2, containertypes.RemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
|
||||
// Restart the first container.
|
||||
err = c.ContainerStart(ctx, ctr1Name, containertypes.StartOptions{})
|
||||
assert.Assert(t, is.Nil(err))
|
||||
|
||||
// Check that the containers ended up with different MAC addresses.
|
||||
|
||||
ctr1Inspect := container.Inspect(ctx, t, c, ctr1Name)
|
||||
ctr1MAC := ctr1Inspect.NetworkSettings.Networks[netName].MacAddress
|
||||
|
||||
ctr2Inspect := container.Inspect(ctx, t, c, ctr2Name)
|
||||
ctr2MAC := ctr2Inspect.NetworkSettings.Networks[netName].MacAddress
|
||||
|
||||
assert.Check(t, ctr1MAC != ctr2MAC,
|
||||
"expected containers to have different MAC addresses; got %q for both", ctr1MAC)
|
||||
}
|
||||
@@ -623,7 +623,7 @@ func (ep *Endpoint) addServiceInfoToCluster(sb *Sandbox) error {
|
||||
// In case the deleteServiceInfoToCluster arrives first, this one is happening after the endpoint is
|
||||
// removed from the list, in this situation the delete will bail out not finding any data to cleanup
|
||||
// and the add will bail out not finding the endpoint on the sandbox.
|
||||
if err := sb.getEndpoint(ep.ID()); err == nil {
|
||||
if err := sb.GetEndpoint(ep.ID()); err == nil {
|
||||
log.G(context.TODO()).Warnf("addServiceInfoToCluster suppressing service resolution ep is not anymore in the sandbox %s", ep.ID())
|
||||
return nil
|
||||
}
|
||||
@@ -692,7 +692,7 @@ func (ep *Endpoint) deleteServiceInfoFromCluster(sb *Sandbox, fullRemove bool, m
|
||||
// get caught in disableServceInNetworkDB, but we check here to make the
|
||||
// nature of the condition more clear.
|
||||
// See comment in addServiceInfoToCluster()
|
||||
if err := sb.getEndpoint(ep.ID()); err == nil {
|
||||
if err := sb.GetEndpoint(ep.ID()); err == nil {
|
||||
log.G(context.TODO()).Warnf("deleteServiceInfoFromCluster suppressing service resolution ep is not anymore in the sandbox %s", ep.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ func (ep *Endpoint) Info() EndpointInfo {
|
||||
return ep
|
||||
}
|
||||
|
||||
return sb.getEndpoint(ep.ID())
|
||||
return sb.GetEndpoint(ep.ID())
|
||||
}
|
||||
|
||||
// Iface returns information about the interface which was assigned to
|
||||
|
||||
@@ -83,7 +83,7 @@ type IpamConf struct {
|
||||
// PreferredPool is the master address pool for containers and network interfaces.
|
||||
PreferredPool string
|
||||
// SubPool is a subset of the master pool. If specified,
|
||||
// this becomes the container pool.
|
||||
// this becomes the container pool for automatic address allocations.
|
||||
SubPool string
|
||||
// Gateway is the preferred Network Gateway address (optional).
|
||||
Gateway string
|
||||
@@ -100,7 +100,7 @@ func (c *IpamConf) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Contains checks whether the ipamSubnet contains [addr].
|
||||
// Contains checks whether the ipam master address pool contains [addr].
|
||||
func (c *IpamConf) Contains(addr net.IP) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
@@ -110,9 +110,6 @@ func (c *IpamConf) Contains(addr net.IP) bool {
|
||||
}
|
||||
|
||||
_, allowedRange, _ := net.ParseCIDR(c.PreferredPool)
|
||||
if c.SubPool != "" {
|
||||
_, allowedRange, _ = net.ParseCIDR(c.SubPool)
|
||||
}
|
||||
|
||||
return allowedRange.Contains(addr)
|
||||
}
|
||||
@@ -1279,8 +1276,8 @@ func (n *Network) EndpointByName(name string) (*Endpoint, error) {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// EndpointByID returns the Endpoint which has the passed id. If not found,
|
||||
// the error ErrNoSuchEndpoint is returned.
|
||||
// EndpointByID should *never* be called as it's going to create a 2nd instance of an Endpoint. The first one lives in
|
||||
// the Sandbox the endpoint is attached to. Instead, the endpoint should be retrieved by calling [Sandbox.Endpoints()].
|
||||
func (n *Network) EndpointByID(id string) (*Endpoint, error) {
|
||||
if id == "" {
|
||||
return nil, ErrInvalidID(id)
|
||||
|
||||
@@ -334,7 +334,7 @@ func (sb *Sandbox) removeEndpointRaw(ep *Endpoint) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *Sandbox) getEndpoint(id string) *Endpoint {
|
||||
func (sb *Sandbox) GetEndpoint(id string) *Endpoint {
|
||||
sb.mu.Lock()
|
||||
defer sb.mu.Unlock()
|
||||
|
||||
@@ -587,7 +587,7 @@ func (sb *Sandbox) DisableService() (err error) {
|
||||
}
|
||||
|
||||
func (sb *Sandbox) clearNetworkResources(origEp *Endpoint) error {
|
||||
ep := sb.getEndpoint(origEp.id)
|
||||
ep := sb.GetEndpoint(origEp.id)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s",
|
||||
origEp.id)
|
||||
|
||||
@@ -57,7 +57,7 @@ func (n *Network) findLBEndpointSandbox() (*Endpoint, *Sandbox, error) {
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Unable to get sandbox for %s(%s) in for %s", ep.Name(), ep.ID(), n.ID())
|
||||
}
|
||||
sep := sb.getEndpoint(ep.ID())
|
||||
sep := sb.GetEndpoint(ep.ID())
|
||||
if sep == nil {
|
||||
return nil, nil, fmt.Errorf("Load balancing endpoint %s(%s) removed from %s", ep.Name(), ep.ID(), n.ID())
|
||||
}
|
||||
|
||||
18
pkg/system/xattrs.go
Normal file
18
pkg/system/xattrs.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
type XattrError struct {
|
||||
Op string
|
||||
Attr string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *XattrError) Error() string { return e.Op + " " + e.Attr + " " + e.Path + ": " + e.Err.Error() }
|
||||
|
||||
func (e *XattrError) Unwrap() error { return e.Err }
|
||||
|
||||
// Timeout reports whether this error represents a timeout.
|
||||
func (e *XattrError) Timeout() bool {
|
||||
t, ok := e.Err.(interface{ Timeout() bool })
|
||||
return ok && t.Timeout()
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@@ -10,8 +8,8 @@ import (
|
||||
// and associated with the given path in the file system.
|
||||
// It will returns a nil slice and nil error if the xattr is not set.
|
||||
func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
pathErr := func(err error) ([]byte, error) {
|
||||
return nil, &fs.PathError{Op: "lgetxattr", Path: path, Err: err}
|
||||
sysErr := func(err error) ([]byte, error) {
|
||||
return nil, &XattrError{Op: "lgetxattr", Attr: attr, Path: path, Err: err}
|
||||
}
|
||||
|
||||
// Start with a 128 length byte array
|
||||
@@ -22,7 +20,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, errno = unix.Lgetxattr(path, attr, []byte{})
|
||||
if errno != nil {
|
||||
return pathErr(errno)
|
||||
return sysErr(errno)
|
||||
}
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = unix.Lgetxattr(path, attr, dest)
|
||||
@@ -32,7 +30,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
case errno == unix.ENODATA:
|
||||
return nil, nil
|
||||
case errno != nil:
|
||||
return pathErr(errno)
|
||||
return sysErr(errno)
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
@@ -43,7 +41,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
func Lsetxattr(path string, attr string, data []byte, flags int) error {
|
||||
err := unix.Lsetxattr(path, attr, data, flags)
|
||||
if err != nil {
|
||||
return &fs.PathError{Op: "lsetxattr", Path: path, Err: err}
|
||||
return &XattrError{Op: "lsetxattr", Attr: attr, Path: path, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package local // import "github.com/docker/docker/volume/local"
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -246,3 +247,84 @@ func TestVolCreateValidation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolMountOpts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts optsConfig
|
||||
expectedErr string
|
||||
expectedDevice, expectedOpts string
|
||||
}{
|
||||
{
|
||||
name: "cifs url with space",
|
||||
opts: optsConfig{
|
||||
MountType: "cifs",
|
||||
MountDevice: "//1.2.3.4/Program Files",
|
||||
},
|
||||
expectedDevice: "//1.2.3.4/Program Files",
|
||||
expectedOpts: "",
|
||||
},
|
||||
{
|
||||
name: "cifs resolve addr",
|
||||
opts: optsConfig{
|
||||
MountType: "cifs",
|
||||
MountDevice: "//example.com/Program Files",
|
||||
MountOpts: "addr=example.com",
|
||||
},
|
||||
expectedDevice: "//example.com/Program Files",
|
||||
expectedOpts: "addr=1.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "cifs resolve device",
|
||||
opts: optsConfig{
|
||||
MountType: "cifs",
|
||||
MountDevice: "//example.com/Program Files",
|
||||
},
|
||||
expectedDevice: "//1.2.3.4/Program Files",
|
||||
},
|
||||
{
|
||||
name: "nfs dont resolve device",
|
||||
opts: optsConfig{
|
||||
MountType: "nfs",
|
||||
MountDevice: "//example.com/Program Files",
|
||||
},
|
||||
expectedDevice: "//example.com/Program Files",
|
||||
},
|
||||
{
|
||||
name: "nfs resolve addr",
|
||||
opts: optsConfig{
|
||||
MountType: "nfs",
|
||||
MountDevice: "//example.com/Program Files",
|
||||
MountOpts: "addr=example.com",
|
||||
},
|
||||
expectedDevice: "//example.com/Program Files",
|
||||
expectedOpts: "addr=1.2.3.4",
|
||||
},
|
||||
}
|
||||
|
||||
ip1234 := net.ParseIP("1.2.3.4")
|
||||
resolveIP := func(network, addr string) (*net.IPAddr, error) {
|
||||
switch addr {
|
||||
case "example.com":
|
||||
return &net.IPAddr{IP: ip1234}, nil
|
||||
}
|
||||
|
||||
return nil, &net.DNSError{Err: "no such host", Name: addr, IsNotFound: true}
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
dev, opts, err := getMountOptions(&tc.opts, resolveIP)
|
||||
|
||||
if tc.expectedErr != "" {
|
||||
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
|
||||
} else {
|
||||
assert.Check(t, err)
|
||||
}
|
||||
|
||||
assert.Check(t, is.Equal(dev, tc.expectedDevice))
|
||||
assert.Check(t, is.Equal(opts, tc.expectedOpts))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,37 +118,56 @@ func (v *localVolume) needsMount() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *localVolume) mount() error {
|
||||
if v.opts.MountDevice == "" {
|
||||
return fmt.Errorf("missing device in volume options")
|
||||
func getMountOptions(opts *optsConfig, resolveIP func(string, string) (*net.IPAddr, error)) (mountDevice string, mountOpts string, _ error) {
|
||||
if opts.MountDevice == "" {
|
||||
return "", "", fmt.Errorf("missing device in volume options")
|
||||
}
|
||||
|
||||
mountOpts := v.opts.MountOpts
|
||||
mountDevice := v.opts.MountDevice
|
||||
mountOpts = opts.MountOpts
|
||||
mountDevice = opts.MountDevice
|
||||
|
||||
switch v.opts.MountType {
|
||||
case "nfs":
|
||||
if addrValue := getAddress(v.opts.MountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
|
||||
ipAddr, err := net.ResolveIPAddr("ip", addrValue)
|
||||
switch opts.MountType {
|
||||
case "nfs", "cifs":
|
||||
if addrValue := getAddress(opts.MountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
|
||||
ipAddr, err := resolveIP("ip", addrValue)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error resolving passed in network volume address")
|
||||
return "", "", errors.Wrap(err, "error resolving passed in network volume address")
|
||||
}
|
||||
mountOpts = strings.Replace(mountOpts, "addr="+addrValue, "addr="+ipAddr.String(), 1)
|
||||
break
|
||||
}
|
||||
case "cifs":
|
||||
deviceURL, err := url.Parse(v.opts.MountDevice)
|
||||
|
||||
if opts.MountType != "cifs" {
|
||||
break
|
||||
}
|
||||
|
||||
deviceURL, err := url.Parse(mountDevice)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing mount device url")
|
||||
return "", "", errors.Wrap(err, "error parsing mount device url")
|
||||
}
|
||||
if deviceURL.Host != "" && net.ParseIP(deviceURL.Host) == nil {
|
||||
ipAddr, err := net.ResolveIPAddr("ip", deviceURL.Host)
|
||||
ipAddr, err := resolveIP("ip", deviceURL.Host)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error resolving passed in network volume address")
|
||||
return "", "", errors.Wrap(err, "error resolving passed in network volume address")
|
||||
}
|
||||
deviceURL.Host = ipAddr.String()
|
||||
mountDevice = deviceURL.String()
|
||||
dev, err := url.QueryUnescape(deviceURL.String())
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to unescape device URL: %q", deviceURL)
|
||||
}
|
||||
mountDevice = dev
|
||||
}
|
||||
}
|
||||
|
||||
return mountDevice, mountOpts, nil
|
||||
}
|
||||
|
||||
func (v *localVolume) mount() error {
|
||||
mountDevice, mountOpts, err := getMountOptions(v.opts, net.ResolveIPAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mount.Mount(mountDevice, v.path, v.opts.MountType, mountOpts); err != nil {
|
||||
if password := getPassword(v.opts.MountOpts); password != "" {
|
||||
err = errors.New(strings.Replace(err.Error(), "password="+password, "password=********", 1))
|
||||
|
||||
Reference in New Issue
Block a user