Compare commits

..

7 Commits

Author SHA1 Message Date
a5ee5b1dfc Merge pull request #4018 from thaJeztah/23.0_backport_fix_ci_events
Some checks failed
build / build (cross, ) (push) Has been cancelled
build / build (cross, glibc) (push) Has been cancelled
build / build (dynbinary-cross, ) (push) Has been cancelled
build / build (dynbinary-cross, glibc) (push) Has been cancelled
build / plugins (push) Has been cancelled
e2e / e2e (19.03-dind, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, non-experimental) (push) Has been cancelled
e2e / e2e (bullseye, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (bullseye, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (bullseye, stable-dind, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-11) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[23.0 backport] ci: fix branch filter pattern
2023-02-09 20:15:59 +01:00
27b19a6acf ci: fix branch filter pattern
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 0f39598687)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-02-09 19:55:03 +01:00
ab4ef4aed4 Merge pull request #4004 from thaJeztah/23.0_backports
[23.0 backports] assorted backports
2023-02-06 11:38:55 -08:00
14aac2c232 vendor: github.com/docker/docker v23.0.0
- client: improve error messaging on crash

full diff: https://github.com/docker/docker/compare/v23.0.0-rc.3...v23.0.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit bbebebaedf)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-02-06 14:47:36 +01:00
0cd15abfde vendor: github.com/containerd/containerd v1.6.16
no changes in vendored code

full diff: https://github.com/containerd/containerd/compare/v1.6.15...v1.6.16

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 5195db1ff5)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-02-06 14:47:36 +01:00
168f1b55e2 cli/command/container: exit 126 on EISDIR error
The error returned from "os/exec".Command when attempting to execute a
directory has been changed from syscall.EACCESS to syscall.EISDIR on
Go 1.20. 2b8f214094
Consequently, any runc runtime built against Go 1.20 will return an
error containing 'is a directory' and not 'permission denied'. Update
the string matching so the CLI exits with status code 126 on 'is a
directory' errors (EISDIR) in addition to 'permission denied' (EACCESS).

Signed-off-by: Cory Snider <csnider@mirantis.com>
(cherry picked from commit 9b5ceb52b0)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-02-06 14:47:36 +01:00
53ed25d9b6 Fix bad ThrottleDevice path
Fixes moby/moby#44904.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
(cherry picked from commit 56051b84b0)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-02-06 14:47:33 +01:00
811 changed files with 10978 additions and 31508 deletions

View File

@ -15,64 +15,43 @@ on:
pull_request:
jobs:
prepare:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.platforms.outputs.matrix }}
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Create matrix
id: platforms
run: |
echo "matrix=$(docker buildx bake cross --print | jq -cr '.target."cross".platforms')" >>${GITHUB_OUTPUT}
-
name: Show matrix
run: |
echo ${{ steps.platforms.outputs.matrix }}
build:
runs-on: ubuntu-20.04
needs:
- prepare
strategy:
fail-fast: false
matrix:
target:
- binary
- dynbinary
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
- cross
- dynbinary-cross
use_glibc:
- ""
- glibc
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/bake-action@v3
name: Run ${{ matrix.target }}
uses: docker/bake-action@v2
with:
targets: ${{ matrix.target }}
set: |
*.platform=${{ matrix.platform }}
env:
USE_GLIBC: ${{ matrix.use_glibc }}
-
name: Create tarball
name: Flatten artifacts
working-directory: ./build
run: |
mkdir /tmp/out
platform=${{ matrix.platform }}
platformPair=${platform//\//-}
tar -cvzf "/tmp/out/docker-${platformPair}.tar.gz" .
for dir in */; do
base=$(basename "$dir")
echo "Creating ${base}.tar.gz ..."
tar -cvzf "${base}.tar.gz" "$dir"
rm -rf "$dir"
done
if [ -z "${{ matrix.use_glibc }}" ]; then
echo "ARTIFACT_NAME=${{ matrix.target }}" >> $GITHUB_ENV
else
@ -83,46 +62,20 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
path: ./build/*
if-no-files-found: error
prepare-plugins:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.platforms.outputs.matrix }}
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Create matrix
id: platforms
run: |
echo "matrix=$(docker buildx bake plugins-cross --print | jq -cr '.target."plugins-cross".platforms')" >>${GITHUB_OUTPUT}
-
name: Show matrix
run: |
echo ${{ steps.platforms.outputs.matrix }}
plugins:
runs-on: ubuntu-20.04
needs:
- prepare-plugins
strategy:
fail-fast: false
matrix:
platform: ${{ fromJson(needs.prepare-plugins.outputs.matrix) }}
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/bake-action@v3
name: Build plugins
uses: docker/bake-action@v2
with:
targets: plugins-cross
set: |
*.platform=${{ matrix.platform }}

View File

@ -19,7 +19,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 2
-

View File

@ -36,7 +36,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: Update daemon.json
run: |

View File

@ -20,13 +20,13 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Test
uses: docker/bake-action@v3
uses: docker/bake-action@v2
with:
targets: test-coverage
-
@ -56,14 +56,14 @@ jobs:
git config --system core.eol lf
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: ${{ env.GOPATH }}/src/github.com/docker/cli
-
name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v3
with:
go-version: 1.20.13
go-version: 1.19.4
-
name: Test
run: |

View File

@ -28,34 +28,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Run
uses: docker/bake-action@v3
uses: docker/bake-action@v2
with:
targets: ${{ matrix.target }}
# check that the generated Markdown and the checked-in files match
validate-md:
runs-on: ubuntu-20.04
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Generate
shell: 'script --return --quiet --command "bash {0}"'
run: |
make -f docker.Makefile mddocs
-
name: Validate
run: |
if [[ $(git diff --stat) != '' ]]; then
echo 'fail: generated files do not match checked-in files'
git --no-pager diff
exit 1
fi
validate-make:
runs-on: ubuntu-20.04
strategy:
@ -67,7 +48,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Run
shell: 'script --return --quiet --command "bash {0}"'

View File

@ -32,11 +32,12 @@ run:
linters-settings:
depguard:
rules:
main:
deny:
- pkg: io/ioutil
desc: The io/ioutil package has been deprecated, see https://go.dev/doc/go1.16#ioutil
list-type: blacklist
include-go-root: true
packages:
# The io/ioutil package has been deprecated.
# https://go.dev/doc/go1.16#ioutil
- io/ioutil
gocyclo:
min-complexity: 16
govet:
@ -116,10 +117,7 @@ issues:
- text: "package-comments: should have a package comment"
linters:
- revive
# FIXME temporarily suppress these (see https://github.com/gotestyourself/gotest.tools/issues/272)
- text: "SA1019: (assert|cmp|is)\\.ErrorType is deprecated"
linters:
- staticcheck
# Exclude some linters from running on tests files.
- path: _test\.go
linters:

View File

@ -192,7 +192,7 @@ For more details, see the [MAINTAINERS](MAINTAINERS) page.
The sign-off is a simple line at the end of the explanation for the patch. Your
signature certifies that you wrote the patch or otherwise have the right to pass
it on as an open-source patch. The rules are pretty simple: if you can certify
the below (from [developercertificate.org](https://developercertificate.org):
the below (from [developercertificate.org](http://developercertificate.org/)):
```
Developer Certificate of Origin
@ -336,8 +336,9 @@ The rules:
1. All code should be formatted with `gofumpt` (preferred) or `gofmt -s`.
2. All code should pass the default levels of
[`golint`](https://github.com/golang/lint).
3. All code should follow the guidelines covered in [Effective Go](https://go.dev/doc/effective_go)
and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
3. All code should follow the guidelines covered in [Effective
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
4. Comment the code. Tell us the why, the history and the context.
5. Document _all_ declarations and methods, even private ones. Declare
expectations, caveats and anything else that may be important. If a type
@ -359,6 +360,6 @@ The rules:
guidelines. Since you've read all the rules, you now know that.
If you are having trouble getting into the mood of idiomatic Go, we recommend
reading through [Effective Go](https://go.dev/doc/effective_go). The
[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
kool-aid is a lot easier than going thirsty.

View File

@ -1,19 +1,17 @@
# syntax=docker/dockerfile:1
ARG BASE_VARIANT=alpine
ARG GO_VERSION=1.20.13
ARG ALPINE_VERSION=3.18
ARG XX_VERSION=1.2.1
ARG GO_VERSION=1.19.5
ARG ALPINE_VERSION=3.16
ARG XX_VERSION=1.1.1
ARG GOVERSIONINFO_VERSION=v1.3.0
ARG GOTESTSUM_VERSION=v1.10.0
ARG BUILDX_VERSION=0.11.2
ARG COMPOSE_VERSION=v2.22.0
ARG GOTESTSUM_VERSION=v1.8.2
ARG BUILDX_VERSION=0.9.1
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS build-base-alpine
ENV GOTOOLCHAIN=local
COPY --link --from=xx / /
COPY --from=xx / /
RUN apk add --no-cache bash clang lld llvm file git
WORKDIR /go/src/github.com/docker/cli
@ -23,8 +21,7 @@ ARG TARGETPLATFORM
RUN xx-apk add --no-cache musl-dev gcc
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bullseye AS build-base-bullseye
ENV GOTOOLCHAIN=local
COPY --link --from=xx / /
COPY --from=xx / /
RUN apt-get update && apt-get install --no-install-recommends -y bash clang lld llvm file
WORKDIR /go/src/github.com/docker/cli
@ -43,13 +40,13 @@ FROM build-base-${BASE_VARIANT} AS goversioninfo
ARG GOVERSIONINFO_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
GOBIN=/out GO111MODULE=on CGO_ENABLED=0 go install "github.com/josephspurrier/goversioninfo/cmd/goversioninfo@${GOVERSIONINFO_VERSION}"
GOBIN=/out GO111MODULE=on go install "github.com/josephspurrier/goversioninfo/cmd/goversioninfo@${GOVERSIONINFO_VERSION}"
FROM build-base-${BASE_VARIANT} AS gotestsum
ARG GOTESTSUM_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
GOBIN=/out GO111MODULE=on CGO_ENABLED=0 go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" \
GOBIN=/out GO111MODULE=on go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" \
&& /out/gotestsum --version
FROM build-${BASE_VARIANT} AS build
@ -65,7 +62,7 @@ ARG CGO_ENABLED
ARG VERSION
# PACKAGER_NAME sets the company that produced the windows binary
ARG PACKAGER_NAME
COPY --link --from=goversioninfo /out/goversioninfo /usr/bin/goversioninfo
COPY --from=goversioninfo /out/goversioninfo /usr/bin/goversioninfo
# in bullseye arm64 target does not link with lld so configure it to use ld instead
RUN [ ! -f /etc/alpine-release ] && xx-info is-cross && [ "$(xx-info arch)" = "arm64" ] && XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple || true
RUN --mount=type=bind,target=.,ro \
@ -79,7 +76,7 @@ RUN --mount=type=bind,target=.,ro \
xx-verify $([ "$GO_LINKMODE" = "static" ] && echo "--static") /out/docker
FROM build-${BASE_VARIANT} AS test
COPY --link --from=gotestsum /out/gotestsum /usr/bin/gotestsum
COPY --from=gotestsum /out/gotestsum /usr/bin/gotestsum
ENV GO111MODULE=auto
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/root/.cache \
@ -101,34 +98,35 @@ RUN --mount=ro --mount=type=cache,target=/root/.cache \
TARGET=/out ./scripts/build/plugins e2e/cli-plugins/plugins/*
FROM build-base-alpine AS e2e-base-alpine
RUN apk add --no-cache build-base curl openssl openssh-client
RUN apk add --no-cache build-base curl docker-compose openssl openssh-client
FROM build-base-bullseye AS e2e-base-bullseye
RUN apt-get update && apt-get install -y build-essential curl openssl openssh-client
ARG COMPOSE_VERSION=1.29.2
RUN curl -fsSL https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose && \
chmod +x /usr/local/bin/docker-compose
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
FROM docker/compose-bin:${COMPOSE_VERSION} AS compose
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
FROM e2e-base-${BASE_VARIANT} AS e2e
ARG NOTARY_VERSION=v0.6.1
ADD --chmod=0755 https://github.com/theupdateframework/notary/releases/download/${NOTARY_VERSION}/notary-Linux-amd64 /usr/local/bin/notary
COPY --link e2e/testdata/notary/root-ca.cert /usr/share/ca-certificates/notary.cert
COPY e2e/testdata/notary/root-ca.cert /usr/share/ca-certificates/notary.cert
RUN echo 'notary.cert' >> /etc/ca-certificates.conf && update-ca-certificates
COPY --link --from=gotestsum /out/gotestsum /usr/bin/gotestsum
COPY --link --from=build /out ./build/
COPY --link --from=build-plugins /out ./build/
COPY --link --from=buildx /buildx /usr/libexec/docker/cli-plugins/docker-buildx
COPY --link --from=compose /docker-compose /usr/libexec/docker/cli-plugins/docker-compose
COPY --link . .
COPY --from=gotestsum /out/gotestsum /usr/bin/gotestsum
COPY --from=build /out ./build/
COPY --from=build-plugins /out ./build/
COPY --from=buildx /buildx /usr/libexec/docker/cli-plugins/docker-buildx
COPY . .
ENV DOCKER_BUILDKIT=1
ENV PATH=/go/src/github.com/docker/cli/build:$PATH
CMD ./scripts/test/e2e/entry
FROM build-base-${BASE_VARIANT} AS dev
COPY --link . .
FROM scratch AS plugins
COPY --from=build-plugins /out .
COPY . .
FROM scratch AS binary
COPY --from=build /out .
FROM scratch AS plugins
COPY --from=build-plugins /out .

View File

@ -49,9 +49,9 @@
people = [
"bsousaa",
"neersighted",
"programmerq",
"sam-thibault",
"thajeztah",
"vvoland"
]
@ -103,11 +103,6 @@
Email = "nicolas.deloof@gmail.com"
GitHub = "ndeloof"
[people.neersighted]
Name = "Bjorn Neergaard"
Email = "bneergaard@mirantis.com"
GitHub = "neersighted"
[people.programmerq]
Name = "Jeff Anderson"
Email = "jeff@docker.com"

View File

@ -1 +1 @@
24.0.0-dev
23.0.0-dev

View File

@ -74,7 +74,7 @@ func TestValidateCandidate(t *testing.T) {
{name: "experimental + allowing experimental", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: metaExperimental}},
} {
t.Run(tc.name, func(t *testing.T) {
p, err := newPlugin(tc.c, fakeroot.Commands())
p, err := newPlugin(tc.c, fakeroot)
if tc.err != "" {
assert.ErrorContains(t, err, tc.err)
} else if tc.invalid != "" {

View File

@ -3,7 +3,6 @@ package manager
import (
"fmt"
"os"
"sync"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
@ -32,69 +31,64 @@ const (
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
)
var pluginCommandStubsOnce sync.Once
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
// plugin. The command stubs will have several annotations added, see
// `CommandAnnotationPlugin*`.
func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err error) {
pluginCommandStubsOnce.Do(func() {
var plugins []Plugin
plugins, err = ListPlugins(dockerCli, rootCmd)
if err != nil {
return
func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) error {
plugins, err := ListPlugins(dockerCli, rootCmd)
if err != nil {
return err
}
for _, p := range plugins {
p := p
vendor := p.Vendor
if vendor == "" {
vendor = "unknown"
}
for _, p := range plugins {
p := p
vendor := p.Vendor
if vendor == "" {
vendor = "unknown"
}
annotations := map[string]string{
CommandAnnotationPlugin: "true",
CommandAnnotationPluginVendor: vendor,
CommandAnnotationPluginVersion: p.Version,
}
if p.Err != nil {
annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
}
rootCmd.AddCommand(&cobra.Command{
Use: p.Name,
Short: p.ShortDescription,
Run: func(_ *cobra.Command, _ []string) {},
Annotations: annotations,
DisableFlagParsing: true,
RunE: func(cmd *cobra.Command, args []string) error {
flags := rootCmd.PersistentFlags()
flags.SetOutput(nil)
perr := flags.Parse(args)
if perr != nil {
return err
}
if flags.Changed("help") {
cmd.HelpFunc()(rootCmd, args)
return nil
}
return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'", cmd.Name())
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Delegate completion to plugin
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
cargs = append(cargs, args...)
cargs = append(cargs, toComplete)
os.Args = cargs
runCommand, runErr := PluginRunCommand(dockerCli, p.Name, cmd)
if runErr != nil {
return nil, cobra.ShellCompDirectiveError
}
runErr = runCommand.Run()
if runErr == nil {
os.Exit(0) // plugin already rendered complete data
}
annotations := map[string]string{
CommandAnnotationPlugin: "true",
CommandAnnotationPluginVendor: vendor,
CommandAnnotationPluginVersion: p.Version,
}
if p.Err != nil {
annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
}
rootCmd.AddCommand(&cobra.Command{
Use: p.Name,
Short: p.ShortDescription,
Run: func(_ *cobra.Command, _ []string) {},
Annotations: annotations,
DisableFlagParsing: true,
RunE: func(cmd *cobra.Command, args []string) error {
flags := rootCmd.PersistentFlags()
flags.SetOutput(nil)
err := flags.Parse(args)
if err != nil {
return err
}
if flags.Changed("help") {
cmd.HelpFunc()(rootCmd, args)
return nil
}
return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'", cmd.Name())
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Delegate completion to plugin
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
cargs = append(cargs, args...)
cargs = append(cargs, toComplete)
os.Args = cargs
runCommand, err := PluginRunCommand(dockerCli, p.Name, cmd)
if err != nil {
return nil, cobra.ShellCompDirectiveError
},
})
}
})
return err
}
err = runCommand.Run()
if err == nil {
os.Exit(0) // plugin already rendered complete data
}
return nil, cobra.ShellCompDirectiveError
},
})
}
return nil
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package manager
import (

View File

@ -1,18 +1,15 @@
package manager
import (
"context"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config"
"github.com/fvbommel/sortorder"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
exec "golang.org/x/sys/execabs"
)
@ -123,7 +120,7 @@ func GetPlugin(name string, dockerCli command.Cli, rootcmd *cobra.Command) (*Plu
return nil, errPluginNotFound(name)
}
c := &candidate{paths[0]}
p, err := newPlugin(c, rootcmd.Commands())
p, err := newPlugin(c, rootcmd)
if err != nil {
return nil, err
}
@ -149,32 +146,19 @@ func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error
}
var plugins []Plugin
var mu sync.Mutex
eg, _ := errgroup.WithContext(context.TODO())
cmds := rootcmd.Commands()
for _, paths := range candidates {
func(paths []string) {
eg.Go(func() error {
if len(paths) == 0 {
return nil
}
c := &candidate{paths[0]}
p, err := newPlugin(c, cmds)
if err != nil {
return err
}
if !IsNotFound(p.Err) {
p.ShadowedPaths = paths[1:]
mu.Lock()
defer mu.Unlock()
plugins = append(plugins, p)
}
return nil
})
}(paths)
}
if err := eg.Wait(); err != nil {
return nil, err
if len(paths) == 0 {
continue
}
c := &candidate{paths[0]}
p, err := newPlugin(c, rootcmd)
if err != nil {
return nil, err
}
if !IsNotFound(p.Err) {
p.ShadowedPaths = paths[1:]
plugins = append(plugins, p)
}
}
sort.Slice(plugins, func(i, j int) bool {
@ -215,7 +199,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
}
c := &candidate{path: path}
plugin, err := newPlugin(c, rootcmd.Commands())
plugin, err := newPlugin(c, rootcmd)
if err != nil {
return nil, err
}

View File

@ -23,7 +23,6 @@ type Metadata struct {
// URL is a pointer to the plugin's homepage.
URL string `json:",omitempty"`
// Experimental specifies whether the plugin is experimental.
//
// Deprecated: experimental features are now always enabled in the CLI
Experimental bool `json:",omitempty"`
}

View File

@ -31,7 +31,7 @@ type Plugin struct {
// is set, and is always a `pluginError`, but the `Plugin` is still
// returned with no error. An error is only returned due to a
// non-recoverable error.
func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
path := c.Path()
if path == "" {
return Plugin{}, errors.New("plugin candidate path cannot be empty")
@ -62,20 +62,22 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
return p, nil
}
for _, cmd := range cmds {
// Ignore conflicts with commands which are
// just plugin stubs (i.e. from a previous
// call to AddPluginCommandStubs).
if IsPluginCommand(cmd) {
continue
}
if cmd.Name() == p.Name {
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
return p, nil
}
if cmd.HasAlias(p.Name) {
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
return p, nil
if rootcmd != nil {
for _, cmd := range rootcmd.Commands() {
// Ignore conflicts with commands which are
// just plugin stubs (i.e. from a previous
// call to AddPluginCommandStubs).
if IsPluginCommand(cmd) {
continue
}
if cmd.Name() == p.Name {
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
return p, nil
}
if cmd.HasAlias(p.Name) {
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
return p, nil
}
}
}

View File

@ -204,16 +204,6 @@ func DisableFlagsInUseLine(cmd *cobra.Command) {
})
}
// HasCompletionArg returns true if a cobra completion arg request is found.
func HasCompletionArg(args []string) bool {
for _, arg := range args {
if arg == cobra.ShellCompRequestCmd || arg == cobra.ShellCompNoDescRequestCmd {
return true
}
}
return false
}
var helpCommand = &cobra.Command{
Use: "help [command]",
Short: "Help about the command",
@ -426,7 +416,7 @@ func invalidPluginReason(cmd *cobra.Command) string {
return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid]
}
const usageTemplate = `Usage:
var usageTemplate = `Usage:
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
{{- if .HasSubCommands}} {{ .CommandPath}}{{- if .HasAvailableFlags}} [OPTIONS]{{end}} COMMAND{{end}}
@ -525,5 +515,5 @@ Run '{{.CommandPath}} COMMAND --help' for more information on a command.
{{- end}}
`
const helpTemplate = `
var helpTemplate = `
{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`

View File

@ -14,21 +14,21 @@ type fakeClient struct {
checkpointListFunc func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
}
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options types.CheckpointCreateOptions) error {
func (cli *fakeClient) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
if cli.checkpointCreateFunc != nil {
return cli.checkpointCreateFunc(container, options)
}
return nil
}
func (cli *fakeClient) CheckpointDelete(_ context.Context, container string, options types.CheckpointDeleteOptions) error {
func (cli *fakeClient) CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error {
if cli.checkpointDeleteFunc != nil {
return cli.checkpointDeleteFunc(container, options)
}
return nil
}
func (cli *fakeClient) CheckpointList(_ context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
func (cli *fakeClient) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
if cli.checkpointListFunc != nil {
return cli.checkpointListFunc(container, options)
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package command
import (
@ -11,6 +8,7 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
@ -50,7 +48,9 @@ type Streams interface {
// Cli represents the docker command line client.
type Cli interface {
Client() client.APIClient
Streams
Out() *streams.Out
Err() io.Writer
In() *streams.In
SetIn(in *streams.In)
Apply(ops ...DockerCliOption) error
ConfigFile() *configfile.ConfigFile
@ -164,8 +164,8 @@ func (cli *DockerCli) ContentTrustEnabled() bool {
// BuildKitEnabled returns buildkit is enabled or not.
func (cli *DockerCli) BuildKitEnabled() (bool, error) {
// use DOCKER_BUILDKIT env var value if set and not empty
if v := os.Getenv("DOCKER_BUILDKIT"); v != "" {
// use DOCKER_BUILDKIT env var value if set
if v, ok := os.LookupEnv("DOCKER_BUILDKIT"); ok {
enabled, err := strconv.ParseBool(v)
if err != nil {
return false, errors.Wrap(err, "DOCKER_BUILDKIT environment variable expects boolean value")
@ -191,7 +191,7 @@ func (cli *DockerCli) ManifestStore() manifeststore.Store {
// RegistryClient returns a client for communicating with a Docker distribution
// registry
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
resolver := func(ctx context.Context, index *registry.IndexInfo) types.AuthConfig {
return ResolveAuthConfig(ctx, cli, index)
}
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
@ -329,8 +329,13 @@ func (cli *DockerCli) getInitTimeout() time.Duration {
func (cli *DockerCli) initializeFromClient() {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, cli.getInitTimeout())
defer cancel()
if !strings.HasPrefix(cli.dockerEndpoint.Host, "ssh://") {
// @FIXME context.WithTimeout doesn't work with connhelper / ssh connections
// time="2020-04-10T10:16:26Z" level=warning msg="commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
var cancel func()
ctx, cancel = context.WithTimeout(ctx, cli.getInitTimeout())
defer cancel()
}
ping, err := cli.client.Ping(ctx)
if err != nil {
@ -378,7 +383,7 @@ func (cli *DockerCli) ContextStore() store.Store {
// the "default" context is used if:
//
// - The "--host" option is set
// - The "DOCKER_HOST" ([client.EnvOverrideHost]) environment variable is set
// - The "DOCKER_HOST" ([DefaultContextName]) environment variable is set
// to a non-empty value.
//
// In these cases, the default context is used, which uses the host as

View File

@ -6,7 +6,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/api/types/filters"
"github.com/spf13/cobra"
)
@ -66,13 +66,13 @@ func ContainerNames(dockerCli command.Cli, all bool, filters ...func(types.Conta
// VolumeNames offers completion for volumes
func VolumeNames(dockerCli command.Cli) ValidArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCli.Client().VolumeList(cmd.Context(), volume.ListOptions{})
list, err := dockerCli.Client().VolumeList(cmd.Context(), filters.Args{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, vol := range list.Volumes {
names = append(names, vol.Name)
for _, volume := range list.Volumes {
names = append(names, volume.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
@ -94,6 +94,6 @@ func NetworkNames(dockerCli command.Cli) ValidArgsFn {
}
// NoComplete is used for commands where there's no relevant completion
func NoComplete(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
func NoComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -10,34 +10,34 @@ import (
type fakeClient struct {
client.Client
configCreateFunc func(context.Context, swarm.ConfigSpec) (types.ConfigCreateResponse, error)
configInspectFunc func(context.Context, string) (swarm.Config, []byte, error)
configListFunc func(context.Context, types.ConfigListOptions) ([]swarm.Config, error)
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
configInspectFunc func(string) (swarm.Config, []byte, error)
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
configRemoveFunc func(string) error
}
func (c *fakeClient) ConfigCreate(ctx context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if c.configCreateFunc != nil {
return c.configCreateFunc(ctx, spec)
return c.configCreateFunc(spec)
}
return types.ConfigCreateResponse{}, nil
}
func (c *fakeClient) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
if c.configInspectFunc != nil {
return c.configInspectFunc(ctx, id)
return c.configInspectFunc(id)
}
return swarm.Config{}, nil, nil
}
func (c *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
if c.configListFunc != nil {
return c.configListFunc(ctx, options)
return c.configListFunc(options)
}
return []swarm.Config{}, nil
}
func (c *fakeClient) ConfigRemove(_ context.Context, name string) error {
func (c *fakeClient) ConfigRemove(ctx context.Context, name string) error {
if c.configRemoveFunc != nil {
return c.configRemoveFunc(name)
}

View File

@ -1,7 +1,6 @@
package config
import (
"context"
"io"
"os"
"path/filepath"
@ -23,7 +22,7 @@ const configDataFile = "config-create-with-name.golden"
func TestConfigCreateErrors(t *testing.T) {
testCases := []struct {
args []string
configCreateFunc func(context.Context, swarm.ConfigSpec) (types.ConfigCreateResponse, error)
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
expectedError string
}{
{
@ -36,7 +35,7 @@ func TestConfigCreateErrors(t *testing.T) {
},
{
args: []string{"name", filepath.Join("testdata", configDataFile)},
configCreateFunc: func(_ context.Context, configSpec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
configCreateFunc: func(configSpec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
return types.ConfigCreateResponse{}, errors.Errorf("error creating config")
},
expectedError: "error creating config",
@ -58,7 +57,7 @@ func TestConfigCreateWithName(t *testing.T) {
name := "foo"
var actual []byte
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if spec.Name != name {
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}
@ -97,7 +96,7 @@ func TestConfigCreateWithLabels(t *testing.T) {
}
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if !reflect.DeepEqual(spec, expected) {
return types.ConfigCreateResponse{}, errors.Errorf("expected %+v, got %+v", expected, spec)
}
@ -123,7 +122,7 @@ func TestConfigCreateWithTemplatingDriver(t *testing.T) {
name := "foo"
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if spec.Name != name {
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package config
import (

View File

@ -1,7 +1,6 @@
package config
import (
"context"
"fmt"
"io"
"testing"
@ -19,7 +18,7 @@ func TestConfigInspectErrors(t *testing.T) {
testCases := []struct {
args []string
flags map[string]string
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
configInspectFunc func(configID string) (swarm.Config, []byte, error)
expectedError string
}{
{
@ -27,7 +26,7 @@ func TestConfigInspectErrors(t *testing.T) {
},
{
args: []string{"foo"},
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
configInspectFunc: func(configID string) (swarm.Config, []byte, error) {
return swarm.Config{}, nil, errors.Errorf("error while inspecting the config")
},
expectedError: "error while inspecting the config",
@ -41,7 +40,7 @@ func TestConfigInspectErrors(t *testing.T) {
},
{
args: []string{"foo", "bar"},
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
configInspectFunc: func(configID string) (swarm.Config, []byte, error) {
if configID == "foo" {
return *Config(ConfigName("foo")), nil, nil
}
@ -69,12 +68,12 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
testCases := []struct {
name string
args []string
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
configInspectFunc func(configID string) (swarm.Config, []byte, error)
}{
{
name: "single-config",
args: []string{"foo"},
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
configInspectFunc: func(name string) (swarm.Config, []byte, error) {
if name != "foo" {
return swarm.Config{}, nil, errors.Errorf("Invalid name, expected %s, got %s", "foo", name)
}
@ -84,7 +83,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
{
name: "multiple-configs-with-labels",
args: []string{"foo", "bar"},
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
configInspectFunc: func(name string) (swarm.Config, []byte, error) {
return *Config(ConfigID("ID-"+name), ConfigName(name), ConfigLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
@ -101,7 +100,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
}
func TestConfigInspectWithFormat(t *testing.T) {
configInspectFunc := func(_ context.Context, name string) (swarm.Config, []byte, error) {
configInspectFunc := func(name string) (swarm.Config, []byte, error) {
return *Config(ConfigName("foo"), ConfigLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
@ -110,7 +109,7 @@ func TestConfigInspectWithFormat(t *testing.T) {
name string
format string
args []string
configInspectFunc func(_ context.Context, name string) (swarm.Config, []byte, error)
configInspectFunc func(name string) (swarm.Config, []byte, error)
}{
{
name: "simple-template",
@ -140,11 +139,11 @@ func TestConfigInspectWithFormat(t *testing.T) {
func TestConfigInspectPretty(t *testing.T) {
testCases := []struct {
name string
configInspectFunc func(context.Context, string) (swarm.Config, []byte, error)
configInspectFunc func(string) (swarm.Config, []byte, error)
}{
{
name: "simple",
configInspectFunc: func(_ context.Context, id string) (swarm.Config, []byte, error) {
configInspectFunc: func(id string) (swarm.Config, []byte, error) {
return *Config(
ConfigLabels(map[string]string{
"lbl1": "value1",

View File

@ -1,7 +1,6 @@
package config
import (
"context"
"io"
"testing"
"time"
@ -20,7 +19,7 @@ import (
func TestConfigListErrors(t *testing.T) {
testCases := []struct {
args []string
configListFunc func(context.Context, types.ConfigListOptions) ([]swarm.Config, error)
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
expectedError string
}{
{
@ -28,7 +27,7 @@ func TestConfigListErrors(t *testing.T) {
expectedError: "accepts no argument",
},
{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{}, errors.Errorf("error listing configs")
},
expectedError: "error listing configs",
@ -48,7 +47,7 @@ func TestConfigListErrors(t *testing.T) {
func TestConfigList(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*Config(ConfigID("ID-1-foo"),
ConfigName("1-foo"),
@ -78,7 +77,7 @@ func TestConfigList(t *testing.T) {
func TestConfigListWithQuietOption(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*Config(ConfigID("ID-foo"), ConfigName("foo")),
*Config(ConfigID("ID-bar"), ConfigName("bar"), ConfigLabels(map[string]string{
@ -95,7 +94,7 @@ func TestConfigListWithQuietOption(t *testing.T) {
func TestConfigListWithConfigFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*Config(ConfigID("ID-foo"), ConfigName("foo")),
*Config(ConfigID("ID-bar"), ConfigName("bar"), ConfigLabels(map[string]string{
@ -114,7 +113,7 @@ func TestConfigListWithConfigFormat(t *testing.T) {
func TestConfigListWithFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*Config(ConfigID("ID-foo"), ConfigName("foo")),
*Config(ConfigID("ID-bar"), ConfigName("bar"), ConfigLabels(map[string]string{
@ -131,7 +130,7 @@ func TestConfigListWithFormat(t *testing.T) {
func TestConfigListWithFilter(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
assert.Check(t, is.Equal("foo", options.Filters.Get("name")[0]))
assert.Check(t, is.Equal("lbl1=Label-bar", options.Filters.Get("label")[0]))
return []swarm.Config{

View File

@ -64,7 +64,7 @@ func (f *fakeClient) ContainerExecInspect(_ context.Context, execID string) (typ
return types.ContainerExecInspect{}, nil
}
func (f *fakeClient) ContainerExecStart(context.Context, string, types.ExecStartCheck) error {
func (f *fakeClient) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
return nil
}
@ -89,7 +89,7 @@ func (f *fakeClient) ContainerRemove(ctx context.Context, container string, opti
return nil
}
func (f *fakeClient) ImageCreate(_ context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
func (f *fakeClient) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
if f.imageCreateFunc != nil {
return f.imageCreateFunc(parentReference, options)
}

View File

@ -1,20 +1,15 @@
package container
import (
"bytes"
"context"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"strings"
"sync/atomic"
"time"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/system"
@ -53,73 +48,26 @@ type cpConfig struct {
// copying files to/from a container.
type copyProgressPrinter struct {
io.ReadCloser
total *int64
toContainer bool
total *float64
writer io.Writer
}
const (
copyToContainerHeader = "Copying to container - "
copyFromContainerHeader = "Copying from container - "
copyProgressUpdateThreshold = 75 * time.Millisecond
)
func (pt *copyProgressPrinter) Read(p []byte) (int, error) {
n, err := pt.ReadCloser.Read(p)
atomic.AddInt64(pt.total, int64(n))
return n, err
}
*pt.total += float64(n)
func copyProgress(ctx context.Context, dst io.Writer, header string, total *int64) (func(), <-chan struct{}) {
done := make(chan struct{})
if !streams.NewOut(dst).IsTerminal() {
close(done)
return func() {}, done
}
fmt.Fprint(dst, aec.Save)
fmt.Fprint(dst, "Preparing to copy...")
restore := func() {
fmt.Fprint(dst, aec.Restore)
fmt.Fprint(dst, aec.EraseLine(aec.EraseModes.All))
}
go func() {
defer close(done)
fmt.Fprint(dst, aec.Hide)
defer fmt.Fprint(dst, aec.Show)
fmt.Fprint(dst, aec.Restore)
fmt.Fprint(dst, aec.EraseLine(aec.EraseModes.All))
fmt.Fprint(dst, header)
var last int64
fmt.Fprint(dst, progressHumanSize(last))
buf := bytes.NewBuffer(nil)
ticker := time.NewTicker(copyProgressUpdateThreshold)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
n := atomic.LoadInt64(total)
if n == last {
// Don't write to the terminal, if we don't need to.
continue
}
// Write to the buffer first to avoid flickering and context switching
fmt.Fprint(buf, aec.Column(uint(len(header)+1)))
fmt.Fprint(buf, aec.EraseLine(aec.EraseModes.Tail))
fmt.Fprint(buf, progressHumanSize(n))
buf.WriteTo(dst)
buf.Reset()
last += n
}
if err == nil {
fmt.Fprint(pt.writer, aec.Restore)
fmt.Fprint(pt.writer, aec.EraseLine(aec.EraseModes.All))
if pt.toContainer {
fmt.Fprintln(pt.writer, "Copying to container - "+units.HumanSize(*pt.total))
} else {
fmt.Fprintln(pt.writer, "Copying from container - "+units.HumanSize(*pt.total))
}
}()
return restore, done
}
return n, err
}
// NewCopyCommand creates a new `docker cp` command
@ -165,10 +113,6 @@ func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
return cmd
}
func progressHumanSize(n int64) string {
return units.HumanSizeWithPrecision(float64(n), 3)
}
func runCopy(dockerCli command.Cli, opts copyOptions) error {
srcContainer, srcPath := splitCpArg(opts.source)
destContainer, destPath := splitCpArg(opts.destination)
@ -209,7 +153,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
if absPath, err = filepath.Abs(localPath); err != nil {
return
}
return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
return archive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil
}
func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpConfig) (err error) {
@ -249,9 +193,6 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
}
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
defer cancel()
content, stat, err := client.CopyFromContainer(ctx, copyConfig.container, srcPath)
if err != nil {
return err
@ -270,11 +211,13 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
RebaseName: rebaseName,
}
var copiedSize int64
var copiedSize float64
if !copyConfig.quiet {
content = &copyProgressPrinter{
ReadCloser: content,
total: &copiedSize,
ReadCloser: content,
toContainer: false,
writer: dockerCli.Err(),
total: &copiedSize,
}
}
@ -288,12 +231,12 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
return archive.CopyTo(preArchive, srcInfo, dstPath)
}
restore, done := copyProgress(ctx, dockerCli.Err(), copyFromContainerHeader, &copiedSize)
fmt.Fprint(dockerCli.Err(), aec.Save)
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
res := archive.CopyTo(preArchive, srcInfo, dstPath)
cancel()
<-done
restore()
fmt.Fprintln(dockerCli.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", dstPath)
fmt.Fprint(dockerCli.Err(), aec.Restore)
fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All))
fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", dstPath)
return res
}
@ -350,7 +293,7 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
var (
content io.ReadCloser
resolvedDstPath string
copiedSize int64
copiedSize float64
)
if srcPath == "-" {
@ -394,8 +337,10 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
content = preparedArchive
if !copyConfig.quiet {
content = &copyProgressPrinter{
ReadCloser: content,
total: &copiedSize,
ReadCloser: content,
toContainer: true,
writer: dockerCli.Err(),
total: &copiedSize,
}
}
}
@ -409,13 +354,12 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
return client.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
}
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
restore, done := copyProgress(ctx, dockerCli.Err(), copyToContainerHeader, &copiedSize)
fmt.Fprint(dockerCli.Err(), aec.Save)
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
res := client.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
cancel()
<-done
restore()
fmt.Fprintln(dockerCli.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
fmt.Fprint(dockerCli.Err(), aec.Restore)
fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All))
fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
return res
}

View File

@ -12,7 +12,6 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/opts"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
@ -20,6 +19,7 @@ import (
"github.com/docker/docker/api/types/versions"
apiclient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -95,16 +95,16 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio
}
}
copts.env = *opts.NewListOptsRef(&newEnv, nil)
containerCfg, err := parse(flags, copts, dockerCli.ServerInfo().OSType)
containerConfig, err := parse(flags, copts, dockerCli.ServerInfo().OSType)
if err != nil {
reportError(dockerCli.Err(), "create", err.Error(), true)
return cli.StatusError{StatusCode: 125}
}
if err = validateAPIVersion(containerCfg, dockerCli.Client().ClientVersion()); err != nil {
if err = validateAPIVersion(containerConfig, dockerCli.Client().ClientVersion()); err != nil {
reportError(dockerCli.Err(), "create", err.Error(), true)
return cli.StatusError{StatusCode: 125}
}
response, err := createContainer(context.Background(), dockerCli, containerCfg, options)
response, err := createContainer(context.Background(), dockerCli, containerConfig, options)
if err != nil {
return err
}
@ -112,27 +112,41 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio
return nil
}
// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa).
func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *createOptions) error {
encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image)
func pullImage(ctx context.Context, dockerCli command.Cli, image string, platform string, out io.Writer) error {
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return err
}
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, types.ImageCreateOptions{
// Resolve the Repository name from fqn to RepositoryInfo
repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil {
return err
}
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
options := types.ImageCreateOptions{
RegistryAuth: encodedAuth,
Platform: opts.platform,
})
Platform: platform,
}
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
if err != nil {
return err
}
defer responseBody.Close()
out := dockerCli.Err()
if opts.quiet {
out = io.Discard
}
return jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(out), nil)
return jsonmessage.DisplayJSONMessagesStream(
responseBody,
out,
dockerCli.Out().FD(),
dockerCli.Out().IsTerminal(),
nil)
}
type cidFile struct {
@ -185,10 +199,10 @@ func newCIDFile(path string) (*cidFile, error) {
}
//nolint:gocyclo
func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *containerConfig, opts *createOptions) (*container.CreateResponse, error) {
config := containerCfg.Config
hostConfig := containerCfg.HostConfig
networkingConfig := containerCfg.NetworkingConfig
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, opts *createOptions) (*container.CreateResponse, error) {
config := containerConfig.Config
hostConfig := containerConfig.HostConfig
networkingConfig := containerConfig.NetworkingConfig
warnOnOomKillDisable(*hostConfig, dockerCli.Err())
warnOnLocalhostDNS(*hostConfig, dockerCli.Err())
@ -213,7 +227,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !opts.untrusted {
var err error
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil)
if err != nil {
return nil, err
}
@ -222,7 +236,11 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
}
pullAndTagImage := func() error {
if err := pullImage(ctx, dockerCli, config.Image, opts); err != nil {
pullOut := dockerCli.Err()
if opts.quiet {
pullOut = io.Discard
}
if err := pullImage(ctx, dockerCli, config.Image, opts.platform, pullOut); err != nil {
return err
}
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
@ -275,8 +293,8 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
}
}
for _, w := range response.Warnings {
fmt.Fprintf(dockerCli.Err(), "WARNING: %s\n", w)
for _, warning := range response.Warnings {
fmt.Fprintf(dockerCli.Err(), "WARNING: %s\n", warning)
}
err = containerIDFile.Write(response.ID)
return &response, err

View File

@ -3,6 +3,7 @@ package container
import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/archive"
)
const (
@ -22,7 +23,7 @@ func NewDiffFormat(source string) formatter.Format {
}
// DiffFormatWrite writes formatted diff using the Context
func DiffFormatWrite(ctx formatter.Context, changes []container.FilesystemChange) error {
func DiffFormatWrite(ctx formatter.Context, changes []container.ContainerChangeResponseItem) error {
render := func(format func(subContext formatter.SubContext) error) error {
for _, change := range changes {
if err := format(&diffContext{c: change}); err != nil {
@ -36,7 +37,7 @@ func DiffFormatWrite(ctx formatter.Context, changes []container.FilesystemChange
type diffContext struct {
formatter.HeaderContext
c container.FilesystemChange
c container.ContainerChangeResponseItem
}
func newDiffContext() *diffContext {
@ -53,7 +54,16 @@ func (d *diffContext) MarshalJSON() ([]byte, error) {
}
func (d *diffContext) Type() string {
return d.c.Kind.String()
var kind string
switch d.c.Kind {
case archive.ChangeModify:
kind = "C"
case archive.ChangeAdd:
kind = "A"
case archive.ChangeDelete:
kind = "D"
}
return kind
}
func (d *diffContext) Path() string {

View File

@ -6,6 +6,7 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/archive"
"gotest.tools/v3/assert"
)
@ -40,10 +41,10 @@ D: /usr/app/old_app.js
},
}
diffs := []container.FilesystemChange{
{Kind: container.ChangeModify, Path: "/var/log/app.log"},
{Kind: container.ChangeAdd, Path: "/usr/app/app.js"},
{Kind: container.ChangeDelete, Path: "/usr/app/old_app.js"},
diffs := []container.ContainerChangeResponseItem{
{Kind: archive.ChangeModify, Path: "/var/log/app.log"},
{Kind: archive.ChangeAdd, Path: "/usr/app/app.js"},
{Kind: archive.ChangeDelete, Path: "/usr/app/old_app.js"},
}
for _, tc := range cases {

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package container
import (

View File

@ -17,15 +17,14 @@ import (
)
type psOptions struct {
quiet bool
size bool
sizeChanged bool
all bool
noTrunc bool
nLatest bool
last int
format string
filter opts.FilterOpt
quiet bool
size bool
all bool
noTrunc bool
nLatest bool
last int
format string
filter opts.FilterOpt
}
// NewPsCommand creates a new cobra.Command for `docker ps`
@ -37,7 +36,6 @@ func NewPsCommand(dockerCli command.Cli) *cobra.Command {
Short: "List containers",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
options.sizeChanged = cmd.Flags().Changed("size")
return runPs(dockerCli, &options)
},
Annotations: map[string]string{
@ -80,8 +78,13 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
options.Limit = 1
}
// always validate template when `--format` is used, for consistency
if len(opts.format) > 0 {
if !opts.quiet && !options.Size && len(opts.format) > 0 {
// The --size option isn't set, but .Size may be used in the template.
// Parse and execute the given template to detect if the .Size field is
// used. If it is, then automatically enable the --size option. See #24696
//
// Only requesting container size information when needed is an optimization,
// because calculating the size is a costly operation.
tmpl, err := templates.NewParse("", opts.format)
if err != nil {
return nil, errors.Wrap(err, "failed to parse template")
@ -95,19 +98,8 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
return nil, errors.Wrap(err, "failed to execute template")
}
// if `size` was not explicitly set to false (with `--size=false`)
// and `--quiet` is not set, request size if the template requires it
if !opts.quiet && !options.Size && !opts.sizeChanged {
// The --size option isn't set, but .Size may be used in the template.
// Parse and execute the given template to detect if the .Size field is
// used. If it is, then automatically enable the --size option. See #24696
//
// Only requesting container size information when needed is an optimization,
// because calculating the size is a costly operation.
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
options.Size = true
}
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
options.Size = true
}
}
@ -120,8 +112,6 @@ func runPs(dockerCli command.Cli, options *psOptions) error {
if len(options.format) == 0 {
// load custom psFormat from CLI config (if any)
options.format = dockerCli.ConfigFile().PsFormat
} else if options.quiet {
_, _ = dockerCli.Err().Write([]byte("WARNING: Ignoring custom format, because both --format and --quiet are set.\n"))
}
listOptions, err := buildContainerListOptions(options)

View File

@ -231,56 +231,15 @@ func TestContainerListFormatTemplateWithArg(t *testing.T) {
}
func TestContainerListFormatSizeSetsOption(t *testing.T) {
tests := []struct {
doc, format, sizeFlag string
sizeExpected bool
}{
{
doc: "detect with all fields",
format: `{{json .}}`,
sizeExpected: true,
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) {
assert.Check(t, options.Size)
return []types.Container{}, nil
},
{
doc: "detect with explicit field",
format: `{{.Size}}`,
sizeExpected: true,
},
{
doc: "detect no size",
format: `{{.Names}}`,
sizeExpected: false,
},
{
doc: "override enable",
format: `{{.Names}}`,
sizeFlag: "true",
sizeExpected: true,
},
{
doc: "override disable",
format: `{{.Size}}`,
sizeFlag: "false",
sizeExpected: false,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) {
assert.Check(t, is.Equal(options.Size, tc.sizeExpected))
return []types.Container{}, nil
},
})
cmd := newListCommand(cli)
cmd.Flags().Set("format", tc.format)
if tc.sizeFlag != "" {
cmd.Flags().Set("size", tc.sizeFlag)
}
assert.NilError(t, cmd.Execute())
})
}
})
cmd := newListCommand(cli)
cmd.Flags().Set("format", `{{.Size}}`)
assert.NilError(t, cmd.Execute())
}
func TestContainerListWithConfigFormat(t *testing.T) {
@ -309,22 +268,8 @@ func TestContainerListWithFormat(t *testing.T) {
}, nil
},
})
t.Run("with format", func(t *testing.T) {
cli.OutBuffer().Reset()
cmd := newListCommand(cli)
assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-with-format.golden")
})
t.Run("with format and quiet", func(t *testing.T) {
cli.OutBuffer().Reset()
cmd := newListCommand(cli)
assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
assert.Check(t, cmd.Flags().Set("quiet", "true"))
assert.NilError(t, cmd.Execute())
assert.Equal(t, cli.ErrBuffer().String(), "WARNING: Ignoring custom format, because both --format and --quiet are set.\n")
golden.Assert(t, cli.OutBuffer().String(), "container-list-quiet.golden")
})
cmd := newListCommand(cli)
cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}")
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-with-format.golden")
}

View File

@ -123,7 +123,6 @@ type containerOptions struct {
runtime string
autoRemove bool
init bool
annotations *opts.MapOpts
Image string
Args []string
@ -164,7 +163,6 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
ulimits: opts.NewUlimitOpt(nil),
volumes: opts.NewListOpts(nil),
volumesFrom: opts.NewListOpts(nil),
annotations: opts.NewMapOpts(nil, nil),
}
// General purpose flags
@ -299,10 +297,6 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes")
flags.SetAnnotation("init", "version", []string{"1.25"})
flags.Var(copts.annotations, "annotation", "Add an annotation to the container (passed through to the OCI runtime)")
flags.SetAnnotation("annotation", "version", []string{"1.43"})
return copts
}
@ -354,10 +348,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
volumes := copts.volumes.GetMap()
// add any bind targets to the list of container volumes
for bind := range copts.volumes.GetMap() {
parsed, err := loader.ParseVolume(bind)
if err != nil {
return nil, err
}
parsed, _ := loader.ParseVolume(bind)
if parsed.Source != "" {
toBind := bind
@ -663,7 +654,6 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
Mounts: mounts,
MaskedPaths: maskedPaths,
ReadonlyPaths: readonlyPaths,
Annotations: copts.annotations.GetAll(),
}
if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {

View File

@ -49,11 +49,11 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *network
return nil, nil, nil, err
}
// TODO: fix tests to accept ContainerConfig
containerCfg, err := parse(flags, copts, runtime.GOOS)
containerConfig, err := parse(flags, copts, runtime.GOOS)
if err != nil {
return nil, nil, nil, err
}
return containerCfg.Config, containerCfg.HostConfig, containerCfg.NetworkingConfig, err
return containerConfig.Config, containerConfig.HostConfig, containerConfig.NetworkingConfig, err
}
func setupRunFlags() (*pflag.FlagSet, *containerOptions) {

View File

@ -75,6 +75,6 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
// RunPrune calls the Container Prune API
// This returns the amount of space reclaimed and a detailed output string
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
func RunPrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
return runPrune(dockerCli, pruneOptions{force: true, filter: filter})
}

View File

@ -105,22 +105,22 @@ func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copt
}
}
copts.env = *opts.NewListOptsRef(&newEnv, nil)
containerCfg, err := parse(flags, copts, dockerCli.ServerInfo().OSType)
containerConfig, err := parse(flags, copts, dockerCli.ServerInfo().OSType)
// just in case the parse does not exit
if err != nil {
reportError(dockerCli.Err(), "run", err.Error(), true)
return cli.StatusError{StatusCode: 125}
}
if err = validateAPIVersion(containerCfg, dockerCli.CurrentVersion()); err != nil {
if err = validateAPIVersion(containerConfig, dockerCli.CurrentVersion()); err != nil {
reportError(dockerCli.Err(), "run", err.Error(), true)
return cli.StatusError{StatusCode: 125}
}
return runContainer(dockerCli, ropts, copts, containerCfg)
return runContainer(dockerCli, ropts, copts, containerConfig)
}
//nolint:gocyclo
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerCfg *containerConfig) error {
config := containerCfg.Config
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
config := containerConfig.Config
stdout, stderr := dockerCli.Out(), dockerCli.Err()
client := dockerCli.Client()
@ -144,7 +144,7 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
ctx, cancelFun := context.WithCancel(context.Background())
defer cancelFun()
createResponse, err := createContainer(ctx, dockerCli, containerCfg, &opts.createOptions)
createResponse, err := createContainer(ctx, dockerCli, containerConfig, &opts.createOptions)
if err != nil {
reportError(stderr, "run", err.Error(), true)
return runStartContainerErr(err)
@ -173,11 +173,11 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
}
closeFn, err := attachContainer(ctx, dockerCli, &errCh, config, createResponse.ID)
close, err := attachContainer(ctx, dockerCli, &errCh, config, createResponse.ID)
if err != nil {
return err
}
defer closeFn()
defer close()
}
statusChan := waitExitOrRemoved(ctx, dockerCli, createResponse.ID, copts.autoRemove)

View File

@ -1,2 +0,0 @@
container_id
container_id

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package command
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package context
import (
@ -121,7 +118,10 @@ func createNewContext(o *CreateOptions, cli command.Cli, s store.Writer) error {
if err := s.CreateOrUpdate(contextMetadata); err != nil {
return err
}
return s.ResetTLSMaterial(o.Name, &contextTLSData)
if err := s.ResetTLSMaterial(o.Name, &contextTLSData); err != nil {
return err
}
return nil
}
func checkContextNameForCreation(s store.Reader, name string) error {

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package context
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package context
import (

View File

@ -6,7 +6,6 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/docker/cli/cli/command"
@ -14,6 +13,7 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/flags"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/homedir"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
@ -57,11 +57,7 @@ func TestUseDefaultWithoutConfigFile(t *testing.T) {
// the _default_ configuration file. If we specify a custom configuration
// file, the CLI produces an error if the file doesn't exist.
tmpHomeDir := t.TempDir()
if runtime.GOOS == "windows" {
t.Setenv("USERPROFILE", tmpHomeDir)
} else {
t.Setenv("HOME", tmpHomeDir)
}
t.Setenv(homedir.Key(), tmpHomeDir)
configDir := filepath.Join(tmpHomeDir, ".docker")
configFilePath := filepath.Join(configDir, "config.json")

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package command
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package command
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package command
import (

View File

@ -3,28 +3,28 @@ package command
import (
"sync"
"github.com/docker/docker/api/types/events"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/sirupsen/logrus"
)
// EventHandler is abstract interface for user to customize
// own handle functions of each type of events
type EventHandler interface {
Handle(action string, h func(events.Message))
Watch(c <-chan events.Message)
Handle(action string, h func(eventtypes.Message))
Watch(c <-chan eventtypes.Message)
}
// InitEventHandler initializes and returns an EventHandler
func InitEventHandler() EventHandler {
return &eventHandler{handlers: make(map[string]func(events.Message))}
return &eventHandler{handlers: make(map[string]func(eventtypes.Message))}
}
type eventHandler struct {
handlers map[string]func(events.Message)
handlers map[string]func(eventtypes.Message)
mu sync.Mutex
}
func (w *eventHandler) Handle(action string, h func(events.Message)) {
func (w *eventHandler) Handle(action string, h func(eventtypes.Message)) {
w.mu.Lock()
w.handlers[action] = h
w.mu.Unlock()
@ -33,7 +33,7 @@ func (w *eventHandler) Handle(action string, h func(events.Message)) {
// Watch ranges over the passed in event chan and processes the events based on the
// handlers created for a given action.
// To stop watching, close the event chan.
func (w *eventHandler) Watch(c <-chan events.Message) {
func (w *eventHandler) Watch(c <-chan eventtypes.Message) {
for e := range c {
w.mu.Lock()
h, exists := w.handlers[e.Action]

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (
@ -58,9 +55,6 @@ ports: {{- pad .Ports 1 0}}
}
return Format(format)
default: // custom format
if quiet {
return DefaultQuietFormat
}
return Format(source)
}
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (
@ -166,7 +163,7 @@ containerID2 ubuntu "" 24 hours ago foobar_bar
},
{
Context{Format: NewContainerFormat("table {{.Image}}", true, false)},
"containerID1\ncontainerID2\n",
"IMAGE\nubuntu\nubuntu\n",
},
{
Context{Format: NewContainerFormat("table", true, false)},

View File

@ -84,3 +84,10 @@ func (c *clientContextContext) Error() string {
// TODO(thaJeztah) add "--no-trunc" option to context ls and set default to 30 cols to match "docker service ps"
return Ellipsis(c.c.Error, maxErrLength)
}
// KubernetesEndpoint returns the kubernetes endpoint.
//
// Deprecated: support for kubernetes endpoints in contexts has been removed, and this formatting option will always be empty.
func (c *clientContextContext) KubernetesEndpoint() string {
return ""
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import "strings"

View File

@ -14,7 +14,7 @@ import (
)
const (
defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}"
defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}\t{{.VirtualSize}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}"
defaultDiskUsageContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.RunningFor}}\t{{.Status}}\t{{.Names}}"
defaultDiskUsageVolumeTableFormat = "table {{.Name}}\t{{.Links}}\t{{.Size}}"
defaultDiskUsageBuildCacheTableFormat = "table {{.ID}}\t{{.CacheType}}\t{{.Size}}\t{{.CreatedSince}}\t{{.LastUsedSince}}\t{{.UsageCount}}\t{{.Shared}}"
@ -296,10 +296,10 @@ func (c *diskUsageImagesContext) Reclaimable() string {
for _, i := range c.images {
if i.Containers != 0 {
if i.Size == -1 || i.SharedSize == -1 {
if i.VirtualSize == -1 || i.SharedSize == -1 {
continue
}
used += i.Size - i.SharedSize
used += i.VirtualSize - i.SharedSize
}
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (
@ -22,7 +19,7 @@ const (
JSONFormatKey = "json"
DefaultQuietFormat = "{{.ID}}"
JSONFormat = "{{json .}}"
jsonFormat = "{{json .}}"
)
// Format is the format string rendered using the Context
@ -65,7 +62,7 @@ func (c *Context) preFormat() {
case c.Format.IsTable():
c.finalFormat = c.finalFormat[len(TableFormatKey):]
case c.Format.IsJSON():
c.finalFormat = JSONFormat
c.finalFormat = jsonFormat
}
c.finalFormat = strings.Trim(c.finalFormat, " ")

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (

View File

@ -27,9 +27,6 @@ type ImageContext struct {
}
func isDangling(image types.ImageSummary) bool {
if len(image.RepoTags) == 0 && len(image.RepoDigests) == 0 {
return true
}
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
}
@ -205,7 +202,7 @@ func newImageContext() *imageContext {
"CreatedAt": CreatedAtHeader,
"Size": SizeHeader,
"Containers": containersHeader,
"VirtualSize": SizeHeader, // Deprecated: VirtualSize is deprecated, and equivalent to Size.
"VirtualSize": SizeHeader,
"SharedSize": sharedSizeHeader,
"UniqueSize": uniqueSizeHeader,
}
@ -260,13 +257,8 @@ func (c *imageContext) Containers() string {
return fmt.Sprintf("%d", c.i.Containers)
}
// VirtualSize shows the virtual size of the image and all of its parent
// images. Starting with docker 1.10, images are self-contained, and
// the VirtualSize is identical to Size.
//
// Deprecated: VirtualSize is deprecated, and equivalent to [imageContext.Size].
func (c *imageContext) VirtualSize() string {
return units.HumanSize(float64(c.i.Size))
return units.HumanSize(float64(c.i.VirtualSize))
}
func (c *imageContext) SharedSize() string {
@ -277,8 +269,8 @@ func (c *imageContext) SharedSize() string {
}
func (c *imageContext) UniqueSize() string {
if c.i.Size == -1 || c.i.SharedSize == -1 {
if c.i.VirtualSize == -1 || c.i.SharedSize == -1 {
return "N/A"
}
return units.HumanSize(float64(c.i.Size - c.i.SharedSize))
return units.HumanSize(float64(c.i.VirtualSize - c.i.SharedSize))
}

View File

@ -36,7 +36,7 @@ func TestImageContext(t *testing.T) {
call: ctx.ID,
},
{
imageCtx: imageContext{i: types.ImageSummary{Size: 10}, trunc: true},
imageCtx: imageContext{i: types.ImageSummary{Size: 10, VirtualSize: 10}, trunc: true},
expValue: "10B",
call: ctx.Size,
},
@ -70,9 +70,9 @@ func TestImageContext(t *testing.T) {
call: ctx.Containers,
},
{
imageCtx: imageContext{i: types.ImageSummary{Size: 10000}},
imageCtx: imageContext{i: types.ImageSummary{VirtualSize: 10000}},
expValue: "10kB",
call: ctx.VirtualSize, //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
call: ctx.VirtualSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 10000}},
@ -80,7 +80,7 @@ func TestImageContext(t *testing.T) {
call: ctx.SharedSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 5000, Size: 20000}},
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 5000, VirtualSize: 20000}},
expValue: "15kB",
call: ctx.UniqueSize,
},

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package formatter
import (

View File

@ -14,14 +14,14 @@ type fakeClient struct {
serviceInspectFunc func(string) (swarm.Service, []byte, error)
}
func (cli *fakeClient) NodeInspectWithRaw(_ context.Context, nodeID string) (swarm.Node, []byte, error) {
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
if cli.nodeInspectFunc != nil {
return cli.nodeInspectFunc(nodeID)
}
return swarm.Node{}, []byte{}, nil
}
func (cli *fakeClient) ServiceInspectWithRaw(_ context.Context, serviceID string, _ types.ServiceInspectOptions) (swarm.Service, []byte, error) {
func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
if cli.serviceInspectFunc != nil {
return cli.serviceInspectFunc(serviceID)
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package idresolver
import (

View File

@ -22,7 +22,6 @@ import (
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
@ -128,7 +127,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", `Set the parent cgroup for the "RUN" instructions during build`)
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
flags.Var(&options.labels, "label", "Set metadata for an image")
flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
@ -280,7 +279,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
var resolvedTags []*resolvedTag
if !options.untrusted {
translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
return TrustedReference(ctx, dockerCli, ref)
return TrustedReference(ctx, dockerCli, ref, nil)
}
// if there is a tar wrapper, the dockerfile needs to be replaced inside it
if buildCtx != nil {
@ -323,9 +322,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
configFile := dockerCli.ConfigFile()
creds, _ := configFile.GetAllCredentials()
authConfigs := make(map[string]registrytypes.AuthConfig, len(creds))
authConfigs := make(map[string]types.AuthConfig, len(creds))
for k, auth := range creds {
authConfigs[k] = registrytypes.AuthConfig(auth)
authConfigs[k] = types.AuthConfig(auth)
}
buildOptions := imageBuildOptions(dockerCli, options)
buildOptions.Version = types.BuilderV1

View File

@ -18,7 +18,7 @@ import (
const dockerfileContents = "FROM busybox"
func prepareEmpty(_ *testing.T) string {
func prepareEmpty(t *testing.T) string {
return ""
}

View File

@ -1,12 +1,11 @@
package build
import (
"fmt"
"os"
"path/filepath"
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/patternmatcher"
"github.com/moby/patternmatcher/ignorefile"
)
// ReadDockerignore reads the .dockerignore file in the context directory and
@ -23,11 +22,7 @@ func ReadDockerignore(contextDir string) ([]string, error) {
}
defer f.Close()
patterns, err := ignorefile.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("error reading .dockerignore: %w", err)
}
return patterns, nil
return dockerignore.ReadAll(f)
}
// TrimBuildFilesFromExcludes removes the named Dockerfile and .dockerignore from

View File

@ -34,7 +34,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild})
dockerfile := bytes.NewBufferString(`
FROM alpine:frozen
FROM alpine:3.6
COPY foo /
`)
cli.SetIn(streams.NewIn(io.NopCloser(dockerfile)))
@ -66,7 +66,7 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) {
dir := fs.NewDir(t, "test-build-context",
fs.WithFile("foo", "some content", fs.AsUser(65534, 65534)),
fs.WithFile("Dockerfile", `
FROM alpine:frozen
FROM alpine:3.6
COPY foo bar /
`),
)
@ -155,7 +155,7 @@ func TestRunBuildFromLocalGitHubDir(t *testing.T) {
func TestRunBuildWithSymlinkedContext(t *testing.T) {
t.Setenv("DOCKER_BUILDKIT", "0")
dockerfile := `
FROM alpine:frozen
FROM alpine:3.6
RUN echo hello world
`

View File

@ -87,11 +87,11 @@ func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, quiet bool)
return types.ImageLoadResponse{}, nil
}
func (cli *fakeClient) ImageList(_ context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
func (cli *fakeClient) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
if cli.imageListFunc != nil {
return cli.imageListFunc(options)
}
return []types.ImageSummary{}, nil
return []types.ImageSummary{{}}, nil
}
func (cli *fakeClient) ImageInspectWithRaw(_ context.Context, image string) (types.ImageInspect, []byte, error) {

View File

@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
"gotest.tools/v3/skip"
)
func TestNewHistoryCommandErrors(t *testing.T) {
@ -42,7 +43,13 @@ func TestNewHistoryCommandErrors(t *testing.T) {
}
}
func notUTCTimezone() bool {
now := time.Now()
return now != now.UTC()
}
func TestNewHistoryCommandSuccess(t *testing.T) {
skip.If(t, notUTCTimezone, "expected output requires UTC timezone")
testCases := []struct {
name string
args []string
@ -55,7 +62,6 @@ func TestNewHistoryCommandSuccess(t *testing.T) {
return []image.HistoryResponseItem{{
ID: "1234567890123456789",
Created: time.Now().Unix(),
Comment: "none",
}}, nil
},
},
@ -87,19 +93,13 @@ func TestNewHistoryCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
// Set to UTC timezone as timestamps in output are
// printed in the current timezone
t.Setenv("TZ", "UTC")
cli := test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc})
cmd := NewHistoryCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NilError(t, err)
actual := cli.OutBuffer().String()
golden.Assert(t, actual, fmt.Sprintf("history-command-success.%s.golden", tc.name))
})
cli := test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc})
cmd := NewHistoryCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NilError(t, err)
actual := cli.OutBuffer().String()
golden.Assert(t, actual, fmt.Sprintf("history-command-success.%s.golden", tc.name))
}
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package image
import (

View File

@ -30,7 +30,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
name: "failed-list",
expectedError: "something went wrong",
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
return []types.ImageSummary{}, errors.Errorf("something went wrong")
return []types.ImageSummary{{}}, errors.Errorf("something went wrong")
},
},
}
@ -66,7 +66,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
args: []string{"image"},
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
assert.Check(t, is.Equal("image", options.Filters.Get("reference")[0]))
return []types.ImageSummary{}, nil
return []types.ImageSummary{{}}, nil
},
},
{
@ -74,7 +74,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
args: []string{"--filter", "name=value"},
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
assert.Check(t, is.Equal("value", options.Filters.Get("name")[0]))
return []types.ImageSummary{}, nil
return []types.ImageSummary{{}}, nil
},
},
}

View File

@ -69,7 +69,7 @@ func RunPull(cli command.Cli, opts PullOptions) error {
}
ctx := context.Background()
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), distributionRef.String())
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, AuthResolver(cli), distributionRef.String())
if err != nil {
return err
}

View File

@ -11,7 +11,6 @@ import (
"github.com/docker/cli/cli/streams"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
@ -77,7 +76,7 @@ func RunPush(dockerCli command.Cli, opts pushOptions) error {
// Resolve the Auth config relevant for this server
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}

View File

@ -1,2 +1,2 @@
IMAGE CREATED AT CREATED BY SIZE COMMENT
abcdef 2017-01-01T12:00:03Z rose 0 new history item!
IMAGE CREATED AT CREATED BY SIZE COMMENT
abcdef 2017-01-01T12:00:03Z rose 0 new history item!

View File

@ -1,2 +1,2 @@
IMAGE CREATED CREATED BY SIZE COMMENT
123456789012 Less than a second ago 0B none
IMAGE CREATED CREATED BY SIZE COMMENT
123456789012 Less than a second ago 0B

View File

@ -14,6 +14,7 @@
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""
@ -38,6 +39,7 @@
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""

View File

@ -14,6 +14,7 @@
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""

View File

@ -30,7 +30,7 @@ type target struct {
}
// TrustedPush handles content trust pushing of an image
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, options types.ImagePushOptions) error {
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, options types.ImagePushOptions) error {
responseBody, err := cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
if err != nil {
return err
@ -44,7 +44,7 @@ func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.Reposi
// PushTrustedReference pushes a canonical reference to the trust server.
//
//nolint:gocyclo
func PushTrustedReference(streams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, in io.Reader) error {
func PushTrustedReference(streams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
// If it is a trusted push we would like to find the target entry which match the
// tag provided in the function and then do an AddTarget later.
target := &client.Target{}
@ -186,7 +186,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
if err != nil {
return err
}
updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), trustedRef.String())
updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, AuthResolver(cli), trustedRef.String())
if err != nil {
return err
}
@ -262,17 +262,20 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error {
encodedAuth, err := registrytypes.EncodeAuthConfig(*imgRefAndAuth.AuthConfig())
ref := reference.FamiliarString(imgRefAndAuth.Reference())
encodedAuth, err := command.EncodeAuthToBase64(*imgRefAndAuth.AuthConfig())
if err != nil {
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull")
responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), types.ImagePullOptions{
options := types.ImagePullOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
All: opts.all,
Platform: opts.platform,
})
}
responseBody, err := cli.Client().ImagePull(ctx, ref, options)
if err != nil {
return err
}
@ -286,8 +289,8 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru
}
// TrustedReference returns the canonical trusted reference for an image reference
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), ref.String())
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) {
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, rs, AuthResolver(cli), ref.String())
if err != nil {
return nil, err
}
@ -337,8 +340,8 @@ func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canon
}
// AuthResolver returns an auth resolver function from a command.Cli
func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
return func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
return command.ResolveAuthConfig(ctx, cli, index)
}
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package inspect
import (

View File

@ -16,7 +16,7 @@ type osArch struct {
// Remove any unsupported os/arch combo
// list of valid os/arch values (see "Optional Environment Variables" section
// of https://go.dev/doc/install/source
// of https://golang.org/doc/install/source
// Added linux/s390x as we know System z support already exists
// Keep in sync with _docker_manifest_annotate in contrib/completion/bash/docker
var validOSArches = map[osArch]bool{

View File

@ -52,6 +52,6 @@ func (c *fakeClient) NetworkRemove(ctx context.Context, networkID string) error
return nil
}
func (c *fakeClient) NetworkInspectWithRaw(context.Context, string, types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
func (c *fakeClient) NetworkInspectWithRaw(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
return types.NetworkResource{}, nil, nil
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package network
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package network
import (

View File

@ -70,7 +70,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (output string, err e
// RunPrune calls the Network Prune API
// This returns the amount of space reclaimed and a detailed output string
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
func RunPrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
output, err := runPrune(dockerCli, pruneOptions{force: true, filter: filter})
return 0, output, err
}

View File

@ -20,49 +20,49 @@ type fakeClient struct {
serviceInspectFunc func(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error)
}
func (cli *fakeClient) NodeInspectWithRaw(context.Context, string) (swarm.Node, []byte, error) {
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
if cli.nodeInspectFunc != nil {
return cli.nodeInspectFunc()
}
return swarm.Node{}, []byte{}, nil
}
func (cli *fakeClient) NodeList(context.Context, types.NodeListOptions) ([]swarm.Node, error) {
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
if cli.nodeListFunc != nil {
return cli.nodeListFunc()
}
return []swarm.Node{}, nil
}
func (cli *fakeClient) NodeRemove(context.Context, string, types.NodeRemoveOptions) error {
func (cli *fakeClient) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error {
if cli.nodeRemoveFunc != nil {
return cli.nodeRemoveFunc()
}
return nil
}
func (cli *fakeClient) NodeUpdate(_ context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
func (cli *fakeClient) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
if cli.nodeUpdateFunc != nil {
return cli.nodeUpdateFunc(nodeID, version, node)
}
return nil
}
func (cli *fakeClient) Info(context.Context) (types.Info, error) {
func (cli *fakeClient) Info(ctx context.Context) (types.Info, error) {
if cli.infoFunc != nil {
return cli.infoFunc()
}
return types.Info{}, nil
}
func (cli *fakeClient) TaskInspectWithRaw(_ context.Context, taskID string) (swarm.Task, []byte, error) {
func (cli *fakeClient) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
if cli.taskInspectFunc != nil {
return cli.taskInspectFunc(taskID)
}
return swarm.Task{}, []byte{}, nil
}
func (cli *fakeClient) TaskList(_ context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
func (cli *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
if cli.taskListFunc != nil {
return cli.taskListFunc(options)
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package node
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package node
import (

View File

@ -20,42 +20,42 @@ type fakeClient struct {
pluginInspectFunc func(name string) (*types.Plugin, []byte, error)
}
func (c *fakeClient) PluginCreate(_ context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error {
func (c *fakeClient) PluginCreate(ctx context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error {
if c.pluginCreateFunc != nil {
return c.pluginCreateFunc(createContext, createOptions)
}
return nil
}
func (c *fakeClient) PluginEnable(_ context.Context, name string, enableOptions types.PluginEnableOptions) error {
func (c *fakeClient) PluginEnable(ctx context.Context, name string, enableOptions types.PluginEnableOptions) error {
if c.pluginEnableFunc != nil {
return c.pluginEnableFunc(name, enableOptions)
}
return nil
}
func (c *fakeClient) PluginDisable(_ context.Context, name string, disableOptions types.PluginDisableOptions) error {
func (c *fakeClient) PluginDisable(context context.Context, name string, disableOptions types.PluginDisableOptions) error {
if c.pluginDisableFunc != nil {
return c.pluginDisableFunc(name, disableOptions)
}
return nil
}
func (c *fakeClient) PluginRemove(_ context.Context, name string, removeOptions types.PluginRemoveOptions) error {
func (c *fakeClient) PluginRemove(context context.Context, name string, removeOptions types.PluginRemoveOptions) error {
if c.pluginRemoveFunc != nil {
return c.pluginRemoveFunc(name, removeOptions)
}
return nil
}
func (c *fakeClient) PluginInstall(_ context.Context, name string, installOptions types.PluginInstallOptions) (io.ReadCloser, error) {
func (c *fakeClient) PluginInstall(context context.Context, name string, installOptions types.PluginInstallOptions) (io.ReadCloser, error) {
if c.pluginInstallFunc != nil {
return c.pluginInstallFunc(name, installOptions)
}
return nil, nil
}
func (c *fakeClient) PluginList(_ context.Context, filter filters.Args) (types.PluginsListResponse, error) {
func (c *fakeClient) PluginList(context context.Context, filter filters.Args) (types.PluginsListResponse, error) {
if c.pluginListFunc != nil {
return c.pluginListFunc(filter)
}
@ -63,7 +63,7 @@ func (c *fakeClient) PluginList(_ context.Context, filter filters.Args) (types.P
return types.PluginsListResponse{}, nil
}
func (c *fakeClient) PluginInspectWithRaw(_ context.Context, name string) (*types.Plugin, []byte, error) {
func (c *fakeClient) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
if c.pluginInspectFunc != nil {
return c.pluginInspectFunc(name)
}
@ -71,6 +71,6 @@ func (c *fakeClient) PluginInspectWithRaw(_ context.Context, name string) (*type
return nil, nil, nil
}
func (c *fakeClient) Info(context.Context) (types.Info, error) {
func (c *fakeClient) Info(ctx context.Context) (types.Info, error) {
return types.Info{}, nil
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package plugin
import (

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package plugin
import (

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/cli/cli/command/image"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
@ -55,6 +54,26 @@ func newInstallCommand(dockerCli command.Cli) *cobra.Command {
return cmd
}
type pluginRegistryService struct {
registry.Service
}
func (s pluginRegistryService) ResolveRepository(name reference.Named) (*registry.RepositoryInfo, error) {
repoInfo, err := s.Service.ResolveRepository(name)
if repoInfo != nil {
repoInfo.Class = "plugin"
}
return repoInfo, err
}
func newRegistryService() (registry.Service, error) {
svc, err := registry.NewService(registry.ServiceOptions{})
if err != nil {
return nil, err
}
return pluginRegistryService{Service: svc}, nil
}
func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
// Names with both tag and digest will be treated by the daemon
// as a pull by digest with a local name for the tag
@ -79,7 +98,12 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String())
}
trusted, err := image.TrustedReference(context.Background(), dockerCli, nt)
ctx := context.Background()
svc, err := newRegistryService()
if err != nil {
return types.PluginInstallOptions{}, err
}
trusted, err := image.TrustedReference(ctx, dockerCli, nt, svc)
if err != nil {
return types.PluginInstallOptions{}, err
}
@ -87,7 +111,8 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
}
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return types.PluginInstallOptions{}, err
}

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image"
"github.com/docker/distribution/reference"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
@ -56,7 +55,8 @@ func runPush(dockerCli command.Cli, opts pushOptions) error {
return err
}
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
@ -68,6 +68,7 @@ func runPush(dockerCli command.Cli, opts pushOptions) error {
defer responseBody.Close()
if !opts.untrusted {
repoInfo.Class = "plugin"
return image.PushTrustedReference(dockerCli, repoInfo, named, authConfig, responseBody)
}

View File

@ -3,6 +3,8 @@ package command
import (
"bufio"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
@ -10,7 +12,6 @@ import (
"strings"
configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/hints"
"github.com/docker/cli/cli/streams"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
@ -20,15 +21,19 @@ import (
"github.com/pkg/errors"
)
const patSuggest = "You can log in with your password or a Personal Access " +
"Token (PAT). Using a limited-scope PAT grants better security and is required " +
"for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/"
// ElectAuthServer returns the default registry to use
// Deprecated: use registry.IndexServer instead
func ElectAuthServer(_ context.Context, _ Cli) string {
return registry.IndexServer
}
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload.
//
// Deprecated: use [registrytypes.EncodeAuthConfig] instead.
func EncodeAuthToBase64(authConfig registrytypes.AuthConfig) (string, error) {
return registrytypes.EncodeAuthConfig(authConfig)
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
buf, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buf), nil
}
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
@ -46,29 +51,26 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
if err != nil {
return "", err
}
return registrytypes.EncodeAuthConfig(authConfig)
return EncodeAuthToBase64(authConfig)
}
}
// ResolveAuthConfig returns auth-config for the given registry from the
// credential-store. It returns an empty AuthConfig if no credentials were
// found.
//
// It is similar to [registry.ResolveAuthConfig], but uses the credentials-
// store, instead of looking up credentials from a map.
func ResolveAuthConfig(_ context.Context, cli Cli, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
// ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
// default index, it uses the default index name for the daemon's platform,
// not the client's platform.
func ResolveAuthConfig(_ context.Context, cli Cli, index *registrytypes.IndexInfo) types.AuthConfig {
configKey := index.Name
if index.Official {
configKey = registry.IndexServer
}
a, _ := cli.ConfigFile().GetAuthConfig(configKey)
return registrytypes.AuthConfig(a)
return types.AuthConfig(a)
}
// GetDefaultAuthConfig gets the default auth config given a serverAddress
// If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) {
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
if !isDefaultRegistry {
serverAddress = registry.ConvertToHostname(serverAddress)
}
@ -77,27 +79,20 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
if checkCredStore {
authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
if err != nil {
return registrytypes.AuthConfig{
return types.AuthConfig{
ServerAddress: serverAddress,
}, err
}
}
authconfig.ServerAddress = serverAddress
authconfig.IdentityToken = ""
res := registrytypes.AuthConfig(authconfig)
res := types.AuthConfig(authconfig)
return res, nil
}
// ConfigureAuth handles prompting of user's username and password if needed
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes.AuthConfig, isDefaultRegistry bool) error {
// On Windows, force the use of the regular OS stdin stream.
//
// See:
// - https://github.com/moby/moby/issues/14336
// - https://github.com/moby/moby/issues/14210
// - https://github.com/moby/moby/pull/17738
//
// TODO(thaJeztah): we need to confirm if this special handling is still needed, as we may not be doing this in other places.
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *types.AuthConfig, isDefaultRegistry bool) error {
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
if runtime.GOOS == "windows" {
cli.SetIn(streams.NewIn(os.Stdin))
}
@ -118,18 +113,11 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
if flUser = strings.TrimSpace(flUser); flUser == "" {
if isDefaultRegistry {
// if this is a default registry (docker hub), then display the following message.
fmt.Fprintln(cli.Out(), "Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.")
if hints.Enabled() {
fmt.Fprintln(cli.Out(), patSuggest)
fmt.Fprintln(cli.Out())
}
fmt.Fprintln(cli.Out(), "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.")
}
promptWithDefault(cli.Out(), "Username", authconfig.Username)
var err error
flUser, err = readInput(cli.In())
if err != nil {
return err
}
flUser = readInput(cli.In(), cli.Out())
flUser = strings.TrimSpace(flUser)
if flUser == "" {
flUser = authconfig.Username
}
@ -143,15 +131,12 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
return err
}
fmt.Fprintf(cli.Out(), "Password: ")
_ = term.DisableEcho(cli.In().FD(), oldState)
defer func() {
_ = term.RestoreTerminal(cli.In().FD(), oldState)
}()
flPassword, err = readInput(cli.In())
if err != nil {
return err
}
term.DisableEcho(cli.In().FD(), oldState)
flPassword = readInput(cli.In(), cli.Out())
fmt.Fprint(cli.Out(), "\n")
term.RestoreTerminal(cli.In().FD(), oldState)
if flPassword == "" {
return errors.Errorf("Error: Password Required")
}
@ -163,15 +148,14 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
return nil
}
// readInput reads, and returns user input from in. It tries to return a
// single line, not including the end-of-line bytes, and trims leading
// and trailing whitespace.
func readInput(in io.Reader) (string, error) {
line, _, err := bufio.NewReader(in).ReadLine()
func readInput(in io.Reader, out io.Writer) string {
reader := bufio.NewReader(in)
line, _, err := reader.ReadLine()
if err != nil {
return "", errors.Wrap(err, "error while reading input")
fmt.Fprintln(out, err.Error())
os.Exit(1)
}
return strings.TrimSpace(string(line)), nil
return string(line)
}
func promptWithDefault(out io.Writer, prompt string, configDefault string) {
@ -182,19 +166,14 @@ func promptWithDefault(out io.Writer, prompt string, configDefault string) {
}
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete
// image. The auth configuration is serialized as a base64url encoded RFC4648,
// section 5) JSON string for sending through the X-Registry-Auth header.
//
// For details on base64url encoding, see:
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image
func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (string, error) {
// Retrieve encoded auth token from the image reference
authConfig, err := resolveAuthConfigFromImage(ctx, cli, image)
if err != nil {
return "", err
}
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
encodedAuth, err := EncodeAuthToBase64(authConfig)
if err != nil {
return "", err
}
@ -202,14 +181,14 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (str
}
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (registrytypes.AuthConfig, error) {
func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (types.AuthConfig, error) {
registryRef, err := reference.ParseNormalizedNamed(image)
if err != nil {
return registrytypes.AuthConfig{}, err
return types.AuthConfig{}, err
}
repoInfo, err := registry.ParseRepositoryInfo(registryRef)
if err != nil {
return registrytypes.AuthConfig{}, err
return types.AuthConfig{}, err
}
return ResolveAuthConfig(ctx, cli, repoInfo.Index), nil
}

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
@ -163,7 +164,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint:gocyclo
return nil
}
func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authConfig *registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authConfig *types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
fmt.Fprintf(dockerCli.Out(), "Authenticating with existing credentials...\n")
cliClient := dockerCli.Client()
response, err := cliClient.RegistryLogin(ctx, *authConfig)
@ -177,7 +178,7 @@ func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authCon
return response, err
}
func loginClientSide(ctx context.Context, auth registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
func loginClientSide(ctx context.Context, auth types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
svc, err := registry.NewService(registry.ServiceOptions{})
if err != nil {
return registrytypes.AuthenticateOKBody{}, err

View File

@ -34,11 +34,11 @@ type fakeClient struct {
client.Client
}
func (c fakeClient) Info(context.Context) (types.Info, error) {
func (c fakeClient) Info(ctx context.Context) (types.Info, error) {
return types.Info{}, nil
}
func (c fakeClient) RegistryLogin(_ context.Context, auth registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
func (c fakeClient) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
if auth.Password == expiredPassword {
return registrytypes.AuthenticateOKBody{}, fmt.Errorf("Invalid Username or Password")
}
@ -53,16 +53,16 @@ func (c fakeClient) RegistryLogin(_ context.Context, auth registrytypes.AuthConf
func TestLoginWithCredStoreCreds(t *testing.T) {
testCases := []struct {
inputAuthConfig registrytypes.AuthConfig
inputAuthConfig types.AuthConfig
expectedMsg string
expectedErr string
}{
{
inputAuthConfig: registrytypes.AuthConfig{},
inputAuthConfig: types.AuthConfig{},
expectedMsg: "Authenticating with existing credentials...\n",
},
{
inputAuthConfig: registrytypes.AuthConfig{
inputAuthConfig: types.AuthConfig{
Username: userErr,
},
expectedMsg: "Authenticating with existing credentials...\n",

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/registry"
"github.com/spf13/cobra"
)
@ -55,19 +54,25 @@ func runSearch(dockerCli command.Cli, options searchOptions) error {
}
ctx := context.Background()
authConfig := command.ResolveAuthConfig(ctx, dockerCli, indexInfo)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search")
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search")
results, err := dockerCli.Client().ImageSearch(ctx, options.term, types.ImageSearchOptions{
searchOptions := types.ImageSearchOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
Filters: options.filter.Value(),
Limit: options.limit,
})
}
clnt := dockerCli.Client()
results, err := clnt.ImageSearch(ctx, options.term, searchOptions)
if err != nil {
return err
}

View File

@ -6,14 +6,16 @@ import (
"fmt"
"testing"
. "github.com/docker/cli/cli/command" // Prevents a circular import with "github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
// Prevents a circular import with "github.com/docker/cli/internal/test"
. "github.com/docker/cli/cli/command"
configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
type fakeClient struct {
@ -21,7 +23,7 @@ type fakeClient struct {
infoFunc func() (types.Info, error)
}
var testAuthConfigs = []registry.AuthConfig{
var testAuthConfigs = []types.AuthConfig{
{
ServerAddress: "https://index.docker.io/v1/",
Username: "u0",
@ -46,13 +48,13 @@ func TestGetDefaultAuthConfig(t *testing.T) {
checkCredStore bool
inputServerAddress string
expectedErr string
expectedAuthConfig registry.AuthConfig
expectedAuthConfig types.AuthConfig
}{
{
checkCredStore: false,
inputServerAddress: "",
expectedErr: "",
expectedAuthConfig: registry.AuthConfig{
expectedAuthConfig: types.AuthConfig{
ServerAddress: "",
Username: "",
Password: "",
@ -102,7 +104,7 @@ func TestGetDefaultAuthConfig_HelperError(t *testing.T) {
cli.SetErr(errBuf)
cli.ConfigFile().CredentialsStore = "fake-does-not-exist"
serverAddress := "test-server-address"
expectedAuthConfig := registry.AuthConfig{
expectedAuthConfig := types.AuthConfig{
ServerAddress: serverAddress,
}
authconfig, err := GetDefaultAuthConfig(cli, true, serverAddress, serverAddress == "https://index.docker.io/v1/")

View File

@ -10,36 +10,36 @@ import (
type fakeClient struct {
client.Client
secretCreateFunc func(context.Context, swarm.SecretSpec) (types.SecretCreateResponse, error)
secretInspectFunc func(context.Context, string) (swarm.Secret, []byte, error)
secretListFunc func(context.Context, types.SecretListOptions) ([]swarm.Secret, error)
secretRemoveFunc func(context.Context, string) error
secretCreateFunc func(swarm.SecretSpec) (types.SecretCreateResponse, error)
secretInspectFunc func(string) (swarm.Secret, []byte, error)
secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error)
secretRemoveFunc func(string) error
}
func (c *fakeClient) SecretCreate(ctx context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if c.secretCreateFunc != nil {
return c.secretCreateFunc(ctx, spec)
return c.secretCreateFunc(spec)
}
return types.SecretCreateResponse{}, nil
}
func (c *fakeClient) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
if c.secretInspectFunc != nil {
return c.secretInspectFunc(ctx, id)
return c.secretInspectFunc(id)
}
return swarm.Secret{}, nil, nil
}
func (c *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
if c.secretListFunc != nil {
return c.secretListFunc(ctx, options)
return c.secretListFunc(options)
}
return []swarm.Secret{}, nil
}
func (c *fakeClient) SecretRemove(ctx context.Context, name string) error {
if c.secretRemoveFunc != nil {
return c.secretRemoveFunc(ctx, name)
return c.secretRemoveFunc(name)
}
return nil
}

View File

@ -1,7 +1,6 @@
package secret
import (
"context"
"io"
"os"
"path/filepath"
@ -22,7 +21,7 @@ const secretDataFile = "secret-create-with-name.golden"
func TestSecretCreateErrors(t *testing.T) {
testCases := []struct {
args []string
secretCreateFunc func(context.Context, swarm.SecretSpec) (types.SecretCreateResponse, error)
secretCreateFunc func(swarm.SecretSpec) (types.SecretCreateResponse, error)
expectedError string
}{
{
@ -35,7 +34,7 @@ func TestSecretCreateErrors(t *testing.T) {
},
{
args: []string{"name", filepath.Join("testdata", secretDataFile)},
secretCreateFunc: func(_ context.Context, secretSpec swarm.SecretSpec) (types.SecretCreateResponse, error) {
secretCreateFunc: func(secretSpec swarm.SecretSpec) (types.SecretCreateResponse, error) {
return types.SecretCreateResponse{}, errors.Errorf("error creating secret")
},
expectedError: "error creating secret",
@ -67,7 +66,7 @@ func TestSecretCreateWithName(t *testing.T) {
}
cli := test.NewFakeCli(&fakeClient{
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if !reflect.DeepEqual(spec, expected) {
return types.SecretCreateResponse{}, errors.Errorf("expected %+v, got %+v", expected, spec)
}
@ -90,7 +89,7 @@ func TestSecretCreateWithDriver(t *testing.T) {
name := "foo"
cli := test.NewFakeCli(&fakeClient{
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if spec.Name != name {
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}
@ -119,7 +118,7 @@ func TestSecretCreateWithTemplatingDriver(t *testing.T) {
name := "foo"
cli := test.NewFakeCli(&fakeClient{
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if spec.Name != name {
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}
@ -149,7 +148,7 @@ func TestSecretCreateWithLabels(t *testing.T) {
name := "foo"
cli := test.NewFakeCli(&fakeClient{
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if spec.Name != name {
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}

View File

@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.19
package secret
import (

View File

@ -1,7 +1,6 @@
package secret
import (
"context"
"fmt"
"io"
"testing"
@ -19,7 +18,7 @@ func TestSecretInspectErrors(t *testing.T) {
testCases := []struct {
args []string
flags map[string]string
secretInspectFunc func(ctx context.Context, secretID string) (swarm.Secret, []byte, error)
secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
expectedError string
}{
{
@ -27,7 +26,7 @@ func TestSecretInspectErrors(t *testing.T) {
},
{
args: []string{"foo"},
secretInspectFunc: func(_ context.Context, secretID string) (swarm.Secret, []byte, error) {
secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
return swarm.Secret{}, nil, errors.Errorf("error while inspecting the secret")
},
expectedError: "error while inspecting the secret",
@ -41,7 +40,7 @@ func TestSecretInspectErrors(t *testing.T) {
},
{
args: []string{"foo", "bar"},
secretInspectFunc: func(_ context.Context, secretID string) (swarm.Secret, []byte, error) {
secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
if secretID == "foo" {
return *Secret(SecretName("foo")), nil, nil
}
@ -69,12 +68,12 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
testCases := []struct {
name string
args []string
secretInspectFunc func(ctx context.Context, secretID string) (swarm.Secret, []byte, error)
secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
}{
{
name: "single-secret",
args: []string{"foo"},
secretInspectFunc: func(_ context.Context, name string) (swarm.Secret, []byte, error) {
secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
if name != "foo" {
return swarm.Secret{}, nil, errors.Errorf("Invalid name, expected %s, got %s", "foo", name)
}
@ -84,7 +83,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
{
name: "multiple-secrets-with-labels",
args: []string{"foo", "bar"},
secretInspectFunc: func(_ context.Context, name string) (swarm.Secret, []byte, error) {
secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
return *Secret(SecretID("ID-"+name), SecretName(name), SecretLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
@ -103,7 +102,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
}
func TestSecretInspectWithFormat(t *testing.T) {
secretInspectFunc := func(_ context.Context, name string) (swarm.Secret, []byte, error) {
secretInspectFunc := func(name string) (swarm.Secret, []byte, error) {
return *Secret(SecretName("foo"), SecretLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
@ -112,7 +111,7 @@ func TestSecretInspectWithFormat(t *testing.T) {
name string
format string
args []string
secretInspectFunc func(_ context.Context, name string) (swarm.Secret, []byte, error)
secretInspectFunc func(name string) (swarm.Secret, []byte, error)
}{
{
name: "simple-template",
@ -142,11 +141,11 @@ func TestSecretInspectWithFormat(t *testing.T) {
func TestSecretInspectPretty(t *testing.T) {
testCases := []struct {
name string
secretInspectFunc func(context.Context, string) (swarm.Secret, []byte, error)
secretInspectFunc func(string) (swarm.Secret, []byte, error)
}{
{
name: "simple",
secretInspectFunc: func(_ context.Context, id string) (swarm.Secret, []byte, error) {
secretInspectFunc: func(id string) (swarm.Secret, []byte, error) {
return *Secret(
SecretLabels(map[string]string{
"lbl1": "value1",

View File

@ -1,7 +1,6 @@
package secret
import (
"context"
"io"
"testing"
"time"
@ -20,7 +19,7 @@ import (
func TestSecretListErrors(t *testing.T) {
testCases := []struct {
args []string
secretListFunc func(context.Context, types.SecretListOptions) ([]swarm.Secret, error)
secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error)
expectedError string
}{
{
@ -28,7 +27,7 @@ func TestSecretListErrors(t *testing.T) {
expectedError: "accepts no argument",
},
{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
return []swarm.Secret{}, errors.Errorf("error listing secrets")
},
expectedError: "error listing secrets",
@ -48,7 +47,7 @@ func TestSecretListErrors(t *testing.T) {
func TestSecretList(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
return []swarm.Secret{
*Secret(SecretID("ID-1-foo"),
SecretName("1-foo"),
@ -80,7 +79,7 @@ func TestSecretList(t *testing.T) {
func TestSecretListWithQuietOption(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
return []swarm.Secret{
*Secret(SecretID("ID-foo"), SecretName("foo")),
*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
@ -97,7 +96,7 @@ func TestSecretListWithQuietOption(t *testing.T) {
func TestSecretListWithConfigFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
return []swarm.Secret{
*Secret(SecretID("ID-foo"), SecretName("foo")),
*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
@ -116,7 +115,7 @@ func TestSecretListWithConfigFormat(t *testing.T) {
func TestSecretListWithFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
return []swarm.Secret{
*Secret(SecretID("ID-foo"), SecretName("foo")),
*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
@ -133,7 +132,7 @@ func TestSecretListWithFormat(t *testing.T) {
func TestSecretListWithFilter(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
assert.Check(t, is.Equal("foo", options.Filters.Get("name")[0]), "foo")
assert.Check(t, is.Equal("lbl1=Label-bar", options.Filters.Get("label")[0]))
return []swarm.Secret{

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