Compare commits
181 Commits
v23.0.0-rc
...
v24.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
| 33961a79f1 | |||
| dcb6a0d182 | |||
| 8e6ac62d53 | |||
| e14f5fc1a7 | |||
| 05cffcbedf | |||
| a36a54d3ca | |||
| f4b22fb6cf | |||
| eb392ff4ce | |||
| de0d30ff24 | |||
| 92fb47f58a | |||
| 7189716d5a | |||
| bfa79fd75a | |||
| e86d2f4113 | |||
| cd9c6a4c02 | |||
| 5843fbd5f5 | |||
| 149d289638 | |||
| 6dca335d05 | |||
| 4389c966f8 | |||
| a798282877 | |||
| adbfa8208a | |||
| dfc98b2dec | |||
| b8747b0f91 | |||
| 399ded9b98 | |||
| 20a70cb530 | |||
| 90380d9576 | |||
| dd6ede2109 | |||
| 7c8680c69b | |||
| 6355bcee66 | |||
| 607f290f65 | |||
| 546cf6d985 | |||
| b32b28041d | |||
| 40a51d5543 | |||
| b0d0b0efcb | |||
| c69640d8c1 | |||
| 5254081fd6 | |||
| da3416c023 | |||
| 625988c3aa | |||
| 92d9e3bf69 | |||
| 316c4992c4 | |||
| ae5a86bb8d | |||
| 38ef40ee7a | |||
| 45b5676acd | |||
| 5563c5a91d | |||
| 9dd012aa5d | |||
| 66c66bdce7 | |||
| ac024a4d8b | |||
| a2d532819d | |||
| f5fad186c0 | |||
| 9252fae838 | |||
| 92506afd49 | |||
| c3d7f167bd | |||
| b4aff3a14d | |||
| f08252c10a | |||
| 78c474539b | |||
| be97731f1a | |||
| a9fff59809 | |||
| 378e35e915 | |||
| b9a1b0928a | |||
| 90b7bc36d4 | |||
| efd011b793 | |||
| e4436853e8 | |||
| fc6703bd3d | |||
| 9335690a66 | |||
| ccae6e9299 | |||
| f27927d934 | |||
| 88924b1802 | |||
| a39958846d | |||
| 69c54641f7 | |||
| c39c711a18 | |||
| 62f2358b99 | |||
| bebdb6fa2a | |||
| 21653863b1 | |||
| bec5d37e91 | |||
| 7f11449f35 | |||
| b201ce5efd | |||
| c6c33380da | |||
| d234a81de7 | |||
| 59ec357996 | |||
| 3ce95c7af0 | |||
| b854eff300 | |||
| f5d698a331 | |||
| 0ba820ed0b | |||
| 14482589df | |||
| a3d56e7d06 | |||
| bfe87fd39b | |||
| 742881fc58 | |||
| e3fa7280ad | |||
| 235b501b71 | |||
| ac38a77ff9 | |||
| 3c9e0073dd | |||
| db827d583b | |||
| 166de0ec97 | |||
| aa0aa4a6dc | |||
| 8805f8ea2d | |||
| e60c748c14 | |||
| 6c8cc226f0 | |||
| d213548bd0 | |||
| 3a0d492d1c | |||
| f40bbf4f7f | |||
| a85537d346 | |||
| 3e9c6e84ce | |||
| ca8783ef43 | |||
| 33806760a4 | |||
| 71e495aa54 | |||
| 12c6126a67 | |||
| 078b99feb4 | |||
| 74c4ed4171 | |||
| 23da1cec6c | |||
| 677aac9011 | |||
| 012b77952e | |||
| a47058bbd5 | |||
| c549fd7360 | |||
| 9733334487 | |||
| cb5463a728 | |||
| 8bc1aaceae | |||
| 179bc7a638 | |||
| 934dd5b5ce | |||
| 881c353576 | |||
| 89687d5b3f | |||
| b244ad61cc | |||
| a1953e19b2 | |||
| 0b05d28815 | |||
| f5ac664f8a | |||
| e636747a14 | |||
| dac79b19a7 | |||
| c2487c2997 | |||
| e921e103a4 | |||
| dfb36eaef8 | |||
| d2f726d5ad | |||
| c173316515 | |||
| 0f39598687 | |||
| 24b4924410 | |||
| 4254cd19b9 | |||
| f42e1ad1a7 | |||
| 6872164e45 | |||
| 7abb189120 | |||
| a04dee2638 | |||
| bbebebaedf | |||
| 5195db1ff5 | |||
| 9b5ceb52b0 | |||
| 56051b84b0 | |||
| e92dd87c32 | |||
| 9e3d5d1528 | |||
| 3ae101f41e | |||
| 3a118309b8 | |||
| 1e3622c50c | |||
| 4a500f690f | |||
| 9b54d860cd | |||
| 0288f7f724 | |||
| 00070e6e23 | |||
| 67b9617898 | |||
| 285e137aa4 | |||
| 070825bc74 | |||
| 645395cc77 | |||
| 551c4e9ab9 | |||
| 5f9c58ffa0 | |||
| 8672540f8c | |||
| c4fff9da13 | |||
| 526e5e7c95 | |||
| d7f21ea9c8 | |||
| ae43eb0e04 | |||
| caf8b152c6 | |||
| e1152b2418 | |||
| be30cb370e | |||
| f7c322edba | |||
| 5d04b1c49e | |||
| 8627a6df16 | |||
| fe694e8219 | |||
| 1493806509 | |||
| 9bb70217f8 | |||
| 59e74b44ae | |||
| fc6be6ad30 | |||
| d347678cde | |||
| d0a4b6f497 | |||
| e04f3dd0df | |||
| 1af9f22c7e | |||
| 2753057c40 | |||
| f1f12a3332 | |||
| c453cc6873 | |||
| 0d16330dd2 | |||
| 35b42efad3 |
73
.github/workflows/build.yml
vendored
73
.github/workflows/build.yml
vendored
@ -9,20 +9,41 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
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:
|
||||
- cross
|
||||
- dynbinary-cross
|
||||
- binary
|
||||
- dynbinary
|
||||
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
|
||||
use_glibc:
|
||||
- ""
|
||||
- glibc
|
||||
@ -36,22 +57,22 @@ jobs:
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Run ${{ matrix.target }}
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
set: |
|
||||
*.platform=${{ matrix.platform }}
|
||||
env:
|
||||
USE_GLIBC: ${{ matrix.use_glibc }}
|
||||
-
|
||||
name: Flatten artifacts
|
||||
name: Create tarball
|
||||
working-directory: ./build
|
||||
run: |
|
||||
for dir in */; do
|
||||
base=$(basename "$dir")
|
||||
echo "Creating ${base}.tar.gz ..."
|
||||
tar -cvzf "${base}.tar.gz" "$dir"
|
||||
rm -rf "$dir"
|
||||
done
|
||||
mkdir /tmp/out
|
||||
platform=${{ matrix.platform }}
|
||||
platformPair=${platform//\//-}
|
||||
tar -cvzf "/tmp/out/docker-${platformPair}.tar.gz" .
|
||||
if [ -z "${{ matrix.use_glibc }}" ]; then
|
||||
echo "ARTIFACT_NAME=${{ matrix.target }}" >> $GITHUB_ENV
|
||||
else
|
||||
@ -62,11 +83,35 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ./build/*
|
||||
path: /tmp/out/*
|
||||
if-no-files-found: error
|
||||
|
||||
prepare-plugins:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
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
|
||||
@ -75,7 +120,9 @@ jobs:
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Build plugins
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
with:
|
||||
targets: plugins-cross
|
||||
set: |
|
||||
*.platform=${{ matrix.platform }}
|
||||
|
||||
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
@ -61,9 +61,9 @@ jobs:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19.4
|
||||
go-version: 1.20.2
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
2
.github/workflows/validate.yml
vendored
2
.github/workflows/validate.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@ -1,12 +1,12 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_VARIANT=alpine
|
||||
ARG GO_VERSION=1.19.5
|
||||
ARG GO_VERSION=1.20.2
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG XX_VERSION=1.1.1
|
||||
ARG GOVERSIONINFO_VERSION=v1.3.0
|
||||
ARG GOTESTSUM_VERSION=v1.8.2
|
||||
ARG BUILDX_VERSION=0.9.1
|
||||
ARG BUILDX_VERSION=0.10.4
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
@ -125,8 +125,8 @@ CMD ./scripts/test/e2e/entry
|
||||
FROM build-base-${BASE_VARIANT} AS dev
|
||||
COPY . .
|
||||
|
||||
FROM scratch AS binary
|
||||
COPY --from=build /out .
|
||||
|
||||
FROM scratch AS plugins
|
||||
COPY --from=build-plugins /out .
|
||||
|
||||
FROM scratch AS binary
|
||||
COPY --from=build /out .
|
||||
|
||||
@ -49,9 +49,9 @@
|
||||
|
||||
people = [
|
||||
"bsousaa",
|
||||
"neersighted",
|
||||
"programmerq",
|
||||
"sam-thibault",
|
||||
"thajeztah",
|
||||
"vvoland"
|
||||
]
|
||||
|
||||
@ -103,6 +103,11 @@
|
||||
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"
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
# Docker CLI
|
||||
|
||||
[](https://pkg.go.dev/github.com/docker/cli)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Atest)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Atest)
|
||||
[](https://goreportcard.com/report/github.com/docker/cli)
|
||||
[](https://codecov.io/gh/docker/cli)
|
||||
[](https://codecov.io/gh/docker/cli)
|
||||
|
||||
## About
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ package manager
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
@ -31,64 +32,69 @@ 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) error {
|
||||
plugins, err := ListPlugins(dockerCli, rootCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range plugins {
|
||||
p := p
|
||||
vendor := p.Vendor
|
||||
if vendor == "" {
|
||||
vendor = "unknown"
|
||||
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
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
err = runCommand.Run()
|
||||
if err == nil {
|
||||
os.Exit(0) // plugin already rendered complete data
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@ -133,7 +133,9 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
||||
}
|
||||
opts, flags := cli.SetupPluginRootCommand(cmd)
|
||||
|
||||
cmd.SetIn(dockerCli.In())
|
||||
cmd.SetOut(dockerCli.Out())
|
||||
cmd.SetErr(dockerCli.Err())
|
||||
|
||||
cmd.AddCommand(
|
||||
plugin,
|
||||
|
||||
19
cli/cobra.go
19
cli/cobra.go
@ -204,6 +204,16 @@ 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",
|
||||
@ -234,9 +244,13 @@ func isExperimental(cmd *cobra.Command) bool {
|
||||
}
|
||||
|
||||
func additionalHelp(cmd *cobra.Command) string {
|
||||
if additionalHelp, ok := cmd.Annotations["additionalHelp"]; ok {
|
||||
if msg, ok := cmd.Annotations["additionalHelp"]; ok {
|
||||
out := cmd.OutOrStderr()
|
||||
if _, isTerminal := term.GetFdInfo(out); !isTerminal {
|
||||
return msg
|
||||
}
|
||||
style := aec.EmptyBuilder.Bold().ANSI
|
||||
return style.Apply(additionalHelp)
|
||||
return style.Apply(msg)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -507,6 +521,7 @@ Run '{{.CommandPath}} COMMAND --help' for more information on a command.
|
||||
{{- if hasAdditionalHelp .}}
|
||||
|
||||
{{ additionalHelp . }}
|
||||
|
||||
{{- end}}
|
||||
`
|
||||
|
||||
|
||||
@ -14,21 +14,21 @@ type fakeClient struct {
|
||||
checkpointListFunc func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||
if cli.checkpointCreateFunc != nil {
|
||||
return cli.checkpointCreateFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error {
|
||||
func (cli *fakeClient) CheckpointDelete(_ context.Context, container string, options types.CheckpointDeleteOptions) error {
|
||||
if cli.checkpointDeleteFunc != nil {
|
||||
return cli.checkpointDeleteFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||
func (cli *fakeClient) CheckpointList(_ context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||
if cli.checkpointListFunc != nil {
|
||||
return cli.checkpointListFunc(container, options)
|
||||
}
|
||||
|
||||
@ -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) types.AuthConfig {
|
||||
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
||||
return ResolveAuthConfig(ctx, cli, index)
|
||||
}
|
||||
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
|
||||
|
||||
@ -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/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"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(), filters.Args{})
|
||||
list, err := dockerCli.Client().VolumeList(cmd.Context(), volume.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
var names []string
|
||||
for _, volume := range list.Volumes {
|
||||
names = append(names, volume.Name)
|
||||
for _, vol := range list.Volumes {
|
||||
names = append(names, vol.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(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func NoComplete(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
@ -10,34 +10,34 @@ import (
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
|
||||
configInspectFunc func(string) (swarm.Config, []byte, error)
|
||||
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
|
||||
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)
|
||||
configRemoveFunc func(string) error
|
||||
}
|
||||
|
||||
func (c *fakeClient) ConfigCreate(ctx context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
if c.configCreateFunc != nil {
|
||||
return c.configCreateFunc(spec)
|
||||
return c.configCreateFunc(ctx, 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(id)
|
||||
return c.configInspectFunc(ctx, 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(options)
|
||||
return c.configListFunc(ctx, options)
|
||||
}
|
||||
return []swarm.Config{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) ConfigRemove(ctx context.Context, name string) error {
|
||||
func (c *fakeClient) ConfigRemove(_ context.Context, name string) error {
|
||||
if c.configRemoveFunc != nil {
|
||||
return c.configRemoveFunc(name)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -22,7 +23,7 @@ const configDataFile = "config-create-with-name.golden"
|
||||
func TestConfigCreateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
|
||||
configCreateFunc func(context.Context, swarm.ConfigSpec) (types.ConfigCreateResponse, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -35,7 +36,7 @@ func TestConfigCreateErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"name", filepath.Join("testdata", configDataFile)},
|
||||
configCreateFunc: func(configSpec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
configCreateFunc: func(_ context.Context, configSpec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
return types.ConfigCreateResponse{}, errors.Errorf("error creating config")
|
||||
},
|
||||
expectedError: "error creating config",
|
||||
@ -57,7 +58,7 @@ func TestConfigCreateWithName(t *testing.T) {
|
||||
name := "foo"
|
||||
var actual []byte
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
}
|
||||
@ -96,7 +97,7 @@ func TestConfigCreateWithLabels(t *testing.T) {
|
||||
}
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
if !reflect.DeepEqual(spec, expected) {
|
||||
return types.ConfigCreateResponse{}, errors.Errorf("expected %+v, got %+v", expected, spec)
|
||||
}
|
||||
@ -122,7 +123,7 @@ func TestConfigCreateWithTemplatingDriver(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
@ -18,7 +19,7 @@ func TestConfigInspectErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
configInspectFunc func(configID string) (swarm.Config, []byte, error)
|
||||
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -26,7 +27,7 @@ func TestConfigInspectErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo"},
|
||||
configInspectFunc: func(configID string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
|
||||
return swarm.Config{}, nil, errors.Errorf("error while inspecting the config")
|
||||
},
|
||||
expectedError: "error while inspecting the config",
|
||||
@ -40,7 +41,7 @@ func TestConfigInspectErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
configInspectFunc: func(configID string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
|
||||
if configID == "foo" {
|
||||
return *Config(ConfigName("foo")), nil, nil
|
||||
}
|
||||
@ -68,12 +69,12 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
configInspectFunc func(configID string) (swarm.Config, []byte, error)
|
||||
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "single-config",
|
||||
args: []string{"foo"},
|
||||
configInspectFunc: func(name string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
|
||||
if name != "foo" {
|
||||
return swarm.Config{}, nil, errors.Errorf("Invalid name, expected %s, got %s", "foo", name)
|
||||
}
|
||||
@ -83,7 +84,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
|
||||
{
|
||||
name: "multiple-configs-with-labels",
|
||||
args: []string{"foo", "bar"},
|
||||
configInspectFunc: func(name string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
|
||||
return *Config(ConfigID("ID-"+name), ConfigName(name), ConfigLabels(map[string]string{
|
||||
"label1": "label-foo",
|
||||
})), nil, nil
|
||||
@ -100,7 +101,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigInspectWithFormat(t *testing.T) {
|
||||
configInspectFunc := func(name string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc := func(_ context.Context, name string) (swarm.Config, []byte, error) {
|
||||
return *Config(ConfigName("foo"), ConfigLabels(map[string]string{
|
||||
"label1": "label-foo",
|
||||
})), nil, nil
|
||||
@ -109,7 +110,7 @@ func TestConfigInspectWithFormat(t *testing.T) {
|
||||
name string
|
||||
format string
|
||||
args []string
|
||||
configInspectFunc func(name string) (swarm.Config, []byte, error)
|
||||
configInspectFunc func(_ context.Context, name string) (swarm.Config, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple-template",
|
||||
@ -139,11 +140,11 @@ func TestConfigInspectWithFormat(t *testing.T) {
|
||||
func TestConfigInspectPretty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
configInspectFunc func(string) (swarm.Config, []byte, error)
|
||||
configInspectFunc func(context.Context, string) (swarm.Config, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
configInspectFunc: func(id string) (swarm.Config, []byte, error) {
|
||||
configInspectFunc: func(_ context.Context, id string) (swarm.Config, []byte, error) {
|
||||
return *Config(
|
||||
ConfigLabels(map[string]string{
|
||||
"lbl1": "value1",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
@ -19,7 +20,7 @@ import (
|
||||
func TestConfigListErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
|
||||
configListFunc func(context.Context, types.ConfigListOptions) ([]swarm.Config, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -27,7 +28,7 @@ func TestConfigListErrors(t *testing.T) {
|
||||
expectedError: "accepts no argument",
|
||||
},
|
||||
{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{}, errors.Errorf("error listing configs")
|
||||
},
|
||||
expectedError: "error listing configs",
|
||||
@ -47,7 +48,7 @@ func TestConfigListErrors(t *testing.T) {
|
||||
|
||||
func TestConfigList(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{
|
||||
*Config(ConfigID("ID-1-foo"),
|
||||
ConfigName("1-foo"),
|
||||
@ -77,7 +78,7 @@ func TestConfigList(t *testing.T) {
|
||||
|
||||
func TestConfigListWithQuietOption(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, 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{
|
||||
@ -94,7 +95,7 @@ func TestConfigListWithQuietOption(t *testing.T) {
|
||||
|
||||
func TestConfigListWithConfigFormat(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, 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{
|
||||
@ -113,7 +114,7 @@ func TestConfigListWithConfigFormat(t *testing.T) {
|
||||
|
||||
func TestConfigListWithFormat(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, 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{
|
||||
@ -130,7 +131,7 @@ func TestConfigListWithFormat(t *testing.T) {
|
||||
|
||||
func TestConfigListWithFilter(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
configListFunc: func(_ context.Context, 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{
|
||||
|
||||
@ -64,7 +64,7 @@ func (f *fakeClient) ContainerExecInspect(_ context.Context, execID string) (typ
|
||||
return types.ContainerExecInspect{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
|
||||
func (f *fakeClient) ContainerExecStart(context.Context, string, 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(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
|
||||
func (f *fakeClient) ImageCreate(_ context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
|
||||
if f.imageCreateFunc != nil {
|
||||
return f.imageCreateFunc(parentReference, options)
|
||||
}
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
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"
|
||||
@ -48,26 +53,73 @@ type cpConfig struct {
|
||||
// copying files to/from a container.
|
||||
type copyProgressPrinter struct {
|
||||
io.ReadCloser
|
||||
toContainer bool
|
||||
total *float64
|
||||
writer io.Writer
|
||||
total *int64
|
||||
}
|
||||
|
||||
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)
|
||||
*pt.total += float64(n)
|
||||
atomic.AddInt64(pt.total, int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
return n, err
|
||||
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
|
||||
}
|
||||
}
|
||||
}()
|
||||
return restore, done
|
||||
}
|
||||
|
||||
// NewCopyCommand creates a new `docker cp` command
|
||||
@ -113,6 +165,10 @@ 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)
|
||||
@ -153,7 +209,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
if absPath, err = filepath.Abs(localPath); err != nil {
|
||||
return
|
||||
}
|
||||
return archive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil
|
||||
return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
|
||||
}
|
||||
|
||||
func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpConfig) (err error) {
|
||||
@ -193,6 +249,9 @@ 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
|
||||
@ -211,13 +270,11 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
|
||||
RebaseName: rebaseName,
|
||||
}
|
||||
|
||||
var copiedSize float64
|
||||
var copiedSize int64
|
||||
if !copyConfig.quiet {
|
||||
content = ©ProgressPrinter{
|
||||
ReadCloser: content,
|
||||
toContainer: false,
|
||||
writer: dockerCli.Err(),
|
||||
total: &copiedSize,
|
||||
ReadCloser: content,
|
||||
total: &copiedSize,
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,12 +288,12 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
|
||||
return archive.CopyTo(preArchive, srcInfo, dstPath)
|
||||
}
|
||||
|
||||
fmt.Fprint(dockerCli.Err(), aec.Save)
|
||||
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
|
||||
restore, done := copyProgress(ctx, dockerCli.Err(), copyFromContainerHeader, &copiedSize)
|
||||
res := archive.CopyTo(preArchive, srcInfo, 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)
|
||||
cancel()
|
||||
<-done
|
||||
restore()
|
||||
fmt.Fprintln(dockerCli.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", dstPath)
|
||||
|
||||
return res
|
||||
}
|
||||
@ -293,7 +350,7 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
|
||||
var (
|
||||
content io.ReadCloser
|
||||
resolvedDstPath string
|
||||
copiedSize float64
|
||||
copiedSize int64
|
||||
)
|
||||
|
||||
if srcPath == "-" {
|
||||
@ -337,10 +394,8 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
|
||||
content = preparedArchive
|
||||
if !copyConfig.quiet {
|
||||
content = ©ProgressPrinter{
|
||||
ReadCloser: content,
|
||||
toContainer: true,
|
||||
writer: dockerCli.Err(),
|
||||
total: &copiedSize,
|
||||
ReadCloser: content,
|
||||
total: &copiedSize,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,12 +409,13 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
|
||||
return client.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
|
||||
}
|
||||
|
||||
fmt.Fprint(dockerCli.Err(), aec.Save)
|
||||
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
|
||||
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
|
||||
restore, done := copyProgress(ctx, dockerCli.Err(), copyToContainerHeader, &copiedSize)
|
||||
res := client.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
|
||||
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)
|
||||
cancel()
|
||||
<-done
|
||||
restore()
|
||||
fmt.Fprintln(dockerCli.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@ -227,7 +227,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
|
||||
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !opts.untrusted {
|
||||
var err error
|
||||
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil)
|
||||
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -17,14 +17,15 @@ import (
|
||||
)
|
||||
|
||||
type psOptions struct {
|
||||
quiet bool
|
||||
size bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
nLatest bool
|
||||
last int
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
quiet bool
|
||||
size bool
|
||||
sizeChanged bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
nLatest bool
|
||||
last int
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
@ -36,6 +37,7 @@ 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{
|
||||
@ -78,13 +80,8 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
options.Limit = 1
|
||||
}
|
||||
|
||||
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.
|
||||
// always validate template when `--format` is used, for consistency
|
||||
if len(opts.format) > 0 {
|
||||
tmpl, err := templates.NewParse("", opts.format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse template")
|
||||
@ -98,8 +95,19 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
return nil, errors.Wrap(err, "failed to execute template")
|
||||
}
|
||||
|
||||
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
|
||||
options.Size = true
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -231,15 +231,56 @@ func TestContainerListFormatTemplateWithArg(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerListFormatSizeSetsOption(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) {
|
||||
assert.Check(t, options.Size)
|
||||
return []types.Container{}, nil
|
||||
tests := []struct {
|
||||
doc, format, sizeFlag string
|
||||
sizeExpected bool
|
||||
}{
|
||||
{
|
||||
doc: "detect with all fields",
|
||||
format: `{{json .}}`,
|
||||
sizeExpected: true,
|
||||
},
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("format", `{{.Size}}`)
|
||||
assert.NilError(t, cmd.Execute())
|
||||
{
|
||||
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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerListWithConfigFormat(t *testing.T) {
|
||||
|
||||
@ -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, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(dockerCli, pruneOptions{force: true, filter: filter})
|
||||
}
|
||||
|
||||
@ -27,15 +27,16 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Remove one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRm(dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rm, docker rm",
|
||||
"aliases": "docker container rm, docker container remove, docker rm",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
}
|
||||
|
||||
@ -173,11 +173,11 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||
}
|
||||
|
||||
close, err := attachContainer(ctx, dockerCli, &errCh, config, createResponse.ID)
|
||||
closeFn, err := attachContainer(ctx, dockerCli, &errCh, config, createResponse.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer close()
|
||||
defer closeFn()
|
||||
}
|
||||
|
||||
statusChan := waitExitOrRemoved(ctx, dockerCli, createResponse.ID, copts.autoRemove)
|
||||
@ -308,7 +308,8 @@ func runStartContainerErr(err error) error {
|
||||
strings.Contains(trimmedErr, "no such file or directory") ||
|
||||
strings.Contains(trimmedErr, "system cannot find the file specified") {
|
||||
statusError = cli.StatusError{StatusCode: 127}
|
||||
} else if strings.Contains(trimmedErr, syscall.EACCES.Error()) {
|
||||
} else if strings.Contains(trimmedErr, syscall.EACCES.Error()) ||
|
||||
strings.Contains(trimmedErr, syscall.EISDIR.Error()) {
|
||||
statusError = cli.StatusError{StatusCode: 126}
|
||||
}
|
||||
|
||||
|
||||
@ -118,10 +118,7 @@ func createNewContext(o *CreateOptions, cli command.Cli, s store.Writer) error {
|
||||
if err := s.CreateOrUpdate(contextMetadata); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ResetTLSMaterial(o.Name, &contextTLSData); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return s.ResetTLSMaterial(o.Name, &contextTLSData)
|
||||
}
|
||||
|
||||
func checkContextNameForCreation(s store.Reader, name string) error {
|
||||
|
||||
@ -27,6 +27,9 @@ 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>"
|
||||
}
|
||||
|
||||
|
||||
@ -14,14 +14,14 @@ type fakeClient struct {
|
||||
serviceInspectFunc func(string) (swarm.Service, []byte, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(_ 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(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
func (cli *fakeClient) ServiceInspectWithRaw(_ context.Context, serviceID string, _ types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
if cli.serviceInspectFunc != nil {
|
||||
return cli.serviceInspectFunc(serviceID)
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ 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"
|
||||
@ -279,7 +280,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, nil)
|
||||
return TrustedReference(ctx, dockerCli, ref)
|
||||
}
|
||||
// if there is a tar wrapper, the dockerfile needs to be replaced inside it
|
||||
if buildCtx != nil {
|
||||
@ -322,9 +323,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||
|
||||
configFile := dockerCli.ConfigFile()
|
||||
creds, _ := configFile.GetAllCredentials()
|
||||
authConfigs := make(map[string]types.AuthConfig, len(creds))
|
||||
authConfigs := make(map[string]registrytypes.AuthConfig, len(creds))
|
||||
for k, auth := range creds {
|
||||
authConfigs[k] = types.AuthConfig(auth)
|
||||
authConfigs[k] = registrytypes.AuthConfig(auth)
|
||||
}
|
||||
buildOptions := imageBuildOptions(dockerCli, options)
|
||||
buildOptions.Version = types.BuilderV1
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
|
||||
const dockerfileContents = "FROM busybox"
|
||||
|
||||
func prepareEmpty(t *testing.T) string {
|
||||
func prepareEmpty(_ *testing.T) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@ -87,11 +87,11 @@ func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, quiet bool)
|
||||
return types.ImageLoadResponse{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||
func (cli *fakeClient) ImageList(_ 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) {
|
||||
|
||||
@ -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
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ func RunPull(cli command.Cli, opts PullOptions) error {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, AuthResolver(cli), distributionRef.String())
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), distributionRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -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 types.AuthConfig, options types.ImagePushOptions) error {
|
||||
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.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 types.AuthConfig, in io.Reader) error {
|
||||
func PushTrustedReference(streams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.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, nil, AuthResolver(cli), trustedRef.String())
|
||||
updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), trustedRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -289,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, rs registry.Service) (reference.Canonical, error) {
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, rs, AuthResolver(cli), ref.String())
|
||||
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), ref.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -340,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) types.AuthConfig {
|
||||
return func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
|
||||
func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return command.ResolveAuthConfig(ctx, cli, index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
registryclient "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
@ -217,18 +218,53 @@ func buildPutManifestRequest(imageManifest types.ImageManifest, targetRef refere
|
||||
return mountRequest{}, err
|
||||
}
|
||||
|
||||
// This indentation has to be added to ensure sha parity with the registry
|
||||
v2ManifestBytes, err := json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
// Attempt to reconstruct indentation of the manifest to ensure sha parity
|
||||
// with the registry - if we haven't preserved the raw content.
|
||||
//
|
||||
// This is necessary because our previous internal storage format did not
|
||||
// preserve whitespace. If we don't have the newer format present, we can
|
||||
// attempt the reconstruction like before, but explicitly error if the
|
||||
// reconstruction failed!
|
||||
switch {
|
||||
case imageManifest.SchemaV2Manifest != nil:
|
||||
dt := imageManifest.Raw
|
||||
if len(dt) == 0 {
|
||||
dt, err = json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
}
|
||||
|
||||
dig := imageManifest.Descriptor.Digest
|
||||
if dig2 := dig.Algorithm().FromBytes(dt); dig != dig2 {
|
||||
return mountRequest{}, errors.Errorf("internal digest mismatch for %s: expected %s, got %s", imageManifest.Ref, dig, dig2)
|
||||
}
|
||||
|
||||
var manifest schema2.DeserializedManifest
|
||||
if err = manifest.UnmarshalJSON(dt); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.SchemaV2Manifest = &manifest
|
||||
case imageManifest.OCIManifest != nil:
|
||||
dt := imageManifest.Raw
|
||||
if len(dt) == 0 {
|
||||
dt, err = json.MarshalIndent(imageManifest.OCIManifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
}
|
||||
|
||||
dig := imageManifest.Descriptor.Digest
|
||||
if dig2 := dig.Algorithm().FromBytes(dt); dig != dig2 {
|
||||
return mountRequest{}, errors.Errorf("internal digest mismatch for %s: expected %s, got %s", imageManifest.Ref, dig, dig2)
|
||||
}
|
||||
|
||||
var manifest ocischema.DeserializedManifest
|
||||
if err = manifest.UnmarshalJSON(dt); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.OCIManifest = &manifest
|
||||
}
|
||||
// indent only the DeserializedManifest portion of this, in order to maintain parity with the registry
|
||||
// and not alter the sha
|
||||
var v2Manifest schema2.DeserializedManifest
|
||||
if err = v2Manifest.UnmarshalJSON(v2ManifestBytes); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.SchemaV2Manifest = &v2Manifest
|
||||
|
||||
return mountRequest{ref: mountRef, manifest: imageManifest}, err
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"variant": "v7"
|
||||
}
|
||||
},
|
||||
"Raw": "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTUyMCwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NzMyOGY2ZjhiNDE4OTA1OTc1NzVjYmFhZGM4ODRlNzM4NmFlMGFjYzUzYjc0NzQwMWViY2U1Y2YwZDYyNDU2MCIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDE5OTA0MDIsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2Ojg4Mjg2ZjQxNTMwZTkzZGZmZDRiOTY0ZTFkYjIyY2U0OTM5ZmZmYTRhNGM2NjVkYWI4NTkxZmJhYjAzZDQ5MjYiCiAgICAgIH0KICAgXQp9",
|
||||
"SchemaV2Manifest": {
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
|
||||
@ -76,6 +76,8 @@ func getManifest(ctx context.Context, dockerCli command.Cli, listRef, namedRef r
|
||||
return dockerCli.RegistryClient(insecure).GetManifest(ctx, namedRef)
|
||||
case err != nil:
|
||||
return types.ImageManifest{}, err
|
||||
case len(data.Raw) == 0:
|
||||
return dockerCli.RegistryClient(insecure).GetManifest(ctx, namedRef)
|
||||
default:
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@ -52,6 +52,6 @@ func (c *fakeClient) NetworkRemove(ctx context.Context, networkID string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) NetworkInspectWithRaw(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
|
||||
func (c *fakeClient) NetworkInspectWithRaw(context.Context, string, types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
|
||||
return types.NetworkResource{}, nil, nil
|
||||
}
|
||||
|
||||
@ -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, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
output, err := runPrune(dockerCli, pruneOptions{force: true, filter: filter})
|
||||
return 0, output, err
|
||||
}
|
||||
|
||||
@ -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(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(context.Context, string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectFunc != nil {
|
||||
return cli.nodeInspectFunc()
|
||||
}
|
||||
return swarm.Node{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
func (cli *fakeClient) NodeList(context.Context, types.NodeListOptions) ([]swarm.Node, error) {
|
||||
if cli.nodeListFunc != nil {
|
||||
return cli.nodeListFunc()
|
||||
}
|
||||
return []swarm.Node{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error {
|
||||
func (cli *fakeClient) NodeRemove(context.Context, string, types.NodeRemoveOptions) error {
|
||||
if cli.nodeRemoveFunc != nil {
|
||||
return cli.nodeRemoveFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
func (cli *fakeClient) NodeUpdate(_ 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(ctx context.Context) (types.Info, error) {
|
||||
func (cli *fakeClient) Info(context.Context) (types.Info, error) {
|
||||
if cli.infoFunc != nil {
|
||||
return cli.infoFunc()
|
||||
}
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||
func (cli *fakeClient) TaskInspectWithRaw(_ 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(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
func (cli *fakeClient) TaskList(_ context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
if cli.taskListFunc != nil {
|
||||
return cli.taskListFunc(options)
|
||||
}
|
||||
|
||||
@ -20,42 +20,42 @@ type fakeClient struct {
|
||||
pluginInspectFunc func(name string) (*types.Plugin, []byte, error)
|
||||
}
|
||||
|
||||
func (c *fakeClient) PluginCreate(ctx context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error {
|
||||
func (c *fakeClient) PluginCreate(_ context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error {
|
||||
if c.pluginCreateFunc != nil {
|
||||
return c.pluginCreateFunc(createContext, createOptions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) PluginEnable(ctx context.Context, name string, enableOptions types.PluginEnableOptions) error {
|
||||
func (c *fakeClient) PluginEnable(_ 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.Context, name string, disableOptions types.PluginDisableOptions) error {
|
||||
func (c *fakeClient) PluginDisable(_ 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.Context, name string, removeOptions types.PluginRemoveOptions) error {
|
||||
func (c *fakeClient) PluginRemove(_ 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.Context, name string, installOptions types.PluginInstallOptions) (io.ReadCloser, error) {
|
||||
func (c *fakeClient) PluginInstall(_ 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.Context, filter filters.Args) (types.PluginsListResponse, error) {
|
||||
func (c *fakeClient) PluginList(_ 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.Context, filter filters.Args) (t
|
||||
return types.PluginsListResponse{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
||||
func (c *fakeClient) PluginInspectWithRaw(_ context.Context, name string) (*types.Plugin, []byte, error) {
|
||||
if c.pluginInspectFunc != nil {
|
||||
return c.pluginInspectFunc(name)
|
||||
}
|
||||
@ -71,6 +71,6 @@ func (c *fakeClient) PluginInspectWithRaw(ctx context.Context, name string) (*ty
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
func (c *fakeClient) Info(context.Context) (types.Info, error) {
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
@ -54,26 +54,6 @@ 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
|
||||
@ -98,12 +78,7 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
|
||||
return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String())
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
svc, err := newRegistryService()
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
trusted, err := image.TrustedReference(ctx, dockerCli, nt, svc)
|
||||
trusted, err := image.TrustedReference(context.Background(), dockerCli, nt)
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
|
||||
@ -68,7 +68,6 @@ 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)
|
||||
}
|
||||
|
||||
|
||||
@ -21,14 +21,8 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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
|
||||
func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
|
||||
func EncodeAuthToBase64(authConfig registrytypes.AuthConfig) (string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -58,19 +52,19 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
|
||||
// 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 {
|
||||
func ResolveAuthConfig(_ context.Context, cli Cli, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
configKey := index.Name
|
||||
if index.Official {
|
||||
configKey = registry.IndexServer
|
||||
}
|
||||
|
||||
a, _ := cli.ConfigFile().GetAuthConfig(configKey)
|
||||
return types.AuthConfig(a)
|
||||
return registrytypes.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) (types.AuthConfig, error) {
|
||||
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) {
|
||||
if !isDefaultRegistry {
|
||||
serverAddress = registry.ConvertToHostname(serverAddress)
|
||||
}
|
||||
@ -79,19 +73,19 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
|
||||
if checkCredStore {
|
||||
authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
|
||||
if err != nil {
|
||||
return types.AuthConfig{
|
||||
return registrytypes.AuthConfig{
|
||||
ServerAddress: serverAddress,
|
||||
}, err
|
||||
}
|
||||
}
|
||||
authconfig.ServerAddress = serverAddress
|
||||
authconfig.IdentityToken = ""
|
||||
res := types.AuthConfig(authconfig)
|
||||
res := registrytypes.AuthConfig(authconfig)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ConfigureAuth handles prompting of user's username and password if needed
|
||||
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *types.AuthConfig, isDefaultRegistry bool) error {
|
||||
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes.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))
|
||||
@ -181,14 +175,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) (types.AuthConfig, error) {
|
||||
func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (registrytypes.AuthConfig, error) {
|
||||
registryRef, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
return types.AuthConfig{}, err
|
||||
return registrytypes.AuthConfig{}, err
|
||||
}
|
||||
repoInfo, err := registry.ParseRepositoryInfo(registryRef)
|
||||
if err != nil {
|
||||
return types.AuthConfig{}, err
|
||||
return registrytypes.AuthConfig{}, err
|
||||
}
|
||||
return ResolveAuthConfig(ctx, cli, repoInfo.Index), nil
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ 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"
|
||||
@ -164,7 +163,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint:gocyclo
|
||||
return nil
|
||||
}
|
||||
|
||||
func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authConfig *types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
|
||||
func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authConfig *registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
|
||||
fmt.Fprintf(dockerCli.Out(), "Authenticating with existing credentials...\n")
|
||||
cliClient := dockerCli.Client()
|
||||
response, err := cliClient.RegistryLogin(ctx, *authConfig)
|
||||
@ -178,7 +177,7 @@ func loginWithCredStoreCreds(ctx context.Context, dockerCli command.Cli, authCon
|
||||
return response, err
|
||||
}
|
||||
|
||||
func loginClientSide(ctx context.Context, auth types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
|
||||
func loginClientSide(ctx context.Context, auth registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
|
||||
svc, err := registry.NewService(registry.ServiceOptions{})
|
||||
if err != nil {
|
||||
return registrytypes.AuthenticateOKBody{}, err
|
||||
|
||||
@ -34,11 +34,11 @@ type fakeClient struct {
|
||||
client.Client
|
||||
}
|
||||
|
||||
func (c fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
func (c fakeClient) Info(context.Context) (types.Info, error) {
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (c fakeClient) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registrytypes.AuthenticateOKBody, error) {
|
||||
func (c fakeClient) RegistryLogin(_ context.Context, auth registrytypes.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(ctx context.Context, auth types.AuthConfig) (r
|
||||
|
||||
func TestLoginWithCredStoreCreds(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inputAuthConfig types.AuthConfig
|
||||
inputAuthConfig registrytypes.AuthConfig
|
||||
expectedMsg string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
inputAuthConfig: types.AuthConfig{},
|
||||
inputAuthConfig: registrytypes.AuthConfig{},
|
||||
expectedMsg: "Authenticating with existing credentials...\n",
|
||||
},
|
||||
{
|
||||
inputAuthConfig: types.AuthConfig{
|
||||
inputAuthConfig: registrytypes.AuthConfig{
|
||||
Username: userErr,
|
||||
},
|
||||
expectedMsg: "Authenticating with existing credentials...\n",
|
||||
|
||||
@ -6,16 +6,14 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
. "github.com/docker/cli/cli/command" // Prevents a circular import with "github.com/docker/cli/internal/test"
|
||||
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 {
|
||||
@ -23,7 +21,7 @@ type fakeClient struct {
|
||||
infoFunc func() (types.Info, error)
|
||||
}
|
||||
|
||||
var testAuthConfigs = []types.AuthConfig{
|
||||
var testAuthConfigs = []registry.AuthConfig{
|
||||
{
|
||||
ServerAddress: "https://index.docker.io/v1/",
|
||||
Username: "u0",
|
||||
@ -48,13 +46,13 @@ func TestGetDefaultAuthConfig(t *testing.T) {
|
||||
checkCredStore bool
|
||||
inputServerAddress string
|
||||
expectedErr string
|
||||
expectedAuthConfig types.AuthConfig
|
||||
expectedAuthConfig registry.AuthConfig
|
||||
}{
|
||||
{
|
||||
checkCredStore: false,
|
||||
inputServerAddress: "",
|
||||
expectedErr: "",
|
||||
expectedAuthConfig: types.AuthConfig{
|
||||
expectedAuthConfig: registry.AuthConfig{
|
||||
ServerAddress: "",
|
||||
Username: "",
|
||||
Password: "",
|
||||
@ -104,7 +102,7 @@ func TestGetDefaultAuthConfig_HelperError(t *testing.T) {
|
||||
cli.SetErr(errBuf)
|
||||
cli.ConfigFile().CredentialsStore = "fake-does-not-exist"
|
||||
serverAddress := "test-server-address"
|
||||
expectedAuthConfig := types.AuthConfig{
|
||||
expectedAuthConfig := registry.AuthConfig{
|
||||
ServerAddress: serverAddress,
|
||||
}
|
||||
authconfig, err := GetDefaultAuthConfig(cli, true, serverAddress, serverAddress == "https://index.docker.io/v1/")
|
||||
|
||||
@ -10,36 +10,36 @@ import (
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
func (c *fakeClient) SecretCreate(ctx context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if c.secretCreateFunc != nil {
|
||||
return c.secretCreateFunc(spec)
|
||||
return c.secretCreateFunc(ctx, 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(id)
|
||||
return c.secretInspectFunc(ctx, 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(options)
|
||||
return c.secretListFunc(ctx, options)
|
||||
}
|
||||
return []swarm.Secret{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) SecretRemove(ctx context.Context, name string) error {
|
||||
if c.secretRemoveFunc != nil {
|
||||
return c.secretRemoveFunc(name)
|
||||
return c.secretRemoveFunc(ctx, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -21,7 +22,7 @@ const secretDataFile = "secret-create-with-name.golden"
|
||||
func TestSecretCreateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
secretCreateFunc func(swarm.SecretSpec) (types.SecretCreateResponse, error)
|
||||
secretCreateFunc func(context.Context, swarm.SecretSpec) (types.SecretCreateResponse, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -34,7 +35,7 @@ func TestSecretCreateErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"name", filepath.Join("testdata", secretDataFile)},
|
||||
secretCreateFunc: func(secretSpec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
secretCreateFunc: func(_ context.Context, secretSpec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("error creating secret")
|
||||
},
|
||||
expectedError: "error creating secret",
|
||||
@ -66,7 +67,7 @@ func TestSecretCreateWithName(t *testing.T) {
|
||||
}
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if !reflect.DeepEqual(spec, expected) {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected %+v, got %+v", expected, spec)
|
||||
}
|
||||
@ -89,7 +90,7 @@ func TestSecretCreateWithDriver(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
}
|
||||
@ -118,7 +119,7 @@ func TestSecretCreateWithTemplatingDriver(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
}
|
||||
@ -148,7 +149,7 @@ func TestSecretCreateWithLabels(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
secretCreateFunc: func(_ context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
@ -18,7 +19,7 @@ func TestSecretInspectErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
|
||||
secretInspectFunc func(ctx context.Context, secretID string) (swarm.Secret, []byte, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -26,7 +27,7 @@ func TestSecretInspectErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo"},
|
||||
secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc: func(_ context.Context, secretID string) (swarm.Secret, []byte, error) {
|
||||
return swarm.Secret{}, nil, errors.Errorf("error while inspecting the secret")
|
||||
},
|
||||
expectedError: "error while inspecting the secret",
|
||||
@ -40,7 +41,7 @@ func TestSecretInspectErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc: func(_ context.Context, secretID string) (swarm.Secret, []byte, error) {
|
||||
if secretID == "foo" {
|
||||
return *Secret(SecretName("foo")), nil, nil
|
||||
}
|
||||
@ -68,12 +69,12 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
|
||||
secretInspectFunc func(ctx context.Context, secretID string) (swarm.Secret, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "single-secret",
|
||||
args: []string{"foo"},
|
||||
secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc: func(_ context.Context, name string) (swarm.Secret, []byte, error) {
|
||||
if name != "foo" {
|
||||
return swarm.Secret{}, nil, errors.Errorf("Invalid name, expected %s, got %s", "foo", name)
|
||||
}
|
||||
@ -83,7 +84,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
|
||||
{
|
||||
name: "multiple-secrets-with-labels",
|
||||
args: []string{"foo", "bar"},
|
||||
secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc: func(_ context.Context, name string) (swarm.Secret, []byte, error) {
|
||||
return *Secret(SecretID("ID-"+name), SecretName(name), SecretLabels(map[string]string{
|
||||
"label1": "label-foo",
|
||||
})), nil, nil
|
||||
@ -102,7 +103,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSecretInspectWithFormat(t *testing.T) {
|
||||
secretInspectFunc := func(name string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc := func(_ context.Context, name string) (swarm.Secret, []byte, error) {
|
||||
return *Secret(SecretName("foo"), SecretLabels(map[string]string{
|
||||
"label1": "label-foo",
|
||||
})), nil, nil
|
||||
@ -111,7 +112,7 @@ func TestSecretInspectWithFormat(t *testing.T) {
|
||||
name string
|
||||
format string
|
||||
args []string
|
||||
secretInspectFunc func(name string) (swarm.Secret, []byte, error)
|
||||
secretInspectFunc func(_ context.Context, name string) (swarm.Secret, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple-template",
|
||||
@ -141,11 +142,11 @@ func TestSecretInspectWithFormat(t *testing.T) {
|
||||
func TestSecretInspectPretty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
secretInspectFunc func(string) (swarm.Secret, []byte, error)
|
||||
secretInspectFunc func(context.Context, string) (swarm.Secret, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
secretInspectFunc: func(id string) (swarm.Secret, []byte, error) {
|
||||
secretInspectFunc: func(_ context.Context, id string) (swarm.Secret, []byte, error) {
|
||||
return *Secret(
|
||||
SecretLabels(map[string]string{
|
||||
"lbl1": "value1",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
@ -19,7 +20,7 @@ import (
|
||||
func TestSecretListErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error)
|
||||
secretListFunc func(context.Context, types.SecretListOptions) ([]swarm.Secret, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -27,7 +28,7 @@ func TestSecretListErrors(t *testing.T) {
|
||||
expectedError: "accepts no argument",
|
||||
},
|
||||
{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{}, errors.Errorf("error listing secrets")
|
||||
},
|
||||
expectedError: "error listing secrets",
|
||||
@ -47,7 +48,7 @@ func TestSecretListErrors(t *testing.T) {
|
||||
|
||||
func TestSecretList(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{
|
||||
*Secret(SecretID("ID-1-foo"),
|
||||
SecretName("1-foo"),
|
||||
@ -79,7 +80,7 @@ func TestSecretList(t *testing.T) {
|
||||
|
||||
func TestSecretListWithQuietOption(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, 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{
|
||||
@ -96,7 +97,7 @@ func TestSecretListWithQuietOption(t *testing.T) {
|
||||
|
||||
func TestSecretListWithConfigFormat(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, 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{
|
||||
@ -115,7 +116,7 @@ func TestSecretListWithConfigFormat(t *testing.T) {
|
||||
|
||||
func TestSecretListWithFormat(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, 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{
|
||||
@ -132,7 +133,7 @@ func TestSecretListWithFormat(t *testing.T) {
|
||||
|
||||
func TestSecretListWithFilter(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
secretListFunc: func(_ context.Context, 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{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -14,7 +15,7 @@ import (
|
||||
func TestSecretRemoveErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
secretRemoveFunc func(string) error
|
||||
secretRemoveFunc func(context.Context, string) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@ -23,7 +24,7 @@ func TestSecretRemoveErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo"},
|
||||
secretRemoveFunc: func(name string) error {
|
||||
secretRemoveFunc: func(_ context.Context, name string) error {
|
||||
return errors.Errorf("error removing secret")
|
||||
},
|
||||
expectedError: "error removing secret",
|
||||
@ -45,7 +46,7 @@ func TestSecretRemoveWithName(t *testing.T) {
|
||||
names := []string{"foo", "bar"}
|
||||
var removedSecrets []string
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretRemoveFunc: func(name string) error {
|
||||
secretRemoveFunc: func(_ context.Context, name string) error {
|
||||
removedSecrets = append(removedSecrets, name)
|
||||
return nil
|
||||
},
|
||||
@ -62,7 +63,7 @@ func TestSecretRemoveContinueAfterError(t *testing.T) {
|
||||
var removedSecrets []string
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
secretRemoveFunc: func(name string) error {
|
||||
secretRemoveFunc: func(_ context.Context, name string) error {
|
||||
removedSecrets = append(removedSecrets, name)
|
||||
if name == "foo" {
|
||||
return errors.Errorf("error removing secret: %s", name)
|
||||
|
||||
@ -414,7 +414,7 @@ type globalProgressUpdater struct {
|
||||
done bool
|
||||
}
|
||||
|
||||
func (u *globalProgressUpdater) update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]struct{}, rollback bool) (bool, error) {
|
||||
func (u *globalProgressUpdater) update(_ swarm.Service, tasks []swarm.Task, activeNodes map[string]struct{}, rollback bool) (bool, error) {
|
||||
tasksByNode := u.tasksByNode(tasks)
|
||||
|
||||
// We don't have perfect knowledge of how many nodes meet the
|
||||
|
||||
@ -504,23 +504,23 @@ type secretAPIClientMock struct {
|
||||
listResult []swarm.Secret
|
||||
}
|
||||
|
||||
func (s secretAPIClientMock) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
func (s secretAPIClientMock) SecretList(context.Context, types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return s.listResult, nil
|
||||
}
|
||||
|
||||
func (s secretAPIClientMock) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
func (s secretAPIClientMock) SecretCreate(context.Context, swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
return types.SecretCreateResponse{}, nil
|
||||
}
|
||||
|
||||
func (s secretAPIClientMock) SecretRemove(ctx context.Context, id string) error {
|
||||
func (s secretAPIClientMock) SecretRemove(context.Context, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s secretAPIClientMock) SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error) {
|
||||
func (s secretAPIClientMock) SecretInspectWithRaw(context.Context, string) (swarm.Secret, []byte, error) {
|
||||
return swarm.Secret{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (s secretAPIClientMock) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
||||
func (s secretAPIClientMock) SecretUpdate(context.Context, string, swarm.Version, swarm.SecretSpec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ type fakeClient struct {
|
||||
configRemoveFunc func(configID string) error
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
|
||||
func (cli *fakeClient) ServerVersion(context.Context) (types.Version, error) {
|
||||
return types.Version{
|
||||
Version: "docker-dev",
|
||||
APIVersion: api.DefaultVersion,
|
||||
@ -54,7 +54,7 @@ func (cli *fakeClient) ClientVersion() string {
|
||||
return cli.version
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
func (cli *fakeClient) ServiceList(_ context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
if cli.serviceListFunc != nil {
|
||||
return cli.serviceListFunc(options)
|
||||
}
|
||||
@ -69,7 +69,7 @@ func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceLis
|
||||
return servicesList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||
func (cli *fakeClient) NetworkList(_ context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||
if cli.networkListFunc != nil {
|
||||
return cli.networkListFunc(options)
|
||||
}
|
||||
@ -84,7 +84,7 @@ func (cli *fakeClient) NetworkList(ctx context.Context, options types.NetworkLis
|
||||
return networksList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
func (cli *fakeClient) SecretList(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
if cli.secretListFunc != nil {
|
||||
return cli.secretListFunc(options)
|
||||
}
|
||||
@ -99,7 +99,7 @@ func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListO
|
||||
return secretsList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
func (cli *fakeClient) ConfigList(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
if cli.configListFunc != nil {
|
||||
return cli.configListFunc(options)
|
||||
}
|
||||
@ -114,28 +114,28 @@ func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListO
|
||||
return configsList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
func (cli *fakeClient) TaskList(_ context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
if cli.taskListFunc != nil {
|
||||
return cli.taskListFunc(options)
|
||||
}
|
||||
return []swarm.Task{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
func (cli *fakeClient) NodeList(_ context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
if cli.nodeListFunc != nil {
|
||||
return cli.nodeListFunc(options)
|
||||
}
|
||||
return []swarm.Node{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(_ context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectWithRaw != nil {
|
||||
return cli.nodeInspectWithRaw(ref)
|
||||
}
|
||||
return swarm.Node{}, nil, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||
func (cli *fakeClient) ServiceUpdate(_ context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||
if cli.serviceUpdateFunc != nil {
|
||||
return cli.serviceUpdateFunc(serviceID, version, service, options)
|
||||
}
|
||||
@ -143,7 +143,7 @@ func (cli *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, vers
|
||||
return types.ServiceUpdateResponse{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||
func (cli *fakeClient) ServiceRemove(_ context.Context, serviceID string) error {
|
||||
if cli.serviceRemoveFunc != nil {
|
||||
return cli.serviceRemoveFunc(serviceID)
|
||||
}
|
||||
@ -152,7 +152,7 @@ func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NetworkRemove(ctx context.Context, networkID string) error {
|
||||
func (cli *fakeClient) NetworkRemove(_ context.Context, networkID string) error {
|
||||
if cli.networkRemoveFunc != nil {
|
||||
return cli.networkRemoveFunc(networkID)
|
||||
}
|
||||
@ -161,7 +161,7 @@ func (cli *fakeClient) NetworkRemove(ctx context.Context, networkID string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error {
|
||||
func (cli *fakeClient) SecretRemove(_ context.Context, secretID string) error {
|
||||
if cli.secretRemoveFunc != nil {
|
||||
return cli.secretRemoveFunc(secretID)
|
||||
}
|
||||
@ -170,7 +170,7 @@ func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ConfigRemove(ctx context.Context, configID string) error {
|
||||
func (cli *fakeClient) ConfigRemove(_ context.Context, configID string) error {
|
||||
if cli.configRemoveFunc != nil {
|
||||
return cli.configRemoveFunc(configID)
|
||||
}
|
||||
@ -179,7 +179,7 @@ func (cli *fakeClient) ConfigRemove(ctx context.Context, configID string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
func (cli *fakeClient) ServiceInspectWithRaw(_ context.Context, serviceID string, _ types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
return swarm.Service{
|
||||
ID: serviceID,
|
||||
Spec: swarm.ServiceSpec{
|
||||
|
||||
@ -28,7 +28,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return RunDeploy(dockerCli, cmd.Flags(), config, opts)
|
||||
return swarm.RunDeploy(dockerCli, opts, config)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -47,7 +47,9 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunDeploy performs a stack deploy against the specified swarm cluster
|
||||
func RunDeploy(dockerCli command.Cli, flags *pflag.FlagSet, config *composetypes.Config, opts options.Deploy) error {
|
||||
// RunDeploy performs a stack deploy against the specified swarm cluster.
|
||||
//
|
||||
// Deprecated: use [swarm.RunDeploy] instead.
|
||||
func RunDeploy(dockerCli command.Cli, _ *pflag.FlagSet, config *composetypes.Config, opts options.Deploy) error {
|
||||
return swarm.RunDeploy(dockerCli, opts, config)
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "List stacks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunList(cmd, dockerCli, opts)
|
||||
return RunList(dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
@ -34,24 +34,24 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunList performs a stack list against the specified swarm cluster
|
||||
func RunList(cmd *cobra.Command, dockerCli command.Cli, opts options.List) error {
|
||||
stacks := []*formatter.Stack{}
|
||||
func RunList(dockerCli command.Cli, opts options.List) error {
|
||||
ss, err := swarm.GetStacks(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stacks := make([]*formatter.Stack, 0, len(ss))
|
||||
stacks = append(stacks, ss...)
|
||||
return format(dockerCli, opts, stacks)
|
||||
}
|
||||
|
||||
func format(dockerCli command.Cli, opts options.List, stacks []*formatter.Stack) error {
|
||||
format := formatter.Format(opts.Format)
|
||||
if format == "" || format == formatter.TableFormatKey {
|
||||
format = formatter.SwarmStackTableFormat
|
||||
fmt := formatter.Format(opts.Format)
|
||||
if fmt == "" || fmt == formatter.TableFormatKey {
|
||||
fmt = formatter.SwarmStackTableFormat
|
||||
}
|
||||
stackCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: format,
|
||||
Format: fmt,
|
||||
}
|
||||
sort.Slice(stacks, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) ||
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@ -104,9 +105,22 @@ func GetConfigDetails(composefiles []string, stdin io.Reader) (composetypes.Conf
|
||||
func buildEnvironment(env []string) (map[string]string, error) {
|
||||
result := make(map[string]string, len(env))
|
||||
for _, s := range env {
|
||||
if runtime.GOOS == "windows" && len(s) > 0 {
|
||||
// cmd.exe can have special environment variables which names start with "=".
|
||||
// They are only there for MS-DOS compatibility and we should ignore them.
|
||||
// See TestBuildEnvironment for examples.
|
||||
//
|
||||
// https://ss64.com/nt/syntax-variables.html
|
||||
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
|
||||
// https://github.com/docker/cli/issues/4078
|
||||
if s[0] == '=' {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
k, v, ok := strings.Cut(s, "=")
|
||||
if !ok || k == "" {
|
||||
return result, errors.Errorf("unexpected environment %q", s)
|
||||
return result, errors.Errorf("unexpected environment variable '%s'", s)
|
||||
}
|
||||
// value may be set, but empty if "s" is like "K=", not "K".
|
||||
result[k] = v
|
||||
|
||||
@ -3,6 +3,7 @@ package loader
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -45,3 +46,34 @@ services:
|
||||
assert.Check(t, is.Equal("3.0", details.ConfigFiles[0].Config["version"]))
|
||||
assert.Check(t, is.Len(details.Environment, len(os.Environ())))
|
||||
}
|
||||
|
||||
func TestBuildEnvironment(t *testing.T) {
|
||||
inputEnv := []string{
|
||||
"LEGIT_VAR=LEGIT_VALUE",
|
||||
"EMPTY_VARIABLE=",
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
inputEnv = []string{
|
||||
"LEGIT_VAR=LEGIT_VALUE",
|
||||
|
||||
// cmd.exe has some special environment variables which start with "=".
|
||||
// These should be ignored as they're only there for MS-DOS compatibility.
|
||||
"=ExitCode=00000041",
|
||||
"=ExitCodeAscii=A",
|
||||
`=C:=C:\some\dir`,
|
||||
`=D:=D:\some\different\dir`,
|
||||
`=X:=X:\`,
|
||||
`=::=::\`,
|
||||
|
||||
"EMPTY_VARIABLE=",
|
||||
}
|
||||
}
|
||||
|
||||
env, err := buildEnvironment(inputEnv)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, is.Len(env, 2))
|
||||
assert.Check(t, is.Equal("LEGIT_VALUE", env["LEGIT_VAR"]))
|
||||
assert.Check(t, is.Equal("", env["EMPTY_VARIABLE"]))
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if err := validateStackName(opts.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
return RunPs(dockerCli, cmd.Flags(), opts)
|
||||
return swarm.RunPS(dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -38,7 +38,9 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunPs performs a stack ps against the specified swarm cluster
|
||||
func RunPs(dockerCli command.Cli, flags *pflag.FlagSet, opts options.PS) error {
|
||||
// RunPs performs a stack ps against the specified swarm cluster.
|
||||
//
|
||||
// Deprecated: use [swarm.RunPS] instead.
|
||||
func RunPs(dockerCli command.Cli, _ *pflag.FlagSet, opts options.PS) error {
|
||||
return swarm.RunPS(dockerCli, opts)
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if err := validateStackNames(opts.Namespaces); err != nil {
|
||||
return err
|
||||
}
|
||||
return RunRemove(dockerCli, cmd.Flags(), opts)
|
||||
return swarm.RunRemove(dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -31,7 +31,9 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunRemove performs a stack remove against the specified swarm cluster
|
||||
func RunRemove(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Remove) error {
|
||||
// RunRemove performs a stack remove against the specified swarm cluster.
|
||||
//
|
||||
// Deprecated: use [swarm.RunRemove] instead.
|
||||
func RunRemove(dockerCli command.Cli, _ *pflag.FlagSet, opts options.Remove) error {
|
||||
return swarm.RunRemove(dockerCli, opts)
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func newServicesCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if err := validateStackName(opts.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
return RunServices(dockerCli, cmd.Flags(), opts)
|
||||
return RunServices(dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -44,16 +44,18 @@ func newServicesCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunServices performs a stack services against the specified swarm cluster
|
||||
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Services) error {
|
||||
services, err := GetServices(dockerCli, flags, opts)
|
||||
func RunServices(dockerCli command.Cli, opts options.Services) error {
|
||||
services, err := swarm.GetServices(dockerCli, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return formatWrite(dockerCli, services, opts)
|
||||
}
|
||||
|
||||
// GetServices returns the services for the specified swarm cluster
|
||||
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Services) ([]swarmtypes.Service, error) {
|
||||
// GetServices returns the services for the specified swarm cluster.
|
||||
//
|
||||
// Deprecated: use [swarm.GetServices] instead.
|
||||
func GetServices(dockerCli command.Cli, _ *pflag.FlagSet, opts options.Services) ([]swarmtypes.Service, error) {
|
||||
return swarm.GetServices(dockerCli, opts)
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ type fakeClient struct {
|
||||
configRemoveFunc func(configID string) error
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
|
||||
func (cli *fakeClient) ServerVersion(context.Context) (types.Version, error) {
|
||||
return types.Version{
|
||||
Version: "docker-dev",
|
||||
APIVersion: api.DefaultVersion,
|
||||
@ -54,7 +54,7 @@ func (cli *fakeClient) ClientVersion() string {
|
||||
return cli.version
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
func (cli *fakeClient) ServiceList(_ context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
if cli.serviceListFunc != nil {
|
||||
return cli.serviceListFunc(options)
|
||||
}
|
||||
@ -69,7 +69,7 @@ func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceLis
|
||||
return servicesList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||
func (cli *fakeClient) NetworkList(_ context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||
if cli.networkListFunc != nil {
|
||||
return cli.networkListFunc(options)
|
||||
}
|
||||
@ -84,7 +84,7 @@ func (cli *fakeClient) NetworkList(ctx context.Context, options types.NetworkLis
|
||||
return networksList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
func (cli *fakeClient) SecretList(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
if cli.secretListFunc != nil {
|
||||
return cli.secretListFunc(options)
|
||||
}
|
||||
@ -99,7 +99,7 @@ func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListO
|
||||
return secretsList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
func (cli *fakeClient) ConfigList(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
if cli.configListFunc != nil {
|
||||
return cli.configListFunc(options)
|
||||
}
|
||||
@ -114,28 +114,28 @@ func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListO
|
||||
return configsList, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
func (cli *fakeClient) TaskList(_ context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
if cli.taskListFunc != nil {
|
||||
return cli.taskListFunc(options)
|
||||
}
|
||||
return []swarm.Task{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
func (cli *fakeClient) NodeList(_ context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
if cli.nodeListFunc != nil {
|
||||
return cli.nodeListFunc(options)
|
||||
}
|
||||
return []swarm.Node{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(_ context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectWithRaw != nil {
|
||||
return cli.nodeInspectWithRaw(ref)
|
||||
}
|
||||
return swarm.Node{}, nil, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||
func (cli *fakeClient) ServiceUpdate(_ context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||
if cli.serviceUpdateFunc != nil {
|
||||
return cli.serviceUpdateFunc(serviceID, version, service, options)
|
||||
}
|
||||
@ -143,7 +143,7 @@ func (cli *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, vers
|
||||
return types.ServiceUpdateResponse{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||
func (cli *fakeClient) ServiceRemove(_ context.Context, serviceID string) error {
|
||||
if cli.serviceRemoveFunc != nil {
|
||||
return cli.serviceRemoveFunc(serviceID)
|
||||
}
|
||||
@ -152,7 +152,7 @@ func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NetworkRemove(ctx context.Context, networkID string) error {
|
||||
func (cli *fakeClient) NetworkRemove(_ context.Context, networkID string) error {
|
||||
if cli.networkRemoveFunc != nil {
|
||||
return cli.networkRemoveFunc(networkID)
|
||||
}
|
||||
@ -161,7 +161,7 @@ func (cli *fakeClient) NetworkRemove(ctx context.Context, networkID string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error {
|
||||
func (cli *fakeClient) SecretRemove(_ context.Context, secretID string) error {
|
||||
if cli.secretRemoveFunc != nil {
|
||||
return cli.secretRemoveFunc(secretID)
|
||||
}
|
||||
@ -170,7 +170,7 @@ func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ConfigRemove(ctx context.Context, configID string) error {
|
||||
func (cli *fakeClient) ConfigRemove(_ context.Context, configID string) error {
|
||||
if cli.configRemoveFunc != nil {
|
||||
return cli.configRemoveFunc(configID)
|
||||
}
|
||||
|
||||
@ -21,63 +21,63 @@ type fakeClient struct {
|
||||
swarmUnlockFunc func(req swarm.UnlockRequest) error
|
||||
}
|
||||
|
||||
func (cli *fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
func (cli *fakeClient) Info(context.Context) (types.Info, error) {
|
||||
if cli.infoFunc != nil {
|
||||
return cli.infoFunc()
|
||||
}
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(context.Context, string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectFunc != nil {
|
||||
return cli.nodeInspectFunc()
|
||||
}
|
||||
return swarm.Node{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) {
|
||||
func (cli *fakeClient) SwarmInit(context.Context, swarm.InitRequest) (string, error) {
|
||||
if cli.swarmInitFunc != nil {
|
||||
return cli.swarmInitFunc()
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmInspect(ctx context.Context) (swarm.Swarm, error) {
|
||||
func (cli *fakeClient) SwarmInspect(context.Context) (swarm.Swarm, error) {
|
||||
if cli.swarmInspectFunc != nil {
|
||||
return cli.swarmInspectFunc()
|
||||
}
|
||||
return swarm.Swarm{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) {
|
||||
func (cli *fakeClient) SwarmGetUnlockKey(context.Context) (types.SwarmUnlockKeyResponse, error) {
|
||||
if cli.swarmGetUnlockKeyFunc != nil {
|
||||
return cli.swarmGetUnlockKeyFunc()
|
||||
}
|
||||
return types.SwarmUnlockKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmJoin(ctx context.Context, req swarm.JoinRequest) error {
|
||||
func (cli *fakeClient) SwarmJoin(context.Context, swarm.JoinRequest) error {
|
||||
if cli.swarmJoinFunc != nil {
|
||||
return cli.swarmJoinFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmLeave(ctx context.Context, force bool) error {
|
||||
func (cli *fakeClient) SwarmLeave(context.Context, bool) error {
|
||||
if cli.swarmLeaveFunc != nil {
|
||||
return cli.swarmLeaveFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
func (cli *fakeClient) SwarmUpdate(_ context.Context, _ swarm.Version, swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
if cli.swarmUpdateFunc != nil {
|
||||
return cli.swarmUpdateFunc(swarm, flags)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error {
|
||||
func (cli *fakeClient) SwarmUnlock(_ context.Context, req swarm.UnlockRequest) error {
|
||||
if cli.swarmUnlockFunc != nil {
|
||||
return cli.swarmUnlockFunc(req)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// Helper function to set static slices
|
||||
func getCIDR(ip net.IP, cidr *net.IPNet, err error) net.IPNet {
|
||||
func getCIDR(_ net.IP, cidr *net.IPNet, _ error) net.IPNet {
|
||||
return *cidr
|
||||
}
|
||||
|
||||
|
||||
@ -14,14 +14,14 @@ type fakeClient struct {
|
||||
serviceInspectWithRaw func(ref string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
func (cli *fakeClient) NodeInspectWithRaw(_ context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectWithRaw != nil {
|
||||
return cli.nodeInspectWithRaw(ref)
|
||||
}
|
||||
return swarm.Node{}, nil, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, ref string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
func (cli *fakeClient) ServiceInspectWithRaw(_ context.Context, ref string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
if cli.serviceInspectWithRaw != nil {
|
||||
return cli.serviceInspectWithRaw(ref, options)
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ type trustKey struct {
|
||||
// This information is to be pretty printed or serialized into a machine-readable format.
|
||||
func lookupTrustInfo(cli command.Cli, remote string) ([]trustTagRow, []client.RoleWithSignatures, []data.Role, error) {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote)
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), remote)
|
||||
if err != nil {
|
||||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err
|
||||
}
|
||||
|
||||
@ -27,15 +27,15 @@ type fakeClient struct {
|
||||
apiclient.Client
|
||||
}
|
||||
|
||||
func (c *fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
func (c *fakeClient) Info(context.Context) (types.Info, error) {
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
|
||||
func (c *fakeClient) ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) {
|
||||
return types.ImageInspect{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) {
|
||||
func (c *fakeClient) ImagePush(context.Context, string, types.ImagePushOptions) (io.ReadCloser, error) {
|
||||
return &utils.NoopCloser{Reader: bytes.NewBuffer([]byte{})}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ func newRevokeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
func revokeTrust(cli command.Cli, remote string, options revokeOptions) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote)
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ func newSignCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func runSignImage(cli command.Cli, options signOptions) error {
|
||||
imageName := options.imageName
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), imageName)
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), imageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ func addSigner(cli command.Cli, options signerAddOptions) error {
|
||||
|
||||
func addSignerToRepo(cli command.Cli, signerName string, repoName string, signerPubKeys []data.PublicKey) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), repoName)
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), repoName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ func isLastSignerForReleases(roleWithSig data.Role, allRoles []client.RoleWithSi
|
||||
// The signer not being removed doesn't necessarily raise an error e.g. user choosing "No" when prompted for confirmation.
|
||||
func removeSingleSigner(cli command.Cli, repoName, signerName string, forceYes bool) (bool, error) {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), repoName)
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), repoName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -32,9 +32,9 @@ func (c *fakeClient) VolumeInspect(_ context.Context, volumeID string) (volume.V
|
||||
return volume.Volume{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) VolumeList(_ context.Context, filter filters.Args) (volume.ListResponse, error) {
|
||||
func (c *fakeClient) VolumeList(_ context.Context, options volume.ListOptions) (volume.ListResponse, error) {
|
||||
if c.volumeListFunc != nil {
|
||||
return c.volumeListFunc(filter)
|
||||
return c.volumeListFunc(options.Filters)
|
||||
}
|
||||
return volume.ListResponse{}, nil
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@ func TestVolumeCreateErrors(t *testing.T) {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@ func TestVolumeInspectErrors(t *testing.T) {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
flagsHelper "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -52,7 +53,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
func runList(dockerCli command.Cli, options listOptions) error {
|
||||
client := dockerCli.Client()
|
||||
volumes, err := client.VolumeList(context.Background(), options.filter.Value())
|
||||
volumes, err := client.VolumeList(context.Background(), volume.ListOptions{Filters: options.filter.Value()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -70,9 +71,9 @@ func runList(dockerCli command.Cli, options listOptions) error {
|
||||
|
||||
// trick for filtering in place
|
||||
n := 0
|
||||
for _, volume := range volumes.Volumes {
|
||||
if volume.ClusterVolume != nil {
|
||||
volumes.Volumes[n] = volume
|
||||
for _, vol := range volumes.Volumes {
|
||||
if vol.ClusterVolume != nil {
|
||||
volumes.Volumes[n] = vol
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ func TestVolumeListErrors(t *testing.T) {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +75,6 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
|
||||
// RunPrune calls the Volume Prune API
|
||||
// This returns the amount of space reclaimed and a detailed output string
|
||||
func RunPrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(dockerCli, pruneOptions{force: true, filter: filter})
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ func TestVolumePruneErrors(t *testing.T) {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
@ -109,7 +110,7 @@ func TestVolumePrunePromptNo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func simplePruneFunc(args filters.Args) (types.VolumesPruneReport, error) {
|
||||
func simplePruneFunc(filters.Args) (types.VolumesPruneReport, error) {
|
||||
return types.VolumesPruneReport{
|
||||
VolumesDeleted: []string{
|
||||
"foo", "bar", "baz",
|
||||
|
||||
@ -33,6 +33,7 @@ func TestVolumeRemoveErrors(t *testing.T) {
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,14 +596,14 @@ type fakeClient struct {
|
||||
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
|
||||
}
|
||||
|
||||
func (c *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
func (c *fakeClient) SecretList(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
if c.secretListFunc != nil {
|
||||
return c.secretListFunc(options)
|
||||
}
|
||||
return []swarm.Secret{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
func (c *fakeClient) ConfigList(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
if c.configListFunc != nil {
|
||||
return c.configListFunc(options)
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ const (
|
||||
|
||||
type portsFormatChecker struct{}
|
||||
|
||||
func (checker portsFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (checker portsFormatChecker) IsFormat(_ interface{}) bool {
|
||||
// TODO: implement this
|
||||
return true
|
||||
}
|
||||
|
||||
@ -300,7 +300,8 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
|
||||
for registryHostname := range configFile.CredentialHelpers {
|
||||
newAuth, err := configFile.GetAuthConfig(registryHostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname)
|
||||
continue
|
||||
}
|
||||
auths[registryHostname] = newAuth
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package configfile
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -166,8 +167,9 @@ func TestConfigFile(t *testing.T) {
|
||||
}
|
||||
|
||||
type mockNativeStore struct {
|
||||
GetAllCallCount int
|
||||
authConfigs map[string]types.AuthConfig
|
||||
GetAllCallCount int
|
||||
authConfigs map[string]types.AuthConfig
|
||||
authConfigErrors map[string]error
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) Erase(registryHostname string) error {
|
||||
@ -176,7 +178,7 @@ func (c *mockNativeStore) Erase(registryHostname string) error {
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) Get(registryHostname string) (types.AuthConfig, error) {
|
||||
return c.authConfigs[registryHostname], nil
|
||||
return c.authConfigs[registryHostname], c.authConfigErrors[registryHostname]
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) {
|
||||
@ -184,15 +186,15 @@ func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) {
|
||||
return c.authConfigs, nil
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) Store(authConfig types.AuthConfig) error {
|
||||
func (c *mockNativeStore) Store(_ types.AuthConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// make sure it satisfies the interface
|
||||
var _ credentials.Store = (*mockNativeStore)(nil)
|
||||
|
||||
func NewMockNativeStore(authConfigs map[string]types.AuthConfig) credentials.Store {
|
||||
return &mockNativeStore{authConfigs: authConfigs}
|
||||
func NewMockNativeStore(authConfigs map[string]types.AuthConfig, authConfigErrors map[string]error) credentials.Store {
|
||||
return &mockNativeStore{authConfigs: authConfigs, authConfigErrors: authConfigErrors}
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsFileStoreOnly(t *testing.T) {
|
||||
@ -220,7 +222,7 @@ func TestGetAllCredentialsCredsStore(t *testing.T) {
|
||||
Password: "pass",
|
||||
}
|
||||
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -237,6 +239,48 @@ func TestGetAllCredentialsCredsStore(t *testing.T) {
|
||||
assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount))
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredStoreErrorHandling(t *testing.T) {
|
||||
const (
|
||||
workingHelperRegistryHostname = "working-helper.example.com"
|
||||
brokenHelperRegistryHostname = "broken-helper.example.com"
|
||||
)
|
||||
configFile := New("filename")
|
||||
configFile.CredentialHelpers = map[string]string{
|
||||
workingHelperRegistryHostname: "cred_helper",
|
||||
brokenHelperRegistryHostname: "broken_cred_helper",
|
||||
}
|
||||
expectedAuth := types.AuthConfig{
|
||||
Username: "username",
|
||||
Password: "pass",
|
||||
}
|
||||
// configure the mock store to throw an error
|
||||
// when calling the helper for this registry
|
||||
authErrors := map[string]error{
|
||||
brokenHelperRegistryHostname: errors.New("an error"),
|
||||
}
|
||||
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{
|
||||
workingHelperRegistryHostname: expectedAuth,
|
||||
// configure an auth entry for the "broken" credential
|
||||
// helper that will throw an error
|
||||
brokenHelperRegistryHostname: {},
|
||||
}, authErrors)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
||||
return testCredsStore
|
||||
}
|
||||
|
||||
authConfigs, err := configFile.GetAllCredentials()
|
||||
|
||||
// make sure we're still returning the expected credentials
|
||||
// and skipping the ones throwing an error
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(1, len(authConfigs)))
|
||||
assert.Check(t, is.DeepEqual(expectedAuth, authConfigs[workingHelperRegistryHostname]))
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
const (
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
@ -261,7 +305,7 @@ func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
// Add in an extra auth entry which doesn't appear in CredentialHelpers section of the configFile.
|
||||
// This verifies that only explicitly configured registries are being requested from the cred helpers.
|
||||
testExtraCredHelperRegistryHostname: unexpectedCredHelperAuth,
|
||||
})
|
||||
}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -298,7 +342,7 @@ func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) {
|
||||
configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix}
|
||||
configFile.AuthConfigs[testFileStoreRegistryHostname] = expectedFileStoreAuth
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
|
||||
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
||||
return testCredHelper
|
||||
@ -337,8 +381,8 @@ func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) {
|
||||
Password: "cred_helper_pass",
|
||||
}
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -380,8 +424,8 @@ func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
||||
Password: "cred_helper_pass",
|
||||
}
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
|
||||
@ -32,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
// New returns net.Conn
|
||||
func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) {
|
||||
func New(_ context.Context, cmd string, args ...string) (net.Conn, error) {
|
||||
var (
|
||||
c commandConn
|
||||
err error
|
||||
|
||||
@ -2,12 +2,14 @@ package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -35,7 +37,7 @@ func (s *metadataStore) createOrUpdate(meta Metadata) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
|
||||
return ioutils.AtomicWriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
|
||||
}
|
||||
|
||||
func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
|
||||
@ -65,7 +67,8 @@ func (s *metadataStore) get(name string) (Metadata, error) {
|
||||
}
|
||||
|
||||
func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
|
||||
bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
|
||||
fileName := filepath.Join(s.contextDir(id), metaFile)
|
||||
bytes, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context not found"))
|
||||
@ -77,15 +80,15 @@ func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
|
||||
Endpoints: make(map[string]interface{}),
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &untyped); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
r.Name = untyped.Name
|
||||
if r.Metadata, err = parseTypedOrMap(untyped.Metadata, s.config.contextType); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
for k, v := range untyped.Endpoints {
|
||||
if r.Endpoints[k], err = parseTypedOrMap(v, s.config.endpointTypes[k]); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
|
||||
@ -7,9 +7,11 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
@ -230,3 +232,28 @@ func TestImportZipInvalid(t *testing.T) {
|
||||
err = Import("zipInvalid", s, r)
|
||||
assert.ErrorContains(t, err, "unexpected context file")
|
||||
}
|
||||
|
||||
func TestCorruptMetadata(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
s := New(tempDir, testCfg)
|
||||
err := s.CreateOrUpdate(
|
||||
Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
"ep1": endpoint{Foo: "bar"},
|
||||
},
|
||||
Metadata: context{Bar: "baz"},
|
||||
Name: "source",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Simulate the meta.json file getting corrupted
|
||||
// by some external process.
|
||||
contextDir := s.meta.contextDir(contextdirOf("source"))
|
||||
contextFile := filepath.Join(contextDir, metaFile)
|
||||
err = os.WriteFile(contextFile, nil, 0o600)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Assert that the error message gives the user some clue where to look.
|
||||
_, err = s.GetMetadata("source")
|
||||
assert.ErrorContains(t, err, fmt.Sprintf("parsing %s: unexpected end of JSON input", contextFile))
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -31,7 +32,7 @@ func (s *tlsStore) createOrUpdate(name, endpointName, filename string, data []by
|
||||
if err := os.MkdirAll(endpointDir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(endpointDir, filename), data, 0o600)
|
||||
return ioutils.AtomicWriteFile(filepath.Join(endpointDir, filename), data, 0o600)
|
||||
}
|
||||
|
||||
func (s *tlsStore) getData(name, endpointName, filename string) ([]byte, error) {
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
@ -16,10 +17,12 @@ import (
|
||||
type ImageManifest struct {
|
||||
Ref *SerializableNamed
|
||||
Descriptor ocispec.Descriptor
|
||||
Raw []byte `json:",omitempty"`
|
||||
|
||||
// SchemaV2Manifest is used for inspection
|
||||
// TODO: Deprecate this and store manifest blobs
|
||||
SchemaV2Manifest *schema2.DeserializedManifest `json:",omitempty"`
|
||||
// OCIManifest is used for inspection
|
||||
OCIManifest *ocischema.DeserializedManifest `json:",omitempty"`
|
||||
}
|
||||
|
||||
// OCIPlatform creates an OCI platform from a manifest list platform spec
|
||||
@ -53,8 +56,15 @@ func PlatformSpecFromOCI(p *ocispec.Platform) *manifestlist.PlatformSpec {
|
||||
// Blobs returns the digests for all the blobs referenced by this manifest
|
||||
func (i ImageManifest) Blobs() []digest.Digest {
|
||||
digests := []digest.Digest{}
|
||||
for _, descriptor := range i.SchemaV2Manifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
for _, descriptor := range i.SchemaV2Manifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
}
|
||||
case i.OCIManifest != nil:
|
||||
for _, descriptor := range i.OCIManifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
}
|
||||
}
|
||||
return digests
|
||||
}
|
||||
@ -65,6 +75,8 @@ func (i ImageManifest) Payload() (string, []byte, error) {
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
return i.SchemaV2Manifest.Payload()
|
||||
case i.OCIManifest != nil:
|
||||
return i.OCIManifest.Payload()
|
||||
default:
|
||||
return "", nil, errors.Errorf("%s has no payload", i.Ref)
|
||||
}
|
||||
@ -76,6 +88,8 @@ func (i ImageManifest) References() []distribution.Descriptor {
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
return i.SchemaV2Manifest.References()
|
||||
case i.OCIManifest != nil:
|
||||
return i.OCIManifest.References()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -84,13 +98,35 @@ func (i ImageManifest) References() []distribution.Descriptor {
|
||||
// NewImageManifest returns a new ImageManifest object. The values for Platform
|
||||
// are initialized from those in the image
|
||||
func NewImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *schema2.DeserializedManifest) ImageManifest {
|
||||
raw, err := manifest.MarshalJSON()
|
||||
if err != nil {
|
||||
raw = nil
|
||||
}
|
||||
|
||||
return ImageManifest{
|
||||
Ref: &SerializableNamed{Named: ref},
|
||||
Descriptor: desc,
|
||||
Raw: raw,
|
||||
SchemaV2Manifest: manifest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOCIImageManifest returns a new ImageManifest object. The values for
|
||||
// Platform are initialized from those in the image
|
||||
func NewOCIImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *ocischema.DeserializedManifest) ImageManifest {
|
||||
raw, err := manifest.MarshalJSON()
|
||||
if err != nil {
|
||||
raw = nil
|
||||
}
|
||||
|
||||
return ImageManifest{
|
||||
Ref: &SerializableNamed{Named: ref},
|
||||
Descriptor: desc,
|
||||
Raw: raw,
|
||||
OCIManifest: manifest,
|
||||
}
|
||||
}
|
||||
|
||||
// SerializableNamed is a reference.Named that can be serialized and deserialized
|
||||
// from JSON
|
||||
type SerializableNamed struct {
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/reference"
|
||||
distributionclient "github.com/docker/distribution/registry/client"
|
||||
"github.com/docker/docker/api/types"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
@ -36,7 +35,7 @@ func NewRegistryClient(resolver AuthConfigResolver, userAgent string, insecure b
|
||||
}
|
||||
|
||||
// AuthConfigResolver returns Auth Configuration for an index
|
||||
type AuthConfigResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig
|
||||
type AuthConfigResolver func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig
|
||||
|
||||
// PutManifestOptions is the data sent to push a manifest
|
||||
type PutManifestOptions struct {
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
authtypes "github.com/docker/docker/api/types"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -74,7 +74,7 @@ func getDefaultEndpointFromRepoInfo(repoInfo *registry.RepositoryInfo) (registry
|
||||
}
|
||||
|
||||
// getHTTPTransport builds a transport for use in communicating with a registry
|
||||
func getHTTPTransport(authConfig authtypes.AuthConfig, endpoint registry.APIEndpoint, repoName string, userAgent string) (http.RoundTripper, error) {
|
||||
func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.APIEndpoint, repoName string, userAgent string) (http.RoundTripper, error) {
|
||||
// get the http transport, this will be used in a client to upload manifest
|
||||
base := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
@ -120,7 +120,7 @@ type existingTokenHandler struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||
func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, _ map[string]string) error {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/docker/cli/cli/manifest/types"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
@ -35,6 +36,12 @@ func fetchManifest(ctx context.Context, repo distribution.Repository, ref refere
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
return imageManifest, nil
|
||||
case *ocischema.DeserializedManifest:
|
||||
imageManifest, err := pullManifestOCISchema(ctx, ref, repo, *v)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
return imageManifest, nil
|
||||
case *manifestlist.DeserializedManifestList:
|
||||
return types.ImageManifest{}, errors.Errorf("%s is a manifest list", ref)
|
||||
}
|
||||
@ -94,6 +101,28 @@ func pullManifestSchemaV2(ctx context.Context, ref reference.Named, repo distrib
|
||||
return types.NewImageManifest(ref, manifestDesc, &mfst), nil
|
||||
}
|
||||
|
||||
func pullManifestOCISchema(ctx context.Context, ref reference.Named, repo distribution.Repository, mfst ocischema.DeserializedManifest) (types.ImageManifest, error) {
|
||||
manifestDesc, err := validateManifestDigest(ref, mfst)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
configJSON, err := pullManifestSchemaV2ImageConfig(ctx, mfst.Target().Digest, repo)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
|
||||
if manifestDesc.Platform == nil {
|
||||
manifestDesc.Platform = &ocispec.Platform{}
|
||||
}
|
||||
|
||||
// Fill in os and architecture fields from config JSON
|
||||
if err := json.Unmarshal(configJSON, manifestDesc.Platform); err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
|
||||
return types.NewOCIImageManifest(ref, manifestDesc, &mfst), nil
|
||||
}
|
||||
|
||||
func pullManifestSchemaV2ImageConfig(ctx context.Context, dgst digest.Digest, repo distribution.Repository) ([]byte, error) {
|
||||
blobs := repo.Blobs(ctx)
|
||||
configJSON, err := blobs.Get(ctx, dgst)
|
||||
@ -153,16 +182,21 @@ func pullManifestList(ctx context.Context, ref reference.Named, repo distributio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, ok := manifest.(*schema2.DeserializedManifest)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unsupported manifest format: %v", v)
|
||||
}
|
||||
|
||||
manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageManifest, err := pullManifestSchemaV2(ctx, manifestRef, repo, *v)
|
||||
|
||||
var imageManifest types.ImageManifest
|
||||
switch v := manifest.(type) {
|
||||
case *schema2.DeserializedManifest:
|
||||
imageManifest, err = pullManifestSchemaV2(ctx, manifestRef, repo, *v)
|
||||
case *ocischema.DeserializedManifest:
|
||||
imageManifest, err = pullManifestOCISchema(ctx, manifestRef, repo, *v)
|
||||
default:
|
||||
err = errors.Errorf("unsupported manifest type: %T", manifest)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ import (
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/docker/api/types"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
@ -79,24 +78,23 @@ func Server(index *registrytypes.IndexInfo) (string, error) {
|
||||
}
|
||||
|
||||
type simpleCredentialStore struct {
|
||||
auth types.AuthConfig
|
||||
auth registrytypes.AuthConfig
|
||||
}
|
||||
|
||||
func (scs simpleCredentialStore) Basic(u *url.URL) (string, string) {
|
||||
func (scs simpleCredentialStore) Basic(*url.URL) (string, string) {
|
||||
return scs.auth.Username, scs.auth.Password
|
||||
}
|
||||
|
||||
func (scs simpleCredentialStore) RefreshToken(u *url.URL, service string) string {
|
||||
func (scs simpleCredentialStore) RefreshToken(*url.URL, string) string {
|
||||
return scs.auth.IdentityToken
|
||||
}
|
||||
|
||||
func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {
|
||||
}
|
||||
func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {}
|
||||
|
||||
// GetNotaryRepository returns a NotaryRepository which stores all the
|
||||
// information needed to operate on a notary repository.
|
||||
// It creates an HTTP transport providing authentication support.
|
||||
func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo *registry.RepositoryInfo, authConfig *types.AuthConfig, actions ...string) (client.Repository, error) {
|
||||
func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo *registry.RepositoryInfo, authConfig *registrytypes.AuthConfig, actions ...string) (client.Repository, error) {
|
||||
server, err := Server(repoInfo.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -160,7 +158,7 @@ func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo
|
||||
scope := auth.RepositoryScope{
|
||||
Repository: repoInfo.Name.Name(),
|
||||
Actions: actions,
|
||||
Class: repoInfo.Class,
|
||||
Class: repoInfo.Class, // TODO(thaJeztah): Class is no longer needed for plugins and can likely be removed; see https://github.com/docker/cli/pull/4114#discussion_r1145430825
|
||||
}
|
||||
creds := simpleCredentialStore{auth: *authConfig}
|
||||
tokenHandlerOptions := auth.TokenHandlerOptions{
|
||||
@ -292,7 +290,7 @@ func GetSignableRoles(repo client.Repository, target *client.Target) ([]data.Rol
|
||||
// ImageRefAndAuth contains all reference information and the auth config for an image request
|
||||
type ImageRefAndAuth struct {
|
||||
original string
|
||||
authConfig *types.AuthConfig
|
||||
authConfig *registrytypes.AuthConfig
|
||||
reference reference.Named
|
||||
repoInfo *registry.RepositoryInfo
|
||||
tag string
|
||||
@ -301,8 +299,8 @@ type ImageRefAndAuth struct {
|
||||
|
||||
// GetImageReferencesAndAuth retrieves the necessary reference and auth information for an image name
|
||||
// as an ImageRefAndAuth struct
|
||||
func GetImageReferencesAndAuth(ctx context.Context, rs registry.Service,
|
||||
authResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig,
|
||||
func GetImageReferencesAndAuth(ctx context.Context,
|
||||
authResolver func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig,
|
||||
imgName string,
|
||||
) (ImageRefAndAuth, error) {
|
||||
ref, err := reference.ParseNormalizedNamed(imgName)
|
||||
@ -311,13 +309,7 @@ func GetImageReferencesAndAuth(ctx context.Context, rs registry.Service,
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
var repoInfo *registry.RepositoryInfo
|
||||
if rs != nil {
|
||||
repoInfo, err = rs.ResolveRepository(ref)
|
||||
} else {
|
||||
repoInfo, err = registry.ParseRepositoryInfo(ref)
|
||||
}
|
||||
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return ImageRefAndAuth{}, err
|
||||
}
|
||||
@ -356,7 +348,7 @@ func getDigest(ref reference.Named) digest.Digest {
|
||||
}
|
||||
|
||||
// AuthConfig returns the auth information (username, etc) for a given ImageRefAndAuth
|
||||
func (imgRefAuth *ImageRefAndAuth) AuthConfig() *types.AuthConfig {
|
||||
func (imgRefAuth *ImageRefAndAuth) AuthConfig() *registrytypes.AuthConfig {
|
||||
return imgRefAuth.authConfig
|
||||
}
|
||||
|
||||
|
||||
@ -51,6 +51,10 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
|
||||
DisableDescriptions: true,
|
||||
},
|
||||
}
|
||||
cmd.SetIn(dockerCli.In())
|
||||
cmd.SetOut(dockerCli.Out())
|
||||
cmd.SetErr(dockerCli.Err())
|
||||
|
||||
opts, flags, helpCmd = cli.SetupRootCommand(cmd)
|
||||
registerCompletionFuncForGlobalFlags(dockerCli, cmd)
|
||||
flags.BoolP("version", "v", false, "Print version information and quit")
|
||||
@ -129,13 +133,20 @@ func tryRunPluginHelp(dockerCli command.Cli, ccmd *cobra.Command, cargs []string
|
||||
func setHelpFunc(dockerCli command.Cli, cmd *cobra.Command) {
|
||||
defaultHelpFunc := cmd.HelpFunc()
|
||||
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
|
||||
if pluginmanager.IsPluginCommand(ccmd) {
|
||||
if err := pluginmanager.AddPluginCommandStubs(dockerCli, ccmd.Root()); err != nil {
|
||||
ccmd.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) >= 1 {
|
||||
err := tryRunPluginHelp(dockerCli, ccmd, args)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if !pluginmanager.IsNotFound(err) {
|
||||
ccmd.Println(err)
|
||||
return
|
||||
}
|
||||
cmd.PrintErrf("unknown help topic: %v\n", ccmd.Name())
|
||||
return
|
||||
}
|
||||
|
||||
if err := isSupported(ccmd, dockerCli); err != nil {
|
||||
@ -223,9 +234,14 @@ func runDocker(dockerCli *command.DockerCli) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pluginmanager.AddPluginCommandStubs(dockerCli, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
if cli.HasCompletionArg(args) {
|
||||
// We add plugin command stubs early only for completion. We don't
|
||||
// want to add them for normal command execution as it would cause
|
||||
// a significant performance hit.
|
||||
err = pluginmanager.AddPluginCommandStubs(dockerCli, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
|
||||
@ -84,13 +84,13 @@ __docker_q() {
|
||||
# precedence over the environment setting.
|
||||
__docker_configs() {
|
||||
local format
|
||||
if [ "$1" = "--id" ] ; then
|
||||
if [ "${1-}" = "--id" ] ; then
|
||||
format='{{.ID}}'
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
elif [ "${1-}" = "--name" ] ; then
|
||||
format='{{.Name}}'
|
||||
shift
|
||||
elif [ "$DOCKER_COMPLETION_SHOW_CONFIG_IDS" = yes ] ; then
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_CONFIG_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Name}}'
|
||||
else
|
||||
format='{{.Name}}'
|
||||
@ -120,13 +120,13 @@ __docker_complete_configs() {
|
||||
# precedence over the environment setting.
|
||||
__docker_containers() {
|
||||
local format
|
||||
if [ "$1" = "--id" ] ; then
|
||||
if [ "${1-}" = "--id" ] ; then
|
||||
format='{{.ID}}'
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
elif [ "${1-}" = "--name" ] ; then
|
||||
format='{{.Names}}'
|
||||
shift
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_CONTAINER_IDS}" = yes ] ; then
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_CONTAINER_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Names}}'
|
||||
else
|
||||
format='{{.Names}}'
|
||||
@ -139,7 +139,7 @@ __docker_containers() {
|
||||
# Additional filters may be appended, see `__docker_containers`.
|
||||
__docker_complete_containers() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -191,7 +191,7 @@ __docker_complete_container_ids() {
|
||||
__docker_contexts() {
|
||||
local add=()
|
||||
while true ; do
|
||||
case "$1" in
|
||||
case "${1-}" in
|
||||
--add)
|
||||
add+=("$2")
|
||||
shift 2
|
||||
@ -236,12 +236,12 @@ __docker_images() {
|
||||
local all
|
||||
local format
|
||||
|
||||
if [ "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" = "all" ] ; then
|
||||
if [ "${DOCKER_COMPLETION_SHOW_IMAGE_IDS-}" = "all" ] ; then
|
||||
all='--all'
|
||||
fi
|
||||
|
||||
while true ; do
|
||||
case "$1" in
|
||||
case "${1-}" in
|
||||
--repo)
|
||||
format+="$repo_format\n"
|
||||
shift
|
||||
@ -253,7 +253,7 @@ __docker_images() {
|
||||
shift
|
||||
;;
|
||||
--id)
|
||||
if [[ $DOCKER_COMPLETION_SHOW_IMAGE_IDS =~ ^(all|non-intermediate)$ ]] ; then
|
||||
if [[ ${DOCKER_COMPLETION_SHOW_IMAGE_IDS-} =~ ^(all|non-intermediate)$ ]] ; then
|
||||
format+="$id_format\n"
|
||||
fi
|
||||
shift
|
||||
@ -269,7 +269,7 @@ __docker_images() {
|
||||
esac
|
||||
done
|
||||
|
||||
__docker_q image ls --no-trunc --format "${format%\\n}" $all "$@" | grep -v '<none>$'
|
||||
__docker_q image ls --no-trunc --format "${format%\\n}" ${all-} "$@" | grep -v '<none>$'
|
||||
}
|
||||
|
||||
# __docker_complete_images applies completion of images based on the current value of `$cur` or
|
||||
@ -277,7 +277,7 @@ __docker_images() {
|
||||
# See __docker_images for customization of the returned items.
|
||||
__docker_complete_images() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -295,13 +295,13 @@ __docker_complete_images() {
|
||||
# precedence over the environment setting.
|
||||
__docker_networks() {
|
||||
local format
|
||||
if [ "$1" = "--id" ] ; then
|
||||
if [ "${1-}" = "--id" ] ; then
|
||||
format='{{.ID}}'
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
elif [ "${1-}" = "--name" ] ; then
|
||||
format='{{.Name}}'
|
||||
shift
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_NETWORK_IDS}" = yes ] ; then
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_NETWORK_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Name}}'
|
||||
else
|
||||
format='{{.Name}}'
|
||||
@ -314,7 +314,7 @@ __docker_networks() {
|
||||
# Additional filters may be appended, see `__docker_networks`.
|
||||
__docker_complete_networks() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -340,7 +340,7 @@ __docker_volumes() {
|
||||
# Additional filters may be appended, see `__docker_volumes`.
|
||||
__docker_complete_volumes() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -356,7 +356,7 @@ __docker_complete_volumes() {
|
||||
__docker_plugins_bundled() {
|
||||
local type add=() remove=()
|
||||
while true ; do
|
||||
case "$1" in
|
||||
case "${1-}" in
|
||||
--type)
|
||||
type="$2"
|
||||
shift 2
|
||||
@ -390,7 +390,7 @@ __docker_plugins_bundled() {
|
||||
# `__docker_complete_plugins_installed`.
|
||||
__docker_complete_plugins_bundled() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -406,7 +406,7 @@ __docker_complete_plugins_bundled() {
|
||||
# For built-in pugins, see `__docker_plugins_bundled`.
|
||||
__docker_plugins_installed() {
|
||||
local format
|
||||
if [ "$DOCKER_COMPLETION_SHOW_PLUGIN_IDS" = yes ] ; then
|
||||
if [ "${DOCKER_COMPLETION_SHOW_PLUGIN_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Name}}'
|
||||
else
|
||||
format='{{.Name}}'
|
||||
@ -421,7 +421,7 @@ __docker_plugins_installed() {
|
||||
# For completion of built-in pugins, see `__docker_complete_plugins_bundled`.
|
||||
__docker_complete_plugins_installed() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -446,13 +446,13 @@ __docker_complete_runtimes() {
|
||||
# precedence over the environment setting.
|
||||
__docker_secrets() {
|
||||
local format
|
||||
if [ "$1" = "--id" ] ; then
|
||||
if [ "${1-}" = "--id" ] ; then
|
||||
format='{{.ID}}'
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
elif [ "${1-}" = "--name" ] ; then
|
||||
format='{{.Name}}'
|
||||
shift
|
||||
elif [ "$DOCKER_COMPLETION_SHOW_SECRET_IDS" = yes ] ; then
|
||||
elif [ "${DOCKER_COMPLETION_SHOW_SECRET_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Name}}'
|
||||
else
|
||||
format='{{.Name}}'
|
||||
@ -465,7 +465,7 @@ __docker_secrets() {
|
||||
# of `$cur` or the value of the optional first option `--cur`, if given.
|
||||
__docker_complete_secrets() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -481,7 +481,7 @@ __docker_stacks() {
|
||||
# of `$cur` or the value of the optional first option `--cur`, if given.
|
||||
__docker_complete_stacks() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -499,7 +499,7 @@ __docker_complete_stacks() {
|
||||
# Completions may be added with `--add`, e.g. `--add self`.
|
||||
__docker_nodes() {
|
||||
local format
|
||||
if [ "$DOCKER_COMPLETION_SHOW_NODE_IDS" = yes ] ; then
|
||||
if [ "${DOCKER_COMPLETION_SHOW_NODE_IDS-}" = yes ] ; then
|
||||
format='{{.ID}} {{.Hostname}}'
|
||||
else
|
||||
format='{{.Hostname}}'
|
||||
@ -508,7 +508,7 @@ __docker_nodes() {
|
||||
local add=()
|
||||
|
||||
while true ; do
|
||||
case "$1" in
|
||||
case "${1-}" in
|
||||
--id)
|
||||
format='{{.ID}}'
|
||||
shift
|
||||
@ -535,7 +535,7 @@ __docker_nodes() {
|
||||
# Additional filters may be appended, see `__docker_nodes`.
|
||||
__docker_complete_nodes() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -552,12 +552,12 @@ __docker_complete_nodes() {
|
||||
# precedence over the environment setting.
|
||||
__docker_services() {
|
||||
local format='{{.Name}}' # default: service name only
|
||||
[ "${DOCKER_COMPLETION_SHOW_SERVICE_IDS}" = yes ] && format='{{.ID}} {{.Name}}' # ID & name
|
||||
[ "${DOCKER_COMPLETION_SHOW_SERVICE_IDS-}" = yes ] && format='{{.ID}} {{.Name}}' # ID & name
|
||||
|
||||
if [ "$1" = "--id" ] ; then
|
||||
if [ "${1-}" = "--id" ] ; then
|
||||
format='{{.ID}}' # IDs only
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
elif [ "${1-}" = "--name" ] ; then
|
||||
format='{{.Name}}' # names only
|
||||
shift
|
||||
fi
|
||||
@ -570,7 +570,7 @@ __docker_services() {
|
||||
# Additional filters may be appended, see `__docker_services`.
|
||||
__docker_complete_services() {
|
||||
local current="$cur"
|
||||
if [ "$1" = "--cur" ] ; then
|
||||
if [ "${1-}" = "--cur" ] ; then
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -601,7 +601,7 @@ __docker_append_to_completions() {
|
||||
# several variables with the results.
|
||||
# The result is cached for the duration of one invocation of bash completion.
|
||||
__docker_fetch_info() {
|
||||
if [ -z "$info_fetched" ] ; then
|
||||
if [ -z "${info_fetched-}" ] ; then
|
||||
read -r server_experimental server_os <<< "$(__docker_q version -f '{{.Server.Experimental}} {{.Server.Os}}')"
|
||||
info_fetched=true
|
||||
fi
|
||||
@ -629,7 +629,7 @@ __docker_server_os_is() {
|
||||
# you should pass a glob describing those options, e.g. "--option1|-o|--option2"
|
||||
# Use this function to restrict completions to exact positions after the argument list.
|
||||
__docker_pos_first_nonflag() {
|
||||
local argument_flags=$1
|
||||
local argument_flags=${1-}
|
||||
|
||||
local counter=$((${subcommand_pos:-${command_pos}} + 1))
|
||||
while [ "$counter" -le "$cword" ]; do
|
||||
@ -766,7 +766,7 @@ __docker_local_interfaces() {
|
||||
command -v ip >/dev/null 2>&1 || return
|
||||
|
||||
local format
|
||||
if [ "$1" = "--ip-only" ] ; then
|
||||
if [ "${1-}" = "--ip-only" ] ; then
|
||||
format='\1'
|
||||
shift
|
||||
else
|
||||
@ -781,7 +781,7 @@ __docker_local_interfaces() {
|
||||
# An additional value can be added to the possible completions with an `--add` argument.
|
||||
__docker_complete_local_interfaces() {
|
||||
local additional_interface
|
||||
if [ "$1" = "--add" ] ; then
|
||||
if [ "${1-}" = "--add" ] ; then
|
||||
additional_interface="$2"
|
||||
shift 2
|
||||
fi
|
||||
@ -1125,7 +1125,7 @@ __docker_complete_ulimits() {
|
||||
sigpending
|
||||
stack
|
||||
"
|
||||
if [ "$1" = "--rm" ] ; then
|
||||
if [ "${1-}" = "--rm" ] ; then
|
||||
COMPREPLY=( $( compgen -W "$limits" -- "$cur" ) )
|
||||
else
|
||||
COMPREPLY=( $( compgen -W "$limits" -S = -- "$cur" ) )
|
||||
@ -1142,6 +1142,29 @@ __docker_complete_user_group() {
|
||||
fi
|
||||
}
|
||||
|
||||
DOCKER_PLUGINS_PATH=$(docker info --format '{{range .ClientInfo.Plugins}}{{.Path}}:{{end}}')
|
||||
|
||||
__docker_complete_plugin() {
|
||||
local path=$1
|
||||
local completionCommand="__completeNoDesc"
|
||||
local resultArray=($path $completionCommand)
|
||||
for value in "${words[@]:2}"; do
|
||||
if [ -z "$value" ]; then
|
||||
resultArray+=( "''" )
|
||||
else
|
||||
resultArray+=( "$value" )
|
||||
fi
|
||||
done
|
||||
local result=$(eval "${resultArray[*]}" 2> /dev/null | grep -v '^:[0-9]*$')
|
||||
|
||||
# if result empty, just use filename completion as fallback
|
||||
if [ -z "$result" ]; then
|
||||
_filedir
|
||||
else
|
||||
COMPREPLY=( $(compgen -W "${result}" -- "${current-}") )
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_docker() {
|
||||
# global options that may appear after the docker command
|
||||
local boolean_options="
|
||||
@ -2785,7 +2808,7 @@ _docker_image_build() {
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$DOCKER_BUILDKIT" = "1" ] ; then
|
||||
if [ "${DOCKER_BUILDKIT-}" = "1" ] ; then
|
||||
options_with_args+="
|
||||
--output -o
|
||||
--progress
|
||||
@ -3133,7 +3156,7 @@ _docker_inspect() {
|
||||
local preselected_type
|
||||
local type
|
||||
|
||||
if [ "$1" = "--type" ] ; then
|
||||
if [ "${1-}" = "--type" ] ; then
|
||||
preselected_type=yes
|
||||
type="$2"
|
||||
else
|
||||
@ -5395,23 +5418,6 @@ _docker_wait() {
|
||||
_docker_container_wait
|
||||
}
|
||||
|
||||
COMPOSE_PLUGIN_PATH=$(docker info --format '{{range .ClientInfo.Plugins}}{{if eq .Name "compose"}}{{.Path}}{{end}}{{end}}')
|
||||
|
||||
_docker_compose() {
|
||||
local completionCommand="__completeNoDesc"
|
||||
local resultArray=($COMPOSE_PLUGIN_PATH $completionCommand compose)
|
||||
for value in "${words[@]:2}"; do
|
||||
if [ -z "$value" ]; then
|
||||
resultArray+=( "''" )
|
||||
else
|
||||
resultArray+=( "$value" )
|
||||
fi
|
||||
done
|
||||
local result=$(eval "${resultArray[*]}" 2> /dev/null | grep -v '^:[0-9]*$')
|
||||
|
||||
COMPREPLY=( $(compgen -W "${result}" -- "$current") )
|
||||
}
|
||||
|
||||
_docker() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
@ -5481,18 +5487,23 @@ _docker() {
|
||||
wait
|
||||
)
|
||||
|
||||
# Create completion functions for all registered plugins
|
||||
local known_plugin_commands=()
|
||||
|
||||
if [ -f "$COMPOSE_PLUGIN_PATH" ] ; then
|
||||
known_plugin_commands+=("compose")
|
||||
fi
|
||||
local plugin_name=""
|
||||
for plugin_path in ${DOCKER_PLUGINS_PATH//:/ }; do
|
||||
plugin_name=$(basename "$plugin_path" | sed 's/ *$//')
|
||||
plugin_name=${plugin_name#docker-}
|
||||
plugin_name=${plugin_name%%.*}
|
||||
eval "_docker_${plugin_name}() { __docker_complete_plugin \"${plugin_path}\"; }"
|
||||
known_plugin_commands+=(${plugin_name})
|
||||
done
|
||||
|
||||
local experimental_server_commands=(
|
||||
checkpoint
|
||||
)
|
||||
|
||||
local commands=(${management_commands[*]} ${top_level_commands[*]} ${known_plugin_commands[*]})
|
||||
[ -z "$DOCKER_HIDE_LEGACY_COMMANDS" ] && commands+=(${legacy_commands[*]})
|
||||
[ -z "${DOCKER_HIDE_LEGACY_COMMANDS-}" ] && commands+=(${legacy_commands[*]})
|
||||
|
||||
# These options are valid as global options for all client commands
|
||||
# and valid as command options for `docker daemon`
|
||||
|
||||
@ -662,7 +662,7 @@ __docker_container_subcommand() {
|
||||
"($help)*--ulimit=[ulimit options]:ulimit: "
|
||||
"($help)--userns=[Container user namespace]:user namespace:(host)"
|
||||
"($help)--tmpfs[mount tmpfs]"
|
||||
"($help)*-v[Bind mount a volume]:volume: "
|
||||
"($help)*-v[Bind mount a volume]:volume:_directories -W / -P '/' -S '\:' -r '/ '"
|
||||
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
|
||||
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
|
||||
"($help -w --workdir)"{-w=,--workdir=}"[Working directory inside the container]:directory:_directories"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.19.5"
|
||||
default = "1.20.2"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
@ -52,7 +52,7 @@ target "binary" {
|
||||
platforms = ["local"]
|
||||
output = ["build"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
PACKAGER_NAME = PACKAGER_NAME
|
||||
GO_STRIP = STRIP_TARGET
|
||||
@ -72,7 +72,7 @@ target "plugins" {
|
||||
platforms = ["local"]
|
||||
output = ["build"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
GO_STRIP = STRIP_TARGET
|
||||
}
|
||||
@ -155,7 +155,13 @@ target "e2e-image" {
|
||||
output = ["type=docker"]
|
||||
tags = ["${IMAGE_NAME}"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
}
|
||||
}
|
||||
|
||||
target "e2e-gencerts" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./e2e/testdata/Dockerfile.gencerts"
|
||||
output = ["./e2e/testdata"]
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.5
|
||||
ARG GO_VERSION=1.20.2
|
||||
ARG ALPINE_VERSION=3.16
|
||||
|
||||
ARG BUILDX_VERSION=0.9.1
|
||||
ARG BUILDX_VERSION=0.10.4
|
||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.5
|
||||
ARG GO_VERSION=1.20.2
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG GOLANGCI_LINT_VERSION=v1.49.0
|
||||
ARG GOLANGCI_LINT_VERSION=v1.52.2
|
||||
|
||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.4
|
||||
ARG GO_VERSION=1.20.2
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG MODOUTDATED_VERSION=v0.8.0
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user