mirror of https://github.com/docker/cli.git
Merge pull request #4984 from vvoland/c8d-multiplatform-push
cli/push: Add `platform` switch
This commit is contained in:
commit
52eddcf4e4
|
@ -2,18 +2,25 @@ package image
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/api/types/auxprogress"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/moby/term"
|
||||
"github.com/morikuni/aec"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -23,6 +30,7 @@ type pushOptions struct {
|
|||
remote string
|
||||
untrusted bool
|
||||
quiet bool
|
||||
platform string
|
||||
}
|
||||
|
||||
// NewPushCommand creates a new `docker push` command
|
||||
|
@ -48,12 +56,33 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
|
|||
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Push all tags of an image to the repository")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
|
||||
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
|
||||
flags.StringVar(&opts.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"),
|
||||
`Push a platform-specific manifest as a single-platform image to the registry.
|
||||
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64)`)
|
||||
flags.SetAnnotation("platform", "version", []string{"1.46"})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunPush performs a push against the engine based on the specified options
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error {
|
||||
var platform *ocispec.Platform
|
||||
if opts.platform != "" {
|
||||
p, err := platforms.Parse(opts.platform)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "Invalid platform %s", opts.platform)
|
||||
return err
|
||||
}
|
||||
platform = &p
|
||||
|
||||
printNote(dockerCli, `Selecting a single platform will only push one matching image manifest from a multi-platform image index.
|
||||
This means that any other components attached to the multi-platform image index (like Buildkit attestations) won't be pushed.
|
||||
If you want to only push a single platform image while preserving the attestations, please use 'docker convert\n'
|
||||
`)
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(opts.remote)
|
||||
switch {
|
||||
case err != nil:
|
||||
|
@ -84,6 +113,7 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
|
|||
All: opts.all,
|
||||
RegistryAuth: encodedAuth,
|
||||
PrivilegeFunc: requestPrivilege,
|
||||
Platform: platform,
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
|
||||
|
@ -91,6 +121,13 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
|
|||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for _, note := range notes {
|
||||
fmt.Fprintln(dockerCli.Err(), "")
|
||||
printNote(dockerCli, note)
|
||||
}
|
||||
}()
|
||||
|
||||
defer responseBody.Close()
|
||||
if !opts.untrusted {
|
||||
// TODO PushTrustedReference currently doesn't respect `--quiet`
|
||||
|
@ -98,11 +135,51 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
|
|||
}
|
||||
|
||||
if opts.quiet {
|
||||
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(io.Discard), nil)
|
||||
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(io.Discard), handleAux(dockerCli))
|
||||
if err == nil {
|
||||
fmt.Fprintln(dockerCli.Out(), ref.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil)
|
||||
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), handleAux(dockerCli))
|
||||
}
|
||||
|
||||
var notes []string
|
||||
|
||||
func handleAux(dockerCli command.Cli) func(jm jsonmessage.JSONMessage) {
|
||||
return func(jm jsonmessage.JSONMessage) {
|
||||
b := []byte(*jm.Aux)
|
||||
|
||||
var stripped auxprogress.ManifestPushedInsteadOfIndex
|
||||
err := json.Unmarshal(b, &stripped)
|
||||
if err == nil && stripped.ManifestPushedInsteadOfIndex {
|
||||
note := fmt.Sprintf("Not all multiplatform-content is present and only the available single-platform image was pushed\n%s -> %s",
|
||||
aec.RedF.Apply(stripped.OriginalIndex.Digest.String()),
|
||||
aec.GreenF.Apply(stripped.SelectedManifest.Digest.String()),
|
||||
)
|
||||
notes = append(notes, note)
|
||||
}
|
||||
|
||||
var missing auxprogress.ContentMissing
|
||||
err = json.Unmarshal(b, &missing)
|
||||
if err == nil && missing.ContentMissing {
|
||||
note := `You're trying to push a manifest list/index which
|
||||
references multiple platform specific manifests, but not all of them are available locally
|
||||
or available to the remote repository.
|
||||
|
||||
Make sure you have all the referenced content and try again.
|
||||
|
||||
You can also push only a single platform specific manifest directly by specifying the platform you want to push with the --platform flag.`
|
||||
notes = append(notes, note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printNote(dockerCli command.Cli, format string, args ...any) {
|
||||
if _, isTTY := term.GetFdInfo(dockerCli.Err()); isTTY {
|
||||
_, _ = fmt.Fprint(dockerCli.Err(), aec.WhiteF.Apply(aec.CyanB.Apply("[ NOTE ]"))+" ")
|
||||
} else {
|
||||
_, _ = fmt.Fprint(dockerCli.Err(), "[ NOTE ] ")
|
||||
}
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), aec.Bold.Apply(format)+"\n", args...)
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@ Upload an image to a registry
|
|||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------------|:-------|:--------|:--------------------------------------------|
|
||||
|:---------------------------------------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`-a`](#all-tags), [`--all-tags`](#all-tags) | | | Push all tags of an image to the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
|
||||
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
|
||||
| `-q`, `--quiet` | | | Suppress verbose output |
|
||||
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@ Upload an image to a registry
|
|||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:-------|:--------|:--------------------------------------------|
|
||||
|:--------------------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all-tags` | | | Push all tags of an image to the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
|
||||
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
|
||||
| `-q`, `--quiet` | | | Suppress verbose output |
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ require (
|
|||
github.com/creack/pty v1.1.21
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/docker v26.1.1-0.20240610145149-a736d0701c41+incompatible // master (v27.0.0-dev)
|
||||
github.com/docker/docker v26.1.1-0.20240610201418-9d9488468fe2+incompatible // master (v27.0.0-dev)
|
||||
github.com/docker/docker-credential-helpers v0.8.2
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
|
|
|
@ -59,8 +59,8 @@ github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5
|
|||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v26.1.1-0.20240610145149-a736d0701c41+incompatible h1:Kraon288jb3POkrmM5w6Xo979z2rrCtFzHycAjafRes=
|
||||
github.com/docker/docker v26.1.1-0.20240610145149-a736d0701c41+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v26.1.1-0.20240610201418-9d9488468fe2+incompatible h1:k63BdhjySkwvmdeofOsBElcuVrWaDBrI7FQgnyoVnnM=
|
||||
github.com/docker/docker v26.1.1-0.20240610201418-9d9488468fe2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
|
|
|
@ -1368,7 +1368,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always empty and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always empty. It must not be used, and will be removed in API v1.47.
|
||||
type: "string"
|
||||
example: ""
|
||||
Domainname:
|
||||
|
@ -1377,7 +1378,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always empty and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always empty. It must not be used, and will be removed in API v1.47.
|
||||
type: "string"
|
||||
example: ""
|
||||
User:
|
||||
|
@ -1390,7 +1392,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1400,7 +1403,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1410,7 +1414,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1436,7 +1441,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1446,7 +1452,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1456,7 +1463,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always false and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always false. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1492,7 +1500,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always empty and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always empty. It must not be used, and will be removed in API v1.47.
|
||||
type: "string"
|
||||
default: ""
|
||||
example: ""
|
||||
|
@ -1530,7 +1539,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always omitted and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always omitted. It must not be used, and will be removed in API v1.47.
|
||||
type: "boolean"
|
||||
default: false
|
||||
example: false
|
||||
|
@ -1541,7 +1551,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: this field is deprecated in API v1.44 and up. It is always omitted.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always omitted. It must not be used, and will be removed in API v1.47.
|
||||
type: "string"
|
||||
default: ""
|
||||
example: ""
|
||||
|
@ -1574,7 +1585,8 @@ definitions:
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: this field is always omitted and must not be used.
|
||||
> **Deprecated**: this field is not part of the image specification and is
|
||||
> always omitted. It must not be used, and will be removed in API v1.47.
|
||||
type: "integer"
|
||||
default: 10
|
||||
x-nullable: true
|
||||
|
@ -2115,6 +2127,7 @@ definitions:
|
|||
format: "dateTime"
|
||||
example: "2022-02-28T14:40:02.623929178Z"
|
||||
x-nullable: true
|
||||
|
||||
ImageSummary:
|
||||
type: "object"
|
||||
x-go-name: "Summary"
|
||||
|
@ -9023,6 +9036,11 @@ paths:
|
|||
details.
|
||||
type: "string"
|
||||
required: true
|
||||
- name: "platform"
|
||||
in: "query"
|
||||
description: "Select a platform-specific manifest to be pushed. OCI platform (JSON encoded)"
|
||||
type: "string"
|
||||
x-nullable: true
|
||||
tags: ["Image"]
|
||||
/images/{name}/tag:
|
||||
post:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package auxprogress
|
||||
|
||||
import (
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ManifestPushedInsteadOfIndex is a note that is sent when a manifest is pushed
|
||||
// instead of an index. It is sent when the pushed image is an multi-platform
|
||||
// index, but the whole index couldn't be pushed.
|
||||
type ManifestPushedInsteadOfIndex struct {
|
||||
ManifestPushedInsteadOfIndex bool `json:"manifestPushedInsteadOfIndex"` // Always true
|
||||
|
||||
// OriginalIndex is the descriptor of the original image index.
|
||||
OriginalIndex ocispec.Descriptor `json:"originalIndex"`
|
||||
|
||||
// SelectedManifest is the descriptor of the manifest that was pushed instead.
|
||||
SelectedManifest ocispec.Descriptor `json:"selectedManifest"`
|
||||
}
|
||||
|
||||
// ContentMissing is a note that is sent when push fails because the content is missing.
|
||||
type ContentMissing struct {
|
||||
ContentMissing bool `json:"contentMissing"` // Always true
|
||||
|
||||
// Desc is the descriptor of the root object that was attempted to be pushed.
|
||||
Desc ocispec.Descriptor `json:"desc"`
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ImportSource holds source information for ImageImport
|
||||
|
@ -43,7 +44,23 @@ type PullOptions struct {
|
|||
}
|
||||
|
||||
// PushOptions holds information to push images.
|
||||
type PushOptions PullOptions
|
||||
type PushOptions struct {
|
||||
All bool
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||
|
||||
// PrivilegeFunc is a function that clients can supply to retry operations
|
||||
// after getting an authorization error. This function returns the registry
|
||||
// authentication header value in base64 encoded format, or an error if the
|
||||
// privilege request fails.
|
||||
//
|
||||
// Also see [github.com/docker/docker/api/types.RequestPrivilegeFunc].
|
||||
PrivilegeFunc func(context.Context) (string, error)
|
||||
|
||||
// Platform is an optional field that selects a specific platform to push
|
||||
// when the image is a multi-platform image.
|
||||
// Using this will only push a single platform-specific manifest.
|
||||
Platform *ocispec.Platform `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ListOptions holds parameters to list images with.
|
||||
type ListOptions struct {
|
||||
|
|
|
@ -2,7 +2,9 @@ package client // import "github.com/docker/docker/client"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -36,6 +38,20 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options image.Pu
|
|||
}
|
||||
}
|
||||
|
||||
if options.Platform != nil {
|
||||
if err := cli.NewVersionError(ctx, "1.46", "platform"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := *options.Platform
|
||||
pJson, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid platform: %v", err)
|
||||
}
|
||||
|
||||
query.Set("platform", string(pJson))
|
||||
}
|
||||
|
||||
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
|
||||
if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
|
||||
newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/pkg/userns"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
@ -35,13 +36,18 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
|||
}
|
||||
|
||||
if fi.Mode()&os.ModeDir != 0 {
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
}
|
||||
|
||||
// convert opaque dirs to AUFS format by writing an empty file with the prefix
|
||||
opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque")
|
||||
opaque, err := system.Lgetxattr(path, opaqueXattrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(opaque) == 1 && opaque[0] == 'y' {
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+"trusted.overlay.opaque")
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
|
||||
|
||||
// create a header for the whiteout file
|
||||
// it should inherit some properties from the parent, but be a regular file
|
||||
|
@ -69,9 +75,14 @@ func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (boo
|
|||
|
||||
// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
|
||||
if base == WhiteoutOpaqueDir {
|
||||
err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
}
|
||||
|
||||
err := unix.Setxattr(dir, opaqueXattrName, []byte{'y'}, 0)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir)
|
||||
return false, errors.Wrapf(err, "setxattr(%q, %s=y)", dir, opaqueXattrName)
|
||||
}
|
||||
// don't write the file itself
|
||||
return false, err
|
||||
|
|
|
@ -56,10 +56,11 @@ github.com/docker/distribution/registry/client/transport
|
|||
github.com/docker/distribution/registry/storage/cache
|
||||
github.com/docker/distribution/registry/storage/cache/memory
|
||||
github.com/docker/distribution/uuid
|
||||
# github.com/docker/docker v26.1.1-0.20240610145149-a736d0701c41+incompatible
|
||||
# github.com/docker/docker v26.1.1-0.20240610201418-9d9488468fe2+incompatible
|
||||
## explicit
|
||||
github.com/docker/docker/api
|
||||
github.com/docker/docker/api/types
|
||||
github.com/docker/docker/api/types/auxprogress
|
||||
github.com/docker/docker/api/types/blkiodev
|
||||
github.com/docker/docker/api/types/checkpoint
|
||||
github.com/docker/docker/api/types/container
|
||||
|
|
Loading…
Reference in New Issue