Compare commits
268 Commits
v25.0.0-be
...
v25.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 29cf629222 | |||
| 4caf4de039 | |||
| 950ecd42fd | |||
| 6ab4781bd0 | |||
| e8852e8ed2 | |||
| 4e097c643d | |||
| 01f9332618 | |||
| 4cd8d5cf47 | |||
| 21c12847bf | |||
| 22e1f2cbfa | |||
| 68abf14c15 | |||
| 85a5ee4cb0 | |||
| 9e1e07657a | |||
| e758fe5a7f | |||
| cdb1c105f6 | |||
| d161a2a470 | |||
| 3df149545e | |||
| 33b00e5185 | |||
| 22bbdc7984 | |||
| 5fde1f2b80 | |||
| 350d6bcc2c | |||
| d2efc5b09c | |||
| a18dad3832 | |||
| 508346ef61 | |||
| ca58fdf5a1 | |||
| f722e07c62 | |||
| 5f6c55a724 | |||
| 26560ff93c | |||
| 37f5515666 | |||
| b35361e8d2 | |||
| 67c5a9b6ff | |||
| 9beb0e5f03 | |||
| dfa2930afc | |||
| 21c2536051 | |||
| a6774de682 | |||
| cf9d3692a3 | |||
| 077d07c4f0 | |||
| d868dca00f | |||
| 1fc6ef9d63 | |||
| 1609916f2c | |||
| ff25c4334b | |||
| 688de6db16 | |||
| ef5e5fa03f | |||
| ad12276ea0 | |||
| a226502619 | |||
| 6d0b329b0d | |||
| dbf992f91f | |||
| 8b6ffbdf77 | |||
| 52b740ac27 | |||
| d469be256e | |||
| ccc7ad2f2c | |||
| 708d1136d6 | |||
| fdcb78a0fe | |||
| 15d4c99f38 | |||
| ecf338f43b | |||
| 6ad07f2a4b | |||
| 859154b94c | |||
| 76e09dd44b | |||
| a745bffb86 | |||
| e5d225de16 | |||
| 0e37dd49f0 | |||
| c0a0b05dc8 | |||
| 11b2e871bc | |||
| 4dc2c895b1 | |||
| c825db8a69 | |||
| 79992184e0 | |||
| 4d2b4e7fba | |||
| 5a31004bdb | |||
| a5e5563f13 | |||
| bc6b9d9c4b | |||
| 4f49508861 | |||
| cfe18f5e03 | |||
| 26f59b2f66 | |||
| 5c6ca07208 | |||
| 9db56ea2f6 | |||
| efae960e5a | |||
| 996cce9098 | |||
| 4b10e55256 | |||
| 1ebc233b4b | |||
| 4b06a93c5e | |||
| b4fe77a124 | |||
| acbc2540ae | |||
| 1df7161b4b | |||
| 909111b3ad | |||
| 26e3eb32ce | |||
| b36c16d38d | |||
| bce868bdfd | |||
| b43ea528b8 | |||
| 6d0aa0a52d | |||
| 9be600a97b | |||
| 8812e0ad3a | |||
| 878b1c55b7 | |||
| f0edc79530 | |||
| 3c0c68a45a | |||
| c1016c05cf | |||
| eed2d9c765 | |||
| 38d141b900 | |||
| 58524685da | |||
| 1e38fc3b9d | |||
| ae41fd913e | |||
| bbb922226c | |||
| 0fa3a365f7 | |||
| 33827cdd08 | |||
| d1a51582e0 | |||
| 8dd2cda041 | |||
| 771bc0afce | |||
| 60af715d65 | |||
| 90a1887d3c | |||
| dd3ba7396d | |||
| 64e9cadd1c | |||
| 21682ebd5c | |||
| 3cc1361f01 | |||
| 924ec7f4ca | |||
| 5777c1bd53 | |||
| 800a728e12 | |||
| 6f2f54715c | |||
| c1c6198aed | |||
| 336787c50a | |||
| 53a129e3d4 | |||
| 3ecd6b6c7f | |||
| a4d805f9cf | |||
| 1dcd5487f0 | |||
| 46355a1941 | |||
| 8b53402125 | |||
| e9a5590add | |||
| 05a2dc401f | |||
| b642078c87 | |||
| 3b57acb236 | |||
| 633ba88c26 | |||
| 5ed6c128e8 | |||
| 2389768fb7 | |||
| 1f97a34ac2 | |||
| 4d434dc691 | |||
| 50ad446a68 | |||
| 5eac07b103 | |||
| 5ba998d6b4 | |||
| 6fd4cff1f7 | |||
| a2c949212a | |||
| 0de84f0190 | |||
| 1866448dfa | |||
| 3cf0bf84a5 | |||
| 36d4db27d5 | |||
| 3d70100d5d | |||
| 70d01b9262 | |||
| 70216b662d | |||
| f1f47793fc | |||
| 88e6474350 | |||
| ae2b58fd98 | |||
| 2b63b4f8d1 | |||
| ca9ff8dafc | |||
| 8f90d984bb | |||
| 77e67a0f92 | |||
| 8f13b04162 | |||
| d79dd7c531 | |||
| b14094df53 | |||
| f63065a58b | |||
| fa1914426d | |||
| 103840e85e | |||
| 9b06602545 | |||
| e81a666614 | |||
| f8dd8f074d | |||
| 5400a48aaf | |||
| 9eb632dc7c | |||
| 7b3a60fad3 | |||
| aec7ec7f61 | |||
| 0a3a16d2b4 | |||
| 54c103aff4 | |||
| d49970590c | |||
| 7af509c7f1 | |||
| 7d92573852 | |||
| 1554ac3b5f | |||
| 2e394eb5f4 | |||
| 4a84514552 | |||
| 259aa90059 | |||
| 52716c813b | |||
| 5ede4c82ac | |||
| 7585d66a07 | |||
| 3eeac20593 | |||
| ce6832a355 | |||
| 0a94d85cd6 | |||
| b01e287527 | |||
| c695ad9d74 | |||
| 4a6cde8859 | |||
| 72df1960e1 | |||
| 9e75a4cf61 | |||
| 92c664b0dc | |||
| f98444490f | |||
| 32189ca273 | |||
| dbffa0d121 | |||
| 73620975d0 | |||
| 5dd6e9a4d4 | |||
| ff62bf47b0 | |||
| d66fe78810 | |||
| 03dc8832ed | |||
| fad227d3fd | |||
| 2f48f41fcb | |||
| 4dd5c23d6e | |||
| 623d9b1f68 | |||
| 3f519b8241 | |||
| 6dc92b6678 | |||
| a682b8e655 | |||
| af23916995 | |||
| 626e64ccfd | |||
| 73ef44fbdb | |||
| 0cf7bff0be | |||
| cddd1869a8 | |||
| c47141bde0 | |||
| bdb45a9c2d | |||
| afc62d8d1c | |||
| 8bbc97c867 | |||
| 0acee94354 | |||
| e93ec2f6a6 | |||
| ecf9bd3870 | |||
| f807c9494b | |||
| 5a04708880 | |||
| aa9fdb4dd0 | |||
| 0c46ef0c3a | |||
| a431b1dda6 | |||
| c1455b6751 | |||
| 654ff3ae00 | |||
| b62b5d48f2 | |||
| 48e889023f | |||
| 354f62f0c5 | |||
| e2626200aa | |||
| 174cbb588f | |||
| 5aa3077c36 | |||
| 539537ddf5 | |||
| 6d820e817b | |||
| 0c0ef7f690 | |||
| 6a74a63ee2 | |||
| 49f7de7089 | |||
| 35c6e915d1 | |||
| 6468c63c81 | |||
| 1c6d7d00d6 | |||
| 0d95231ccb | |||
| c5194a9e5d | |||
| ce22ac2736 | |||
| 8046bb2d9e | |||
| 7be05a6f44 | |||
| 1401f91085 | |||
| 6620b0bfd0 | |||
| dad4a19624 | |||
| 781f243502 | |||
| 30d36e977e | |||
| 4e2477f85f | |||
| 0e73168b7e | |||
| 9c0c49a5f2 | |||
| 8e9aec6904 | |||
| f387558b55 | |||
| f4962c65bc | |||
| 391668f57a | |||
| 8bbdb93cf9 | |||
| 7e9d2c78c6 | |||
| 2d61f70f00 | |||
| 606cbd60a1 | |||
| 8661552e7a | |||
| 8e98c765f8 | |||
| 9d7e21be21 | |||
| 3f0c189e48 | |||
| a2c9f3c6ce | |||
| 888df09879 | |||
| d0dee3cebe | |||
| efbcdce9b9 | |||
| 580ebf41a0 | |||
| 45f62dba0e | |||
| a6114fc424 | |||
| 8642b3c35f | |||
| 74bace156c |
47
.github/workflows/build.yml
vendored
47
.github/workflows/build.yml
vendored
@ -4,6 +4,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
VERSION: ${{ github.ref }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
@ -86,6 +89,50 @@ jobs:
|
||||
path: /tmp/out/*
|
||||
if-no-files-found: error
|
||||
|
||||
bin-image:
|
||||
runs-on: ubuntu-20.04
|
||||
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/cli' }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: dockereng/cli-bin
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=sha
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@v4
|
||||
with:
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
${{ steps.meta.outputs.bake-file }}
|
||||
targets: bin-image-cross
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
set: |
|
||||
*.cache-from=type=gha,scope=bin-image
|
||||
*.cache-to=type=gha,scope=bin-image,mode=max
|
||||
|
||||
prepare-plugins:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@ -44,19 +44,19 @@ jobs:
|
||||
git checkout HEAD^2
|
||||
-
|
||||
name: Update Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: go
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:go"
|
||||
|
||||
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
- connhelper-ssh
|
||||
base:
|
||||
- alpine
|
||||
- bullseye
|
||||
- debian
|
||||
engine-version:
|
||||
# - 20.10-dind # FIXME: Fails on 20.10
|
||||
- stable-dind # TODO: Use 20.10-dind, stable-dind is deprecated
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@ -45,7 +45,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- macos-11
|
||||
- macos-12
|
||||
# - windows-2022 # FIXME: some tests are failing on the Windows runner, as well as on Appveyor since June 24, 2018: https://ci.appveyor.com/project/docker/cli/history
|
||||
steps:
|
||||
-
|
||||
@ -61,9 +61,9 @@ jobs:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.3
|
||||
go-version: 1.21.6
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
@ -3,23 +3,41 @@ linters:
|
||||
- bodyclose
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupword # Detects duplicate words.
|
||||
- durationcheck
|
||||
- errchkjson
|
||||
- exportloopref # Detects pointers to enclosing loop variables.
|
||||
- gocritic # Metalinter; detects bugs, performance, and styling issues.
|
||||
- gocyclo
|
||||
- gofumpt
|
||||
- gofumpt # Detects whether code was gofumpt-ed.
|
||||
- goimports
|
||||
- gosec
|
||||
- gosec # Detects security problems.
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- megacheck
|
||||
- misspell
|
||||
- misspell # Detects commonly misspelled English words in comments.
|
||||
- nakedret
|
||||
- revive
|
||||
- nilerr # Detects code that returns nil even if it checks that the error is not nil.
|
||||
- nolintlint # Detects ill-formed or insufficient nolint directives.
|
||||
- perfsprint # Detects fmt.Sprintf uses that can be replaced with a faster alternative.
|
||||
- prealloc # Detects slice declarations that could potentially be pre-allocated.
|
||||
- predeclared # Detects code that shadows one of Go's predeclared identifiers
|
||||
- reassign
|
||||
- revive # Metalinter; drop-in replacement for golint.
|
||||
- staticcheck
|
||||
- stylecheck # Replacement for golint
|
||||
- tenv # Detects using os.Setenv instead of t.Setenv.
|
||||
- thelper # Detects test helpers without t.Helper().
|
||||
- tparallel # Detects inappropriate usage of t.Parallel().
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unconvert # Detects unnecessary type conversions.
|
||||
- unparam
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- vet
|
||||
- wastedassign
|
||||
|
||||
disable:
|
||||
- errcheck
|
||||
@ -40,13 +58,35 @@ linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 16
|
||||
govet:
|
||||
check-shadowing: false
|
||||
check-shadowing: true
|
||||
settings:
|
||||
shadow:
|
||||
strict: true
|
||||
lll:
|
||||
line-length: 200
|
||||
nakedret:
|
||||
command: nakedret
|
||||
pattern: ^(?P<path>.*?\\.go):(?P<line>\\d+)\\s*(?P<message>.*)$
|
||||
|
||||
revive:
|
||||
rules:
|
||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing
|
||||
- name: import-shadowing
|
||||
severity: warning
|
||||
disabled: false
|
||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
|
||||
- name: empty-block
|
||||
severity: warning
|
||||
disabled: false
|
||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
|
||||
- name: empty-lines
|
||||
severity: warning
|
||||
disabled: false
|
||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#use-any
|
||||
- name: use-any
|
||||
severity: warning
|
||||
disabled: false
|
||||
|
||||
issues:
|
||||
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
|
||||
exclude-use-default: false
|
||||
@ -83,7 +123,7 @@ issues:
|
||||
- gosec
|
||||
# EXC0008
|
||||
# TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close" (gosec)
|
||||
- text: "(G104|G307)"
|
||||
- text: "G307"
|
||||
linters:
|
||||
- gosec
|
||||
# EXC0009
|
||||
@ -97,10 +137,13 @@ issues:
|
||||
|
||||
# G113 Potential uncontrolled memory consumption in Rat.SetString (CVE-2022-23772)
|
||||
# only affects gp < 1.16.14. and go < 1.17.7
|
||||
- text: "(G113)"
|
||||
- text: "G113"
|
||||
linters:
|
||||
- gosec
|
||||
# TODO: G104: Errors unhandled. (gosec)
|
||||
- text: "G104"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# Looks like the match in "EXC0007" above doesn't catch this one
|
||||
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
|
||||
- text: "G204: Subprocess launched with a potential tainted input or cmd arguments"
|
||||
@ -125,6 +168,15 @@ issues:
|
||||
linters:
|
||||
- errcheck
|
||||
- gosec
|
||||
- text: "ST1000: at least one file in a package should have a package comment"
|
||||
linters:
|
||||
- stylecheck
|
||||
|
||||
# Allow "err" and "ok" vars to shadow existing declarations, otherwise we get too many false positives.
|
||||
- text: '^shadow: declaration of "(err|ok)" shadows declaration'
|
||||
linters:
|
||||
- govet
|
||||
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
26
.mailmap
26
.mailmap
@ -22,6 +22,8 @@ Akihiro Matsushima <amatsusbit@gmail.com> <amatsus@users.noreply.github.com>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp> <suda.akihiro@lab.ntt.co.jp>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp> <suda.kyoto@gmail.com>
|
||||
Albin Kerouanton <albinker@gmail.com>
|
||||
Albin Kerouanton <albinker@gmail.com> <albin@akerouanton.name>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksa Sarai <asarai@suse.de> <asarai@suse.com>
|
||||
Aleksa Sarai <asarai@suse.de> <cyphar@cyphar.com>
|
||||
@ -29,6 +31,7 @@ Aleksandrs Fadins <aleks@s-ko.net>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com> <aboch@docker.com>
|
||||
Alex Chen <alexchenunix@gmail.com> <root@localhost.localdomain>
|
||||
Alex Ellis <alexellis2@gmail.com>
|
||||
Alexander Chneerov <achneerov@gmail.com>
|
||||
Alexander Larsson <alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||
Alexander Morozov <lk4d4math@gmail.com>
|
||||
Alexander Morozov <lk4d4math@gmail.com> <lk4d4@docker.com>
|
||||
@ -72,6 +75,9 @@ Bill Wang <ozbillwang@gmail.com> <SydOps@users.noreply.github.com>
|
||||
Bin Liu <liubin0329@gmail.com>
|
||||
Bin Liu <liubin0329@gmail.com> <liubin0329@users.noreply.github.com>
|
||||
Bingshen Wang <bingshen.wbs@alibaba-inc.com>
|
||||
Bjorn Neergaard <bjorn.neergaard@docker.com>
|
||||
Bjorn Neergaard <bjorn.neergaard@docker.com> <bjorn@neersighted.com>
|
||||
Bjorn Neergaard <bjorn.neergaard@docker.com> <bneergaard@mirantis.com>
|
||||
Boaz Shuster <ripcurld.github@gmail.com>
|
||||
Brad Baker <brad@brad.fi>
|
||||
Brad Baker <brad@brad.fi> <88946291+brdbkr@users.noreply.github.com>
|
||||
@ -81,6 +87,7 @@ Brent Salisbury <brent.salisbury@docker.com> <brent@docker.com>
|
||||
Brian Goff <cpuguy83@gmail.com>
|
||||
Brian Goff <cpuguy83@gmail.com> <bgoff@cpuguy83-mbp.home>
|
||||
Brian Goff <cpuguy83@gmail.com> <bgoff@cpuguy83-mbp.local>
|
||||
Brian Tracy <brian.tracy33@gmail.com>
|
||||
Carlos de Paula <me@carlosedp.com>
|
||||
Chad Faragher <wyckster@hotmail.com>
|
||||
Chander Govindarajan <chandergovind@gmail.com>
|
||||
@ -91,6 +98,8 @@ Chen Chuanliang <chen.chuanliang@zte.com.cn>
|
||||
Chen Mingjie <chenmingjie0828@163.com>
|
||||
Chen Qiu <cheney-90@hotmail.com>
|
||||
Chen Qiu <cheney-90@hotmail.com> <21321229@zju.edu.cn>
|
||||
Chris Chinchilla <chris@chrischinchilla.com>
|
||||
Chris Chinchilla <chris@chrischinchilla.com> <chris.ward@docker.com>
|
||||
Chris Dias <cdias@microsoft.com>
|
||||
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Christopher Biscardi <biscarch@sketcht.com>
|
||||
@ -101,6 +110,7 @@ Chun Chen <ramichen@tencent.com> <chenchun.feed@gmail.com>
|
||||
Comical Derskeal <27731088+derskeal@users.noreply.github.com>
|
||||
Corbin Coleman <corbin.coleman@docker.com>
|
||||
Cory Bennet <cbennett@netflix.com>
|
||||
Craig Osterhout <craig.osterhout@docker.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com> <unclejack@users.noreply.github.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com> <unclejacksons@gmail.com>
|
||||
@ -110,6 +120,7 @@ Daehyeok Mun <daehyeok@gmail.com> <daehyeok@daehyeok-ui-MacBook-Air.local>
|
||||
Daehyeok Mun <daehyeok@gmail.com> <daehyeok@daehyeokui-MacBook-Air.local>
|
||||
Daisuke Ito <itodaisuke00@gmail.com>
|
||||
Dan Feldman <danf@jfrog.com>
|
||||
Danial Gharib <danial.mail.gh@gmail.com>
|
||||
Daniel Dao <dqminh@cloudflare.com>
|
||||
Daniel Dao <dqminh@cloudflare.com> <dqminh89@gmail.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
@ -131,6 +142,8 @@ Dave Henderson <dhenderson@gmail.com> <Dave.Henderson@ca.ibm.com>
|
||||
Dave Tucker <dt@docker.com> <dave@dtucker.co.uk>
|
||||
David Alvarez <david.alvarez@flyeralarm.com>
|
||||
David Alvarez <david.alvarez@flyeralarm.com> <busilezas@gmail.com>
|
||||
David Karlsson <david.karlsson@docker.com>
|
||||
David Karlsson <david.karlsson@docker.com> <35727626+dvdksn@users.noreply.github.com>
|
||||
David M. Karr <davidmichaelkarr@gmail.com>
|
||||
David Sheets <dsheets@docker.com> <sheets@alum.mit.edu>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
@ -181,6 +194,8 @@ Gerwim Feiken <g.feiken@tfe.nl> <gerwim@gmail.com>
|
||||
Giampaolo Mancini <giampaolo@trampolineup.com>
|
||||
Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
|
||||
Gou Rao <gou@portworx.com> <gourao@users.noreply.github.com>
|
||||
Graeme Wiebe <graeme.wiebe@gmail.com>
|
||||
Graeme Wiebe <graeme.wiebe@gmail.com> <79593869+TheRealGramdalf@users.noreply.github.com>
|
||||
Greg Stephens <greg@udon.org>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <charmes.guillaume@gmail.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <guillaume.charmes@dotcloud.com>
|
||||
@ -289,7 +304,8 @@ Kelton Bassingthwaite <KeltonBassingthwaite@gmail.com> <github@bassingthwaite.or
|
||||
Ken Cochrane <kencochrane@gmail.com> <KenCochrane@gmail.com>
|
||||
Ken Herner <kherner@progress.com> <chosenken@gmail.com>
|
||||
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
|
||||
Kevin Alvarez <crazy-max@users.noreply.github.com>
|
||||
Kevin Alvarez <github@crazymax.dev>
|
||||
Kevin Alvarez <github@crazymax.dev> <crazy-max@users.noreply.github.com>
|
||||
Kevin Feyrer <kevin.feyrer@btinternet.com> <kevinfeyrer@users.noreply.github.com>
|
||||
Kevin Kern <kaiwentan@harmonycloud.cn>
|
||||
Kevin Meredith <kevin.m.meredith@gmail.com>
|
||||
@ -306,6 +322,7 @@ Kyle Mitofsky <Kylemit@gmail.com>
|
||||
Lajos Papp <lajos.papp@sequenceiq.com> <lalyos@yahoo.com>
|
||||
Lei Jitang <leijitang@huawei.com>
|
||||
Lei Jitang <leijitang@huawei.com> <leijitang@gmail.com>
|
||||
Li Fu Bang <lifubang@acmcoder.com>
|
||||
Liang Mingqiang <mqliang.zju@gmail.com>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Liao Qingwei <liaoqingwei@huawei.com>
|
||||
@ -330,6 +347,7 @@ Mansi Nahar <mmn4185@rit.edu> <mansi.nahar@macbookpro-mansinahar.local>
|
||||
Mansi Nahar <mmn4185@rit.edu> <mansinahar@users.noreply.github.com>
|
||||
Marc Abramowitz <marc@marc-abramowitz.com> <msabramo@gmail.com>
|
||||
Marcelo Horacio Fortino <info@fortinux.com> <fortinux@users.noreply.github.com>
|
||||
Marco Spiess <marco.spiess@hotmail.de>
|
||||
Marcus Linke <marcus.linke@gmx.de>
|
||||
Marianna Tessel <mtesselh@gmail.com>
|
||||
Marius Ileana <marius.ileana@gmail.com>
|
||||
@ -399,6 +417,9 @@ Paul Liljenberg <liljenberg.paul@gmail.com> <letters@paulnotcom.se>
|
||||
Pavel Tikhomirov <ptikhomirov@virtuozzo.com> <ptikhomirov@parallels.com>
|
||||
Pawel Konczalski <mail@konczalski.de>
|
||||
Paweł Pokrywka <pepawel@users.noreply.github.com>
|
||||
Per Lundberg <perlun@gmail.com>
|
||||
Per Lundberg <perlun@gmail.com> <per.lundberg@ecraft.com>
|
||||
Per Lundberg <perlun@gmail.com> <per.lundberg@hibox.tv>
|
||||
Peter Choi <phkchoi89@gmail.com> <reikani@Peters-MacBook-Pro.local>
|
||||
Peter Dave Hello <hsu@peterdavehello.org> <PeterDaveHello@users.noreply.github.com>
|
||||
Peter Hsu <shhsu@microsoft.com>
|
||||
@ -414,6 +435,8 @@ Qiang Huang <h.huangqiang@huawei.com>
|
||||
Qiang Huang <h.huangqiang@huawei.com> <qhuang@10.0.2.15>
|
||||
Ray Tsang <rayt@google.com> <saturnism@users.noreply.github.com>
|
||||
Renaud Gaubert <rgaubert@nvidia.com> <renaud.gaubert@gmail.com>
|
||||
Rob Murray <rob.murray@docker.com>
|
||||
Rob Murray <rob.murray@docker.com> <148866618+robmry@users.noreply.github.com>
|
||||
Robert Terhaar <rterhaar@atlanticdynamic.com> <robbyt@users.noreply.github.com>
|
||||
Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com>
|
||||
Roberto Muñoz Fernández <robertomf@gmail.com> <roberto.munoz.fernandez.contractor@bbva.com>
|
||||
@ -429,6 +452,7 @@ Sandeep Bansal <sabansal@microsoft.com>
|
||||
Sandeep Bansal <sabansal@microsoft.com> <msabansal@microsoft.com>
|
||||
Sandro Jäckel <sandro.jaeckel@gmail.com>
|
||||
Sargun Dhillon <sargun@netflix.com> <sargun@sargun.me>
|
||||
Saurabh Kumar <saurabhkumar0184@gmail.com>
|
||||
Sean Lee <seanlee@tw.ibm.com> <scaleoutsean@users.noreply.github.com>
|
||||
Sebastiaan van Stijn <github@gone.nl> <sebastiaan@ws-key-sebas3.dpi1.dpi>
|
||||
Sebastiaan van Stijn <github@gone.nl> <thaJeztah@users.noreply.github.com>
|
||||
|
||||
46
AUTHORS
46
AUTHORS
@ -2,6 +2,7 @@
|
||||
# This file lists all contributors to the repository.
|
||||
# See scripts/docs/generate-authors.sh to make modifications.
|
||||
|
||||
A. Lester Buck III <github-reg@nbolt.com>
|
||||
Aanand Prasad <aanand.prasad@gmail.com>
|
||||
Aaron L. Xu <liker.xu@foxmail.com>
|
||||
Aaron Lehmann <alehmann@netflix.com>
|
||||
@ -16,6 +17,7 @@ Adolfo Ochagavía <aochagavia92@gmail.com>
|
||||
Adrian Plata <adrian.plata@docker.com>
|
||||
Adrien Duermael <adrien@duermael.com>
|
||||
Adrien Folie <folie.adrien@gmail.com>
|
||||
Adyanth Hosavalike <ahosavalike@ucsd.edu>
|
||||
Ahmet Alp Balkan <ahmetb@microsoft.com>
|
||||
Aidan Feldman <aidan.feldman@gmail.com>
|
||||
Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||
@ -26,7 +28,7 @@ Akim Demaille <akim.demaille@docker.com>
|
||||
Alan Thompson <cloojure@gmail.com>
|
||||
Albert Callarisa <shark234@gmail.com>
|
||||
Alberto Roura <mail@albertoroura.com>
|
||||
Albin Kerouanton <albin@akerouanton.name>
|
||||
Albin Kerouanton <albinker@gmail.com>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksander Piotrowski <apiotrowski312@gmail.com>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com>
|
||||
@ -34,6 +36,7 @@ Alex Couture-Beil <alex@earthly.dev>
|
||||
Alex Mavrogiannis <alex.mavrogiannis@docker.com>
|
||||
Alex Mayer <amayer5125@gmail.com>
|
||||
Alexander Boyd <alex@opengroove.org>
|
||||
Alexander Chneerov <achneerov@gmail.com>
|
||||
Alexander Larsson <alexl@redhat.com>
|
||||
Alexander Morozov <lk4d4math@gmail.com>
|
||||
Alexander Ryabov <i@sepa.spb.ru>
|
||||
@ -41,6 +44,7 @@ Alexandre González <agonzalezro@gmail.com>
|
||||
Alexey Igrychev <alexey.igrychev@flant.com>
|
||||
Alexis Couvreur <alexiscouvreur.pro@gmail.com>
|
||||
Alfred Landrum <alfred.landrum@docker.com>
|
||||
Ali Rostami <rostami.ali@gmail.com>
|
||||
Alicia Lauerman <alicia@eta.im>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com>
|
||||
Alvin Deng <alvin.q.deng@utexas.edu>
|
||||
@ -79,7 +83,9 @@ Arko Dasgupta <arko@tetrate.io>
|
||||
Arnaud Porterie <icecrime@gmail.com>
|
||||
Arnaud Rebillout <elboulangero@gmail.com>
|
||||
Arthur Peka <arthur.peka@outlook.com>
|
||||
Ashly Mathew <ashly.mathew@sap.com>
|
||||
Ashwini Oruganti <ashwini.oruganti@gmail.com>
|
||||
Aslam Ahemad <aslamahemad@gmail.com>
|
||||
Azat Khuyiyakhmetov <shadow_uz@mail.ru>
|
||||
Bardia Keyoumarsi <bkeyouma@ucsc.edu>
|
||||
Barnaby Gray <barnaby@pickle.me.uk>
|
||||
@ -98,7 +104,9 @@ Bill Wang <ozbillwang@gmail.com>
|
||||
Bin Liu <liubin0329@gmail.com>
|
||||
Bingshen Wang <bingshen.wbs@alibaba-inc.com>
|
||||
Bishal Das <bishalhnj127@gmail.com>
|
||||
Bjorn Neergaard <bjorn.neergaard@docker.com>
|
||||
Boaz Shuster <ripcurld.github@gmail.com>
|
||||
Boban Acimovic <boban.acimovic@gmail.com>
|
||||
Bogdan Anton <contact@bogdananton.ro>
|
||||
Boris Pruessmann <boris@pruessmann.org>
|
||||
Brad Baker <brad@brad.fi>
|
||||
@ -109,6 +117,7 @@ Brent Salisbury <brent.salisbury@docker.com>
|
||||
Bret Fisher <bret@bretfisher.com>
|
||||
Brian (bex) Exelbierd <bexelbie@redhat.com>
|
||||
Brian Goff <cpuguy83@gmail.com>
|
||||
Brian Tracy <brian.tracy33@gmail.com>
|
||||
Brian Wieder <brian@4wieders.com>
|
||||
Bruno Sousa <bruno.sousa@docker.com>
|
||||
Bryan Bess <squarejaw@bsbess.com>
|
||||
@ -136,6 +145,7 @@ Chen Chuanliang <chen.chuanliang@zte.com.cn>
|
||||
Chen Hanxiao <chenhanxiao@cn.fujitsu.com>
|
||||
Chen Mingjie <chenmingjie0828@163.com>
|
||||
Chen Qiu <cheney-90@hotmail.com>
|
||||
Chris Chinchilla <chris@chrischinchilla.com>
|
||||
Chris Couzens <ccouzens@gmail.com>
|
||||
Chris Gavin <chris@chrisgavin.me>
|
||||
Chris Gibson <chris@chrisg.io>
|
||||
@ -163,6 +173,8 @@ Conner Crosby <conner@cavcrosby.tech>
|
||||
Corey Farrell <git@cfware.com>
|
||||
Corey Quon <corey.quon@docker.com>
|
||||
Cory Bennet <cbennett@netflix.com>
|
||||
Cory Snider <csnider@mirantis.com>
|
||||
Craig Osterhout <craig.osterhout@docker.com>
|
||||
Craig Wilhite <crwilhit@microsoft.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com>
|
||||
Daehyeok Mun <daehyeok@gmail.com>
|
||||
@ -171,6 +183,7 @@ Daisuke Ito <itodaisuke00@gmail.com>
|
||||
dalanlan <dalanlan925@gmail.com>
|
||||
Damien Nadé <github@livna.org>
|
||||
Dan Cotora <dan@bluevision.ro>
|
||||
Danial Gharib <danial.mail.gh@gmail.com>
|
||||
Daniel Artine <daniel.artine@ufrj.br>
|
||||
Daniel Cassidy <mail@danielcassidy.me.uk>
|
||||
Daniel Dao <dqminh@cloudflare.com>
|
||||
@ -210,6 +223,7 @@ Denis Defreyne <denis@soundcloud.com>
|
||||
Denis Gladkikh <denis@gladkikh.email>
|
||||
Denis Ollier <larchunix@users.noreply.github.com>
|
||||
Dennis Docter <dennis@d23.nl>
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
Derek McGowan <derek@mcg.dev>
|
||||
Des Preston <despreston@gmail.com>
|
||||
Deshi Xiao <dxiao@redhat.com>
|
||||
@ -232,11 +246,13 @@ DongGeon Lee <secmatth1996@gmail.com>
|
||||
Doug Davis <dug@us.ibm.com>
|
||||
Drew Erny <derny@mirantis.com>
|
||||
Ed Costello <epc@epcostello.com>
|
||||
Ed Morley <501702+edmorley@users.noreply.github.com>
|
||||
Elango Sivanandam <elango.siva@docker.com>
|
||||
Eli Uriegas <eli.uriegas@docker.com>
|
||||
Eli Uriegas <seemethere101@gmail.com>
|
||||
Elias Faxö <elias.faxo@tre.se>
|
||||
Elliot Luo <956941328@qq.com>
|
||||
Eric Bode <eric.bode@foundries.io>
|
||||
Eric Curtin <ericcurtin17@gmail.com>
|
||||
Eric Engestrom <eric@engestrom.ch>
|
||||
Eric G. Noriega <enoriega@vizuri.com>
|
||||
@ -254,6 +270,7 @@ Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||
Evan Allrich <evan@unguku.com>
|
||||
Evan Hazlett <ejhazlett@gmail.com>
|
||||
Evan Krall <krall@yelp.com>
|
||||
Evan Lezar <elezar@nvidia.com>
|
||||
Evelyn Xu <evelynhsu21@gmail.com>
|
||||
Everett Toews <everett.toews@rackspace.com>
|
||||
Fabio Falci <fabiofalci@gmail.com>
|
||||
@ -275,6 +292,7 @@ Frederik Nordahl Jul Sabroe <frederikns@gmail.com>
|
||||
Frieder Bluemle <frieder.bluemle@gmail.com>
|
||||
Gabriel Gore <gabgore@cisco.com>
|
||||
Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com>
|
||||
Gabriela Georgieva <gabriela.georgieva@docker.com>
|
||||
Gaetan de Villele <gdevillele@gmail.com>
|
||||
Gang Qiao <qiaohai8866@gmail.com>
|
||||
Gary Schaetz <gary@schaetzkc.com>
|
||||
@ -288,6 +306,7 @@ Gleb Stsenov <gleb.stsenov@gmail.com>
|
||||
Goksu Toprak <goksu.toprak@docker.com>
|
||||
Gou Rao <gou@portworx.com>
|
||||
Govind Rai <raigovind93@gmail.com>
|
||||
Graeme Wiebe <graeme.wiebe@gmail.com>
|
||||
Grant Reaber <grant.reaber@gmail.com>
|
||||
Greg Pflaum <gpflaum@users.noreply.github.com>
|
||||
Gsealy <jiaojingwei1001@hotmail.com>
|
||||
@ -311,6 +330,7 @@ Hernan Garcia <hernandanielg@gmail.com>
|
||||
Hongbin Lu <hongbin034@gmail.com>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Huayi Zhang <irachex@gmail.com>
|
||||
Hugo Chastel <Hugo-C@users.noreply.github.com>
|
||||
Hugo Gabriel Eyherabide <hugogabriel.eyherabide@gmail.com>
|
||||
huqun <huqun@zju.edu.cn>
|
||||
Huu Nguyen <huu@prismskylabs.com>
|
||||
@ -329,9 +349,12 @@ Ivan Grund <ivan.grund@gmail.com>
|
||||
Ivan Markin <sw@nogoegst.net>
|
||||
Jacob Atzen <jacob@jacobatzen.dk>
|
||||
Jacob Tomlinson <jacob@tom.linson.uk>
|
||||
Jacopo Rigoli <rigoli.jacopo@gmail.com>
|
||||
Jaivish Kothari <janonymous.codevulture@gmail.com>
|
||||
Jake Lambert <jake.lambert@volusion.com>
|
||||
Jake Sanders <jsand@google.com>
|
||||
Jake Stokes <contactjake@developerjake.com>
|
||||
Jakub Panek <me@panekj.dev>
|
||||
James Nesbitt <james.nesbitt@wunderkraut.com>
|
||||
James Turnbull <james@lovedthanlost.net>
|
||||
Jamie Hannaford <jamie@limetree.org>
|
||||
@ -408,10 +431,12 @@ Josh Chorlton <jchorlton@gmail.com>
|
||||
Josh Hawn <josh.hawn@docker.com>
|
||||
Josh Horwitz <horwitz@addthis.com>
|
||||
Josh Soref <jsoref@gmail.com>
|
||||
Julian <gitea+julian@ic.thejulian.uk>
|
||||
Julien Barbier <write0@gmail.com>
|
||||
Julien Kassar <github@kassisol.com>
|
||||
Julien Maitrehenry <julien.maitrehenry@me.com>
|
||||
Justas Brazauskas <brazauskasjustas@gmail.com>
|
||||
Justin Chadwell <me@jedevc.com>
|
||||
Justin Cormack <justin.cormack@docker.com>
|
||||
Justin Simonelis <justin.p.simonelis@gmail.com>
|
||||
Justyn Temme <justyntemme@gmail.com>
|
||||
@ -434,7 +459,7 @@ Kelton Bassingthwaite <KeltonBassingthwaite@gmail.com>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
||||
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
|
||||
Kevin Alvarez <crazy-max@users.noreply.github.com>
|
||||
Kevin Alvarez <github@crazymax.dev>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Kevin Feyrer <kevin.feyrer@btinternet.com>
|
||||
Kevin Kern <kaiwentan@harmonycloud.cn>
|
||||
@ -454,6 +479,7 @@ Kyle Mitofsky <Kylemit@gmail.com>
|
||||
Lachlan Cooper <lachlancooper@gmail.com>
|
||||
Lai Jiangshan <jiangshanlai@gmail.com>
|
||||
Lars Kellogg-Stedman <lars@redhat.com>
|
||||
Laura Brehm <laurabrehm@hey.com>
|
||||
Laura Frank <ljfrank@gmail.com>
|
||||
Laurent Erignoux <lerignoux@gmail.com>
|
||||
Lee Gaines <eightlimbed@gmail.com>
|
||||
@ -462,10 +488,10 @@ Lennie <github@consolejunkie.net>
|
||||
Leo Gallucci <elgalu3@gmail.com>
|
||||
Leonid Skorospelov <leosko94@gmail.com>
|
||||
Lewis Daly <lewisdaly@me.com>
|
||||
Li Fu Bang <lifubang@acmcoder.com>
|
||||
Li Yi <denverdino@gmail.com>
|
||||
Li Yi <weiyuan.yl@alibaba-inc.com>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Lifubang <lifubang@acmcoder.com>
|
||||
Lihua Tang <lhtang@alauda.io>
|
||||
Lily Guo <lily.guo@docker.com>
|
||||
Lin Lu <doraalin@163.com>
|
||||
@ -480,6 +506,7 @@ Louis Opter <kalessin@kalessin.fr>
|
||||
Luca Favatella <luca.favatella@erlang-solutions.com>
|
||||
Luca Marturana <lucamarturana@gmail.com>
|
||||
Lucas Chan <lucas-github@lucaschan.com>
|
||||
Luis Henrique Mulinari <luis.mulinari@gmail.com>
|
||||
Luka Hartwig <mail@lukahartwig.de>
|
||||
Lukas Heeren <lukas-heeren@hotmail.com>
|
||||
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
|
||||
@ -498,6 +525,7 @@ mapk0y <mapk0y@gmail.com>
|
||||
Marc Bihlmaier <marc.bihlmaier@reddoxx.com>
|
||||
Marc Cornellà <hello@mcornella.com>
|
||||
Marco Mariani <marco.mariani@alterway.fr>
|
||||
Marco Spiess <marco.spiess@hotmail.de>
|
||||
Marco Vedovati <mvedovati@suse.com>
|
||||
Marcus Martins <marcus@docker.com>
|
||||
Marianna Tessel <mtesselh@gmail.com>
|
||||
@ -522,6 +550,7 @@ Max Shytikov <mshytikov@gmail.com>
|
||||
Maxime Petazzoni <max@signalfuse.com>
|
||||
Maximillian Fan Xavier <maximillianfx@gmail.com>
|
||||
Mei ChunTao <mei.chuntao@zte.com.cn>
|
||||
Melroy van den Berg <melroy@melroy.org>
|
||||
Metal <2466052+tedhexaflow@users.noreply.github.com>
|
||||
Micah Zoltu <micah@newrelic.com>
|
||||
Michael A. Smith <michael@smith-li.com>
|
||||
@ -593,6 +622,7 @@ Nishant Totla <nishanttotla@gmail.com>
|
||||
NIWA Hideyuki <niwa.niwa@nifty.ne.jp>
|
||||
Noah Treuhaft <noah.treuhaft@docker.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
Oded Arbel <oded@geek.co.il>
|
||||
Odin Ugedal <odin@ugedal.com>
|
||||
ohmystack <jun.jiang02@ele.me>
|
||||
OKA Naoya <git@okanaoya.com>
|
||||
@ -604,19 +634,21 @@ Otto Kekäläinen <otto@seravo.fi>
|
||||
Ovidio Mallo <ovidio.mallo@gmail.com>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Patrick Böänziger <patrick.baenziger@bsi-software.com>
|
||||
Patrick Daigle <114765035+pdaig@users.noreply.github.com>
|
||||
Patrick Hemmer <patrick.hemmer@gmail.com>
|
||||
Patrick Lang <plang@microsoft.com>
|
||||
Paul <paul9869@gmail.com>
|
||||
Paul Kehrer <paul.l.kehrer@gmail.com>
|
||||
Paul Lietar <paul@lietar.net>
|
||||
Paul Mulders <justinkb@gmail.com>
|
||||
Paul Seyfert <pseyfert.mathphys@gmail.com>
|
||||
Paul Weaver <pauweave@cisco.com>
|
||||
Pavel Pospisil <pospispa@gmail.com>
|
||||
Paweł Gronowski <pawel.gronowski@docker.com>
|
||||
Paweł Pokrywka <pepawel@users.noreply.github.com>
|
||||
Paweł Szczekutowicz <pszczekutowicz@gmail.com>
|
||||
Peeyush Gupta <gpeeyush@linux.vnet.ibm.com>
|
||||
Per Lundberg <per.lundberg@ecraft.com>
|
||||
Per Lundberg <perlun@gmail.com>
|
||||
Peter Dave Hello <hsu@peterdavehello.org>
|
||||
Peter Edge <peter.edge@gmail.com>
|
||||
Peter Hsu <shhsu@microsoft.com>
|
||||
@ -639,6 +671,7 @@ Preston Cowley <preston.cowley@sony.com>
|
||||
Pure White <daniel48@126.com>
|
||||
Qiang Huang <h.huangqiang@huawei.com>
|
||||
Qinglan Peng <qinglanpeng@zju.edu.cn>
|
||||
QQ喵 <gqqnb2005@gmail.com>
|
||||
qudongfang <qudongfang@gmail.com>
|
||||
Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com>
|
||||
Rahul Kadyan <hi@znck.me>
|
||||
@ -657,6 +690,7 @@ Rick Wieman <git@rickw.nl>
|
||||
Ritesh H Shukla <sritesh@vmware.com>
|
||||
Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
|
||||
Rob Gulewich <rgulewich@netflix.com>
|
||||
Rob Murray <rob.murray@docker.com>
|
||||
Robert Wallis <smilingrob@gmail.com>
|
||||
Robin Naundorf <r.naundorf@fh-muenster.de>
|
||||
Robin Speekenbrink <robin@kingsquare.nl>
|
||||
@ -689,6 +723,7 @@ Sandro Jäckel <sandro.jaeckel@gmail.com>
|
||||
Santhosh Manohar <santhosh@docker.com>
|
||||
Sargun Dhillon <sargun@netflix.com>
|
||||
Saswat Bhattacharya <sas.saswat@gmail.com>
|
||||
Saurabh Kumar <saurabhkumar0184@gmail.com>
|
||||
Scott Brenner <scott@scottbrenner.me>
|
||||
Scott Collier <emailscottcollier@gmail.com>
|
||||
Sean Christopherson <sean.j.christopherson@intel.com>
|
||||
@ -788,6 +823,7 @@ uhayate <uhayate.gong@daocloud.io>
|
||||
Ulrich Bareth <ulrich.bareth@gmail.com>
|
||||
Ulysses Souza <ulysses.souza@docker.com>
|
||||
Umesh Yadav <umesh4257@gmail.com>
|
||||
Vaclav Struhar <struharv@gmail.com>
|
||||
Valentin Lorentz <progval+git@progval.net>
|
||||
Vardan Pogosian <vardan.pogosyan@gmail.com>
|
||||
Venkateswara Reddy Bukkasamudram <bukkasamudram@outlook.com>
|
||||
@ -795,6 +831,7 @@ Veres Lajos <vlajos@gmail.com>
|
||||
Victor Vieux <victor.vieux@docker.com>
|
||||
Victoria Bialas <victoria.bialas@docker.com>
|
||||
Viktor Stanchev <me@viktorstanchev.com>
|
||||
Ville Skyttä <ville.skytta@iki.fi>
|
||||
Vimal Raghubir <vraghubir0418@gmail.com>
|
||||
Vincent Batts <vbatts@redhat.com>
|
||||
Vincent Bernat <Vincent.Bernat@exoscale.ch>
|
||||
@ -831,6 +868,7 @@ Yong Tang <yong.tang.github@outlook.com>
|
||||
Yosef Fertel <yfertel@gmail.com>
|
||||
Yu Peng <yu.peng36@zte.com.cn>
|
||||
Yuan Sun <sunyuan3@huawei.com>
|
||||
Yucheng Wu <wyc123wyc@gmail.com>
|
||||
Yue Zhang <zy675793960@yeah.net>
|
||||
Yunxiang Huang <hyxqshk@vip.qq.com>
|
||||
Zachary Romero <zacromero3@gmail.com>
|
||||
|
||||
30
Dockerfile
30
Dockerfile
@ -1,13 +1,15 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_VARIANT=alpine
|
||||
ARG GO_VERSION=1.21.3
|
||||
ARG ALPINE_VERSION=3.17
|
||||
ARG ALPINE_VERSION=3.18
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.21.6
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG GOVERSIONINFO_VERSION=v1.3.0
|
||||
ARG GOTESTSUM_VERSION=v1.10.0
|
||||
ARG BUILDX_VERSION=0.11.2
|
||||
ARG COMPOSE_VERSION=v2.22.0
|
||||
ARG BUILDX_VERSION=0.12.1
|
||||
ARG COMPOSE_VERSION=v2.24.2
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
@ -22,22 +24,15 @@ ARG TARGETPLATFORM
|
||||
# gcc is installed for libgcc only
|
||||
RUN xx-apk add --no-cache musl-dev gcc
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bullseye AS build-base-bullseye
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO} AS build-base-debian
|
||||
ENV GOTOOLCHAIN=local
|
||||
COPY --link --from=xx / /
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y bash clang lld llvm file
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
FROM build-base-bullseye AS build-bullseye
|
||||
FROM build-base-debian AS build-debian
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apt-get install --no-install-recommends -y libc6-dev libgcc-10-dev
|
||||
# workaround for issue with llvm 11 for darwin/amd64 platform:
|
||||
# # github.com/docker/cli/cmd/docker
|
||||
# /usr/local/go/pkg/tool/linux_amd64/link: /usr/local/go/pkg/tool/linux_amd64/link: running strip failed: exit status 1
|
||||
# llvm-strip: error: unsupported load command (cmd=0x5)
|
||||
# more info: https://github.com/docker/cli/pull/3717
|
||||
# FIXME: remove once llvm 12 available on debian
|
||||
RUN [ "$TARGETPLATFORM" != "darwin/amd64" ] || ln -sfnT /bin/true /usr/bin/llvm-strip
|
||||
RUN xx-apt-get install --no-install-recommends -y libc6-dev libgcc-12-dev pkgconf
|
||||
|
||||
FROM build-base-${BASE_VARIANT} AS goversioninfo
|
||||
ARG GOVERSIONINFO_VERSION
|
||||
@ -66,8 +61,6 @@ ARG VERSION
|
||||
# PACKAGER_NAME sets the company that produced the windows binary
|
||||
ARG PACKAGER_NAME
|
||||
COPY --link --from=goversioninfo /out/goversioninfo /usr/bin/goversioninfo
|
||||
# in bullseye arm64 target does not link with lld so configure it to use ld instead
|
||||
RUN [ ! -f /etc/alpine-release ] && xx-info is-cross && [ "$(xx-info arch)" = "arm64" ] && XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple || true
|
||||
RUN --mount=type=bind,target=.,ro \
|
||||
--mount=type=cache,target=/root/.cache \
|
||||
--mount=from=dockercore/golang-cross:xx-sdk-extras,target=/xx-sdk,src=/xx-sdk \
|
||||
@ -103,7 +96,7 @@ RUN --mount=ro --mount=type=cache,target=/root/.cache \
|
||||
FROM build-base-alpine AS e2e-base-alpine
|
||||
RUN apk add --no-cache build-base curl openssl openssh-client
|
||||
|
||||
FROM build-base-bullseye AS e2e-base-bullseye
|
||||
FROM build-base-debian AS e2e-base-debian
|
||||
RUN apt-get update && apt-get install -y build-essential curl openssl openssh-client
|
||||
|
||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||
@ -130,5 +123,8 @@ COPY --link . .
|
||||
FROM scratch AS plugins
|
||||
COPY --from=build-plugins /out .
|
||||
|
||||
FROM scratch AS bin-image
|
||||
COPY --from=build /out/docker /docker
|
||||
|
||||
FROM scratch AS binary
|
||||
COPY --from=build /out .
|
||||
|
||||
@ -8,8 +8,7 @@
|
||||
|
||||
## About
|
||||
|
||||
This repository is the home of the cli used in the Docker CE and
|
||||
Docker EE products.
|
||||
This repository is the home of the Docker CLI.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@ -45,8 +45,8 @@ func main() {
|
||||
}
|
||||
|
||||
var (
|
||||
who, context string
|
||||
preRun, debug bool
|
||||
who, optContext string
|
||||
preRun, debug bool
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "helloworld",
|
||||
@ -65,7 +65,7 @@ func main() {
|
||||
fmt.Fprintf(dockerCli.Err(), "Plugin debug mode enabled")
|
||||
}
|
||||
|
||||
switch context {
|
||||
switch optContext {
|
||||
case "Christmas":
|
||||
fmt.Fprintf(dockerCli.Out(), "Merry Christmas!\n")
|
||||
return nil
|
||||
@ -92,7 +92,7 @@ func main() {
|
||||
// These are intended to deliberately clash with the CLIs own top
|
||||
// level arguments.
|
||||
flags.BoolVarP(&debug, "debug", "D", false, "Enable debug")
|
||||
flags.StringVarP(&context, "context", "c", "", "Is it Christmas?")
|
||||
flags.StringVarP(&optContext, "context", "c", "", "Is it Christmas?")
|
||||
|
||||
cmd.AddCommand(goodbye, apiversion, exitStatus2)
|
||||
return cmd
|
||||
|
||||
@ -75,13 +75,14 @@ func TestValidateCandidate(t *testing.T) {
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p, err := newPlugin(tc.c, fakeroot.Commands())
|
||||
if tc.err != "" {
|
||||
switch {
|
||||
case tc.err != "":
|
||||
assert.ErrorContains(t, err, tc.err)
|
||||
} else if tc.invalid != "" {
|
||||
case tc.invalid != "":
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, cmp.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
|
||||
assert.ErrorContains(t, p.Err, tc.invalid)
|
||||
} else {
|
||||
default:
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, NamePrefix+p.Name, goodPluginName)
|
||||
assert.Equal(t, p.SchemaVersion, "0.1.0")
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
@ -43,6 +46,6 @@ func wrapAsPluginError(err error, msg string) error {
|
||||
|
||||
// NewPluginError creates a new pluginError, analogous to
|
||||
// errors.Errorf.
|
||||
func NewPluginError(msg string, args ...interface{}) error {
|
||||
func NewPluginError(msg string, args ...any) error {
|
||||
return &pluginError{cause: errors.Errorf(msg, args...)}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@ -42,10 +43,10 @@ func IsNotFound(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func getPluginDirs(dockerCli command.Cli) ([]string, error) {
|
||||
func getPluginDirs(cfg *configfile.ConfigFile) ([]string, error) {
|
||||
var pluginDirs []string
|
||||
|
||||
if cfg := dockerCli.ConfigFile(); cfg != nil {
|
||||
if cfg != nil {
|
||||
pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...)
|
||||
}
|
||||
pluginDir, err := config.Path("cli-plugins")
|
||||
@ -108,7 +109,7 @@ func listPluginCandidates(dirs []string) (map[string][]string, error) {
|
||||
|
||||
// GetPlugin returns a plugin on the system by its name
|
||||
func GetPlugin(name string, dockerCli command.Cli, rootcmd *cobra.Command) (*Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -138,7 +139,7 @@ func GetPlugin(name string, dockerCli command.Cli, rootcmd *cobra.Command) (*Plu
|
||||
|
||||
// ListPlugins produces a list of the plugins available on the system
|
||||
func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -198,7 +199,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
exename := addExeSuffix(NamePrefix + name)
|
||||
pluginDirs, err := getPluginDirs(dockerCli)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ func TestListPluginCandidates(t *testing.T) {
|
||||
)
|
||||
defer dir.Remove()
|
||||
|
||||
var dirs []string
|
||||
dirs := make([]string, 0, 6)
|
||||
for _, d := range []string{"plugins1", "nonexistent", "plugins2", "plugins3", "plugins4", "plugins5"} {
|
||||
dirs = append(dirs, dir.Join(d))
|
||||
}
|
||||
@ -149,7 +149,7 @@ func TestGetPluginDirs(t *testing.T) {
|
||||
expected := append([]string{pluginDir}, defaultSystemPluginDirs...)
|
||||
|
||||
var pluginDirs []string
|
||||
pluginDirs, err = getPluginDirs(cli)
|
||||
pluginDirs, err = getPluginDirs(cli.ConfigFile())
|
||||
assert.Equal(t, strings.Join(expected, ":"), strings.Join(pluginDirs, ":"))
|
||||
assert.NilError(t, err)
|
||||
|
||||
@ -160,7 +160,7 @@ func TestGetPluginDirs(t *testing.T) {
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
CLIPluginsExtraDirs: extras,
|
||||
})
|
||||
pluginDirs, err = getPluginDirs(cli)
|
||||
pluginDirs, err = getPluginDirs(cli.ConfigFile())
|
||||
assert.DeepEqual(t, expected, pluginDirs)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -8,6 +9,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/socket"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
"github.com/docker/docker/client"
|
||||
@ -29,10 +31,21 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
|
||||
tcmd := newPluginCommand(dockerCli, plugin, meta)
|
||||
|
||||
var persistentPreRunOnce sync.Once
|
||||
PersistentPreRunE = func(_ *cobra.Command, _ []string) error {
|
||||
PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
|
||||
var err error
|
||||
persistentPreRunOnce.Do(func() {
|
||||
var opts []command.InitializeOpt
|
||||
cmdContext := cmd.Context()
|
||||
// TODO: revisit and make sure this check makes sense
|
||||
// see: https://github.com/docker/cli/pull/4599#discussion_r1422487271
|
||||
if cmdContext == nil {
|
||||
cmdContext = context.TODO()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(cmdContext)
|
||||
cmd.SetContext(ctx)
|
||||
// Set up the context to cancel based on signalling via CLI socket.
|
||||
socket.ConnectAndWait(cancel)
|
||||
|
||||
var opts []command.CLIOption
|
||||
if os.Getenv("DOCKER_CLI_PLUGIN_USE_DIAL_STDIO") != "" {
|
||||
opts = append(opts, withPluginClientConn(plugin.Name()))
|
||||
}
|
||||
@ -78,7 +91,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
|
||||
}
|
||||
}
|
||||
|
||||
func withPluginClientConn(name string) command.InitializeOpt {
|
||||
func withPluginClientConn(name string) command.CLIOption {
|
||||
return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) {
|
||||
cmd := "docker"
|
||||
if x := os.Getenv(manager.ReexecEnvvar); x != "" {
|
||||
|
||||
72
cli-plugins/socket/socket.go
Normal file
72
cli-plugins/socket/socket.go
Normal file
@ -0,0 +1,72 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/docker/distribution/uuid"
|
||||
)
|
||||
|
||||
// EnvKey represents the well-known environment variable used to pass the plugin being
|
||||
// executed the socket name it should listen on to coordinate with the host CLI.
|
||||
const EnvKey = "DOCKER_CLI_PLUGIN_SOCKET"
|
||||
|
||||
// SetupConn sets up a Unix socket listener, establishes a goroutine to handle connections
|
||||
// and update the conn pointer, and returns the listener for the socket (which the caller
|
||||
// is responsible for closing when it's no longer needed).
|
||||
func SetupConn(conn **net.UnixConn) (*net.UnixListener, error) {
|
||||
listener, err := listen("docker_cli_" + uuid.Generate().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accept(listener, conn)
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func accept(listener *net.UnixListener, conn **net.UnixConn) {
|
||||
go func() {
|
||||
for {
|
||||
// ignore error here, if we failed to accept a connection,
|
||||
// conn is nil and we fallback to previous behavior
|
||||
*conn, _ = listener.AcceptUnix()
|
||||
// perform any platform-specific actions on accept (e.g. unlink non-abstract sockets)
|
||||
onAccept(*conn, listener)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// ConnectAndWait connects to the socket passed via well-known env var,
|
||||
// if present, and attempts to read from it until it receives an EOF, at which
|
||||
// point cb is called.
|
||||
func ConnectAndWait(cb func()) {
|
||||
socketAddr, ok := os.LookupEnv(EnvKey)
|
||||
if !ok {
|
||||
// if a plugin compiled against a more recent version of docker/cli
|
||||
// is executed by an older CLI binary, ignore missing environment
|
||||
// variable and behave as usual
|
||||
return
|
||||
}
|
||||
addr, err := net.ResolveUnixAddr("unix", socketAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.DialUnix("unix", nil, addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
b := make([]byte, 1)
|
||||
for {
|
||||
_, err := conn.Read(b)
|
||||
if errors.Is(err, io.EOF) {
|
||||
cb()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
19
cli-plugins/socket/socket_darwin.go
Normal file
19
cli-plugins/socket/socket_darwin.go
Normal file
@ -0,0 +1,19 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func listen(socketname string) (*net.UnixListener, error) {
|
||||
return net.ListenUnix("unix", &net.UnixAddr{
|
||||
Name: filepath.Join(os.TempDir(), socketname),
|
||||
Net: "unix",
|
||||
})
|
||||
}
|
||||
|
||||
func onAccept(conn *net.UnixConn, listener *net.UnixListener) {
|
||||
syscall.Unlink(listener.Addr().String())
|
||||
}
|
||||
19
cli-plugins/socket/socket_nodarwin.go
Normal file
19
cli-plugins/socket/socket_nodarwin.go
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build !darwin
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func listen(socketname string) (*net.UnixListener, error) {
|
||||
return net.ListenUnix("unix", &net.UnixAddr{
|
||||
Name: "@" + socketname,
|
||||
Net: "unix",
|
||||
})
|
||||
}
|
||||
|
||||
func onAccept(conn *net.UnixConn, listener *net.UnixListener) {
|
||||
// do nothing
|
||||
// while on darwin we would unlink here; on non-darwin the socket is abstract and not present on the filesystem
|
||||
}
|
||||
@ -176,7 +176,7 @@ func (tcmd *TopLevelCommand) HandleGlobalFlags() (*cobra.Command, []string, erro
|
||||
}
|
||||
|
||||
// Initialize finalises global option parsing and initializes the docker client.
|
||||
func (tcmd *TopLevelCommand) Initialize(ops ...command.InitializeOpt) error {
|
||||
func (tcmd *TopLevelCommand) Initialize(ops ...command.CLIOption) error {
|
||||
tcmd.opts.SetDefaultOptions(tcmd.flags)
|
||||
return tcmd.dockerCli.Initialize(tcmd.opts, ops...)
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "Remove build cache",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
spaceReclaimed, output, err := runPrune(dockerCli, options)
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCli, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -58,7 +58,7 @@ const (
|
||||
allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
|
||||
)
|
||||
|
||||
func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
|
||||
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
|
||||
pruneFilters := options.filter.Value()
|
||||
pruneFilters = command.PruneFilters(dockerCli, pruneFilters)
|
||||
|
||||
@ -70,7 +70,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().BuildCachePrune(context.Background(), types.BuildCachePruneOptions{
|
||||
report, err := dockerCli.Client().BuildCachePrune(ctx, types.BuildCachePruneOptions{
|
||||
All: options.all,
|
||||
KeepStorage: options.keepStorage.Value(),
|
||||
Filters: pruneFilters,
|
||||
@ -93,6 +93,6 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
}
|
||||
|
||||
// CachePrune executes a prune command for build cache
|
||||
func CachePrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(dockerCli, pruneOptions{force: true, all: all, filter: filter})
|
||||
func CachePrune(ctx context.Context, dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(ctx, dockerCli, pruneOptions{force: true, all: all, filter: filter})
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
opts.checkpoint = args[1]
|
||||
return runCreate(dockerCli, opts)
|
||||
return runCreate(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
@ -40,8 +40,8 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli command.Cli, opts createOptions) error {
|
||||
err := dockerCli.Client().CheckpointCreate(context.Background(), opts.container, checkpoint.CreateOptions{
|
||||
func runCreate(ctx context.Context, dockerCli command.Cli, opts createOptions) error {
|
||||
err := dockerCli.Client().CheckpointCreate(ctx, opts.container, checkpoint.CreateOptions{
|
||||
CheckpointID: opts.checkpoint,
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
Exit: !opts.leaveRunning,
|
||||
|
||||
@ -12,8 +12,7 @@ const (
|
||||
|
||||
// NewFormat returns a format for use with a checkpoint Context
|
||||
func NewFormat(source string) formatter.Format {
|
||||
switch source {
|
||||
case formatter.TableFormatKey:
|
||||
if source == formatter.TableFormatKey {
|
||||
return defaultCheckpointFormat
|
||||
}
|
||||
return formatter.Format(source)
|
||||
|
||||
@ -24,7 +24,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "List checkpoints for a container",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, args[0], opts)
|
||||
return runList(cmd.Context(), dockerCli, args[0], opts)
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
}
|
||||
@ -35,8 +35,8 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli command.Cli, container string, opts listOptions) error {
|
||||
checkpoints, err := dockerCli.Client().CheckpointList(context.Background(), container, checkpoint.ListOptions{
|
||||
func runList(ctx context.Context, dockerCli command.Cli, container string, opts listOptions) error {
|
||||
checkpoints, err := dockerCli.Client().CheckpointList(ctx, container, checkpoint.ListOptions{
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@ -22,7 +22,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "Remove a checkpoint",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(dockerCli, args[0], args[1], opts)
|
||||
return runRemove(cmd.Context(), dockerCli, args[0], args[1], opts)
|
||||
},
|
||||
}
|
||||
|
||||
@ -32,8 +32,8 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli command.Cli, container string, checkpointID string, opts removeOptions) error {
|
||||
return dockerCli.Client().CheckpointDelete(context.Background(), container, checkpoint.DeleteOptions{
|
||||
func runRemove(ctx context.Context, dockerCli command.Cli, container string, checkpointID string, opts removeOptions) error {
|
||||
return dockerCli.Client().CheckpointDelete(ctx, container, checkpoint.DeleteOptions{
|
||||
CheckpointID: checkpointID,
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
})
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -49,7 +52,7 @@ type Cli interface {
|
||||
Client() client.APIClient
|
||||
Streams
|
||||
SetIn(in *streams.In)
|
||||
Apply(ops ...DockerCliOption) error
|
||||
Apply(ops ...CLIOption) error
|
||||
ConfigFile() *configfile.ConfigFile
|
||||
ServerInfo() ServerInfo
|
||||
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
|
||||
@ -82,6 +85,11 @@ type DockerCli struct {
|
||||
dockerEndpoint docker.Endpoint
|
||||
contextStoreConfig store.Config
|
||||
initTimeout time.Duration
|
||||
|
||||
// baseCtx is the base context used for internal operations. In the future
|
||||
// this may be replaced by explicitly passing a context to functions that
|
||||
// need it.
|
||||
baseCtx context.Context
|
||||
}
|
||||
|
||||
// DefaultVersion returns api.defaultVersion.
|
||||
@ -194,11 +202,8 @@ func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.Registry
|
||||
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
|
||||
}
|
||||
|
||||
// InitializeOpt is the type of the functional options passed to DockerCli.Initialize
|
||||
type InitializeOpt func(dockerCli *DockerCli) error
|
||||
|
||||
// WithInitializeClient is passed to DockerCli.Initialize by callers who wish to set a particular API Client for use by the CLI.
|
||||
func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClient, error)) InitializeOpt {
|
||||
func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClient, error)) CLIOption {
|
||||
return func(dockerCli *DockerCli) error {
|
||||
var err error
|
||||
dockerCli.client, err = makeClient(dockerCli)
|
||||
@ -208,7 +213,7 @@ func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClien
|
||||
|
||||
// Initialize the dockerCli runs initialization that must happen after command
|
||||
// line flags are parsed.
|
||||
func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...InitializeOpt) error {
|
||||
func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption) error {
|
||||
for _, o := range ops {
|
||||
if err := o(cli); err != nil {
|
||||
return err
|
||||
@ -323,8 +328,7 @@ func (cli *DockerCli) getInitTimeout() time.Duration {
|
||||
}
|
||||
|
||||
func (cli *DockerCli) initializeFromClient() {
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithTimeout(ctx, cli.getInitTimeout())
|
||||
ctx, cancel := context.WithTimeout(cli.baseCtx, cli.getInitTimeout())
|
||||
defer cancel()
|
||||
|
||||
ping, err := cli.client.Ping(ctx)
|
||||
@ -394,7 +398,7 @@ func (cli *DockerCli) CurrentContext() string {
|
||||
// occur when trying to use it.
|
||||
//
|
||||
// Refer to [DockerCli.CurrentContext] above for further details.
|
||||
func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigFile) string {
|
||||
func resolveContextName(opts *cliflags.ClientOptions, cfg *configfile.ConfigFile) string {
|
||||
if opts != nil && opts.Context != "" {
|
||||
return opts.Context
|
||||
}
|
||||
@ -407,9 +411,9 @@ func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigF
|
||||
if ctxName := os.Getenv(EnvOverrideContext); ctxName != "" {
|
||||
return ctxName
|
||||
}
|
||||
if config != nil && config.CurrentContext != "" {
|
||||
if cfg != nil && cfg.CurrentContext != "" {
|
||||
// We don't validate if this context exists: errors may occur when trying to use it.
|
||||
return config.CurrentContext
|
||||
return cfg.CurrentContext
|
||||
}
|
||||
return DefaultContextName
|
||||
}
|
||||
@ -444,13 +448,16 @@ func (cli *DockerCli) initialize() error {
|
||||
return
|
||||
}
|
||||
}
|
||||
if cli.baseCtx == nil {
|
||||
cli.baseCtx = context.Background()
|
||||
}
|
||||
cli.initializeFromClient()
|
||||
})
|
||||
return cli.initErr
|
||||
}
|
||||
|
||||
// Apply all the operation on the cli
|
||||
func (cli *DockerCli) Apply(ops ...DockerCliOption) error {
|
||||
func (cli *DockerCli) Apply(ops ...CLIOption) error {
|
||||
for _, op := range ops {
|
||||
if err := op(cli); err != nil {
|
||||
return err
|
||||
@ -479,15 +486,15 @@ type ServerInfo struct {
|
||||
// NewDockerCli returns a DockerCli instance with all operators applied on it.
|
||||
// It applies by default the standard streams, and the content trust from
|
||||
// environment.
|
||||
func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
|
||||
defaultOps := []DockerCliOption{
|
||||
func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
|
||||
defaultOps := []CLIOption{
|
||||
WithContentTrustFromEnv(),
|
||||
WithDefaultContextStoreConfig(),
|
||||
WithStandardStreams(),
|
||||
}
|
||||
ops = append(defaultOps, ops...)
|
||||
|
||||
cli := &DockerCli{}
|
||||
cli := &DockerCli{baseCtx: context.Background()}
|
||||
if err := cli.Apply(ops...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -514,7 +521,7 @@ func UserAgent() string {
|
||||
}
|
||||
|
||||
var defaultStoreEndpoints = []store.NamedTypeGetter{
|
||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() any { return &docker.EndpointMeta{} }),
|
||||
}
|
||||
|
||||
// RegisterDefaultStoreEndpoints registers a new named endpoint
|
||||
@ -528,7 +535,7 @@ func RegisterDefaultStoreEndpoints(ep ...store.NamedTypeGetter) {
|
||||
// DefaultContextStoreConfig returns a new store.Config with the default set of endpoints configured.
|
||||
func DefaultContextStoreConfig() store.Config {
|
||||
return store.NewConfig(
|
||||
func() interface{} { return &DockerContext{} },
|
||||
func() any { return &DockerContext{} },
|
||||
defaultStoreEndpoints...,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
@ -10,11 +11,21 @@ import (
|
||||
"github.com/moby/term"
|
||||
)
|
||||
|
||||
// CLIOption applies a modification on a DockerCli.
|
||||
type CLIOption func(cli *DockerCli) error
|
||||
|
||||
// DockerCliOption applies a modification on a DockerCli.
|
||||
type DockerCliOption func(cli *DockerCli) error
|
||||
//
|
||||
// Deprecated: use [CLIOption] instead.
|
||||
type DockerCliOption = CLIOption
|
||||
|
||||
// InitializeOpt is the type of the functional options passed to DockerCli.Initialize
|
||||
//
|
||||
// Deprecated: use [CLIOption] instead.
|
||||
type InitializeOpt = CLIOption
|
||||
|
||||
// WithStandardStreams sets a cli in, out and err streams with the standard streams.
|
||||
func WithStandardStreams() DockerCliOption {
|
||||
func WithStandardStreams() CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
// Set terminal emulation based on platform as required.
|
||||
stdin, stdout, stderr := term.StdStreams()
|
||||
@ -25,8 +36,17 @@ func WithStandardStreams() DockerCliOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaseContext sets the base context of a cli. It is used to propagate
|
||||
// the context from the command line to the client.
|
||||
func WithBaseContext(ctx context.Context) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.baseCtx = ctx
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithCombinedStreams uses the same stream for the output and error streams.
|
||||
func WithCombinedStreams(combined io.Writer) DockerCliOption {
|
||||
func WithCombinedStreams(combined io.Writer) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.out = streams.NewOut(combined)
|
||||
cli.err = combined
|
||||
@ -35,7 +55,7 @@ func WithCombinedStreams(combined io.Writer) DockerCliOption {
|
||||
}
|
||||
|
||||
// WithInputStream sets a cli input stream.
|
||||
func WithInputStream(in io.ReadCloser) DockerCliOption {
|
||||
func WithInputStream(in io.ReadCloser) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.in = streams.NewIn(in)
|
||||
return nil
|
||||
@ -43,7 +63,7 @@ func WithInputStream(in io.ReadCloser) DockerCliOption {
|
||||
}
|
||||
|
||||
// WithOutputStream sets a cli output stream.
|
||||
func WithOutputStream(out io.Writer) DockerCliOption {
|
||||
func WithOutputStream(out io.Writer) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.out = streams.NewOut(out)
|
||||
return nil
|
||||
@ -51,7 +71,7 @@ func WithOutputStream(out io.Writer) DockerCliOption {
|
||||
}
|
||||
|
||||
// WithErrorStream sets a cli error stream.
|
||||
func WithErrorStream(err io.Writer) DockerCliOption {
|
||||
func WithErrorStream(err io.Writer) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.err = err
|
||||
return nil
|
||||
@ -59,7 +79,7 @@ func WithErrorStream(err io.Writer) DockerCliOption {
|
||||
}
|
||||
|
||||
// WithContentTrustFromEnv enables content trust on a cli from environment variable DOCKER_CONTENT_TRUST value.
|
||||
func WithContentTrustFromEnv() DockerCliOption {
|
||||
func WithContentTrustFromEnv() CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.contentTrust = false
|
||||
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
||||
@ -73,7 +93,7 @@ func WithContentTrustFromEnv() DockerCliOption {
|
||||
}
|
||||
|
||||
// WithContentTrust enables content trust on a cli.
|
||||
func WithContentTrust(enabled bool) DockerCliOption {
|
||||
func WithContentTrust(enabled bool) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.contentTrust = enabled
|
||||
return nil
|
||||
@ -81,7 +101,7 @@ func WithContentTrust(enabled bool) DockerCliOption {
|
||||
}
|
||||
|
||||
// WithDefaultContextStoreConfig configures the cli to use the default context store configuration.
|
||||
func WithDefaultContextStoreConfig() DockerCliOption {
|
||||
func WithDefaultContextStoreConfig() CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.contextStoreConfig = DefaultContextStoreConfig()
|
||||
return nil
|
||||
@ -89,7 +109,7 @@ func WithDefaultContextStoreConfig() DockerCliOption {
|
||||
}
|
||||
|
||||
// WithAPIClient configures the cli to use the given API client.
|
||||
func WithAPIClient(c client.APIClient) DockerCliOption {
|
||||
func WithAPIClient(c client.APIClient) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.client = c
|
||||
return nil
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func contentTrustEnabled(t *testing.T) bool {
|
||||
t.Helper()
|
||||
var cli DockerCli
|
||||
assert.NilError(t, WithContentTrustFromEnv()(&cli))
|
||||
return cli.contentTrust
|
||||
|
||||
@ -35,7 +35,7 @@ func newConfigCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
createOpts.Name = args[0]
|
||||
createOpts.File = args[1]
|
||||
return RunConfigCreate(dockerCli, createOpts)
|
||||
return RunConfigCreate(cmd.Context(), dockerCli, createOpts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
@ -48,9 +48,8 @@ func newConfigCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunConfigCreate creates a config with the given options.
|
||||
func RunConfigCreate(dockerCli command.Cli, options CreateOptions) error {
|
||||
func RunConfigCreate(ctx context.Context, dockerCli command.Cli, options CreateOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
var in io.Reader = dockerCli.In()
|
||||
if options.File != "-" {
|
||||
|
||||
@ -101,7 +101,7 @@ func (c *configContext) Labels() string {
|
||||
if mapLabels == nil {
|
||||
return ""
|
||||
}
|
||||
var joinLabels []string
|
||||
joinLabels := make([]string, 0, len(mapLabels))
|
||||
for k, v := range mapLabels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
@ -27,7 +30,7 @@ func newConfigInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Names = args
|
||||
return RunConfigInspect(dockerCli, opts)
|
||||
return RunConfigInspect(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -40,15 +43,14 @@ func newConfigInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunConfigInspect inspects the given Swarm config.
|
||||
func RunConfigInspect(dockerCli command.Cli, opts InspectOptions) error {
|
||||
func RunConfigInspect(ctx context.Context, dockerCli command.Cli, opts InspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
if opts.Pretty {
|
||||
opts.Format = "pretty"
|
||||
}
|
||||
|
||||
getRef := func(id string) (interface{}, []byte, error) {
|
||||
getRef := func(id string) (any, []byte, error) {
|
||||
return client.ConfigInspectWithRaw(ctx, id)
|
||||
}
|
||||
f := opts.Format
|
||||
|
||||
@ -31,7 +31,7 @@ func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "List configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunConfigList(dockerCli, listOpts)
|
||||
return RunConfigList(cmd.Context(), dockerCli, listOpts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
@ -45,9 +45,8 @@ func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunConfigList lists Swarm configs.
|
||||
func RunConfigList(dockerCli command.Cli, options ListOptions) error {
|
||||
func RunConfigList(ctx context.Context, dockerCli command.Cli, options ListOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
configs, err := client.ConfigList(ctx, types.ConfigListOptions{Filters: options.Filter.Value()})
|
||||
if err != nil {
|
||||
|
||||
@ -26,7 +26,7 @@ func newConfigRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := RemoveOptions{
|
||||
Names: args,
|
||||
}
|
||||
return RunConfigRemove(dockerCli, opts)
|
||||
return RunConfigRemove(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
@ -35,9 +35,8 @@ func newConfigRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunConfigRemove removes the given Swarm configs.
|
||||
func RunConfigRemove(dockerCli command.Cli, opts RemoveOptions) error {
|
||||
func RunConfigRemove(ctx context.Context, dockerCli command.Cli, opts RemoveOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
|
||||
@ -24,8 +24,8 @@ type AttachOptions struct {
|
||||
DetachKeys string
|
||||
}
|
||||
|
||||
func inspectContainerAndCheckState(ctx context.Context, cli client.APIClient, args string) (*types.ContainerJSON, error) {
|
||||
c, err := cli.ContainerInspect(ctx, args)
|
||||
func inspectContainerAndCheckState(ctx context.Context, apiClient client.APIClient, args string) (*types.ContainerJSON, error) {
|
||||
c, err := apiClient.ContainerInspect(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -45,21 +45,21 @@ func inspectContainerAndCheckState(ctx context.Context, cli client.APIClient, ar
|
||||
// NewAttachCommand creates a new cobra.Command for `docker attach`
|
||||
func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts AttachOptions
|
||||
var container string
|
||||
var ctr string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "attach [OPTIONS] CONTAINER",
|
||||
Short: "Attach local standard input, output, and error streams to a running container",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
container = args[0]
|
||||
return RunAttach(context.Background(), dockerCli, container, &opts)
|
||||
ctr = args[0]
|
||||
return RunAttach(cmd.Context(), dockerCli, ctr, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container attach, docker attach",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(container types.Container) bool {
|
||||
return container.State != "paused"
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(ctr types.Container) bool {
|
||||
return ctr.State != "paused"
|
||||
}),
|
||||
}
|
||||
|
||||
@ -71,8 +71,8 @@ func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunAttach executes an `attach` command
|
||||
func RunAttach(ctx context.Context, dockerCli command.Cli, target string, opts *AttachOptions) error {
|
||||
apiClient := dockerCli.Client()
|
||||
func RunAttach(ctx context.Context, dockerCLI command.Cli, target string, opts *AttachOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
// request channel to wait for client
|
||||
resultC, errC := apiClient.ContainerWait(ctx, target, "")
|
||||
@ -82,11 +82,11 @@ func RunAttach(ctx context.Context, dockerCli command.Cli, target string, opts *
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dockerCli.In().CheckTty(!opts.NoStdin, c.Config.Tty); err != nil {
|
||||
if err := dockerCLI.In().CheckTty(!opts.NoStdin, c.Config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
detachKeys := dockerCli.ConfigFile().DetachKeys
|
||||
detachKeys := dockerCLI.ConfigFile().DetachKeys
|
||||
if opts.DetachKeys != "" {
|
||||
detachKeys = opts.DetachKeys
|
||||
}
|
||||
@ -101,7 +101,7 @@ func RunAttach(ctx context.Context, dockerCli command.Cli, target string, opts *
|
||||
|
||||
var in io.ReadCloser
|
||||
if options.Stdin {
|
||||
in = dockerCli.In()
|
||||
in = dockerCLI.In()
|
||||
}
|
||||
|
||||
if opts.Proxy && !c.Config.Tty {
|
||||
@ -129,15 +129,15 @@ func RunAttach(ctx context.Context, dockerCli command.Cli, target string, opts *
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Config.Tty && dockerCli.Out().IsTerminal() {
|
||||
resizeTTY(ctx, dockerCli, target)
|
||||
if c.Config.Tty && dockerCLI.Out().IsTerminal() {
|
||||
resizeTTY(ctx, dockerCLI, target)
|
||||
}
|
||||
|
||||
streamer := hijackedIOStreamer{
|
||||
streams: dockerCli,
|
||||
streams: dockerCLI,
|
||||
inputStream: in,
|
||||
outputStream: dockerCli.Out(),
|
||||
errorStream: dockerCli.Err(),
|
||||
outputStream: dockerCLI.Out(),
|
||||
errorStream: dockerCLI.Err(),
|
||||
resp: resp,
|
||||
tty: c.Config.Tty,
|
||||
detachKeys: options.DetachKeys,
|
||||
|
||||
@ -16,24 +16,24 @@ type fakeClient struct {
|
||||
client.Client
|
||||
inspectFunc func(string) (types.ContainerJSON, error)
|
||||
execInspectFunc func(execID string) (types.ContainerExecInspect, error)
|
||||
execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error)
|
||||
execCreateFunc func(containerID string, config types.ExecConfig) (types.IDResponse, error)
|
||||
createContainerFunc func(config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig,
|
||||
platform *specs.Platform,
|
||||
containerName string) (container.CreateResponse, error)
|
||||
containerStartFunc func(container string, options container.StartOptions) error
|
||||
containerStartFunc func(containerID string, options container.StartOptions) error
|
||||
imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
|
||||
infoFunc func() (system.Info, error)
|
||||
containerStatPathFunc func(container, path string) (types.ContainerPathStat, error)
|
||||
containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
|
||||
containerStatPathFunc func(containerID, path string) (types.ContainerPathStat, error)
|
||||
containerCopyFromFunc func(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
|
||||
logFunc func(string, container.LogsOptions) (io.ReadCloser, error)
|
||||
waitFunc func(string) (<-chan container.WaitResponse, <-chan error)
|
||||
containerListFunc func(container.ListOptions) ([]types.Container, error)
|
||||
containerExportFunc func(string) (io.ReadCloser, error)
|
||||
containerExecResizeFunc func(id string, options container.ResizeOptions) error
|
||||
containerRemoveFunc func(ctx context.Context, container string, options container.RemoveOptions) error
|
||||
containerKillFunc func(ctx context.Context, container, signal string) error
|
||||
containerRemoveFunc func(ctx context.Context, containerID string, options container.RemoveOptions) error
|
||||
containerKillFunc func(ctx context.Context, containerID, signal string) error
|
||||
Version string
|
||||
}
|
||||
|
||||
@ -51,9 +51,9 @@ func (f *fakeClient) ContainerInspect(_ context.Context, containerID string) (ty
|
||||
return types.ContainerJSON{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerExecCreate(_ context.Context, container string, config types.ExecConfig) (types.IDResponse, error) {
|
||||
func (f *fakeClient) ContainerExecCreate(_ context.Context, containerID string, config types.ExecConfig) (types.IDResponse, error) {
|
||||
if f.execCreateFunc != nil {
|
||||
return f.execCreateFunc(container, config)
|
||||
return f.execCreateFunc(containerID, config)
|
||||
}
|
||||
return types.IDResponse{}, nil
|
||||
}
|
||||
@ -83,9 +83,9 @@ func (f *fakeClient) ContainerCreate(
|
||||
return container.CreateResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerRemove(ctx context.Context, container string, options container.RemoveOptions) error {
|
||||
func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error {
|
||||
if f.containerRemoveFunc != nil {
|
||||
return f.containerRemoveFunc(ctx, container, options)
|
||||
return f.containerRemoveFunc(ctx, containerID, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -104,23 +104,23 @@ func (f *fakeClient) Info(_ context.Context) (system.Info, error) {
|
||||
return system.Info{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerStatPath(_ context.Context, container, path string) (types.ContainerPathStat, error) {
|
||||
func (f *fakeClient) ContainerStatPath(_ context.Context, containerID, path string) (types.ContainerPathStat, error) {
|
||||
if f.containerStatPathFunc != nil {
|
||||
return f.containerStatPathFunc(container, path)
|
||||
return f.containerStatPathFunc(containerID, path)
|
||||
}
|
||||
return types.ContainerPathStat{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) CopyFromContainer(_ context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
||||
func (f *fakeClient) CopyFromContainer(_ context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
||||
if f.containerCopyFromFunc != nil {
|
||||
return f.containerCopyFromFunc(container, srcPath)
|
||||
return f.containerCopyFromFunc(containerID, srcPath)
|
||||
}
|
||||
return nil, types.ContainerPathStat{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerLogs(_ context.Context, container string, options container.LogsOptions) (io.ReadCloser, error) {
|
||||
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options container.LogsOptions) (io.ReadCloser, error) {
|
||||
if f.logFunc != nil {
|
||||
return f.logFunc(container, options)
|
||||
return f.logFunc(containerID, options)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@ -129,23 +129,23 @@ func (f *fakeClient) ClientVersion() string {
|
||||
return f.Version
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerWait(_ context.Context, container string, _ container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
|
||||
func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
|
||||
if f.waitFunc != nil {
|
||||
return f.waitFunc(container)
|
||||
return f.waitFunc(containerID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerStart(_ context.Context, container string, options container.StartOptions) error {
|
||||
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options container.StartOptions) error {
|
||||
if f.containerStartFunc != nil {
|
||||
return f.containerStartFunc(container, options)
|
||||
return f.containerStartFunc(containerID, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerExport(_ context.Context, container string) (io.ReadCloser, error) {
|
||||
func (f *fakeClient) ContainerExport(_ context.Context, containerID string) (io.ReadCloser, error) {
|
||||
if f.containerExportFunc != nil {
|
||||
return f.containerExportFunc(container)
|
||||
return f.containerExportFunc(containerID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@ -157,9 +157,9 @@ func (f *fakeClient) ContainerExecResize(_ context.Context, id string, options c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeClient) ContainerKill(ctx context.Context, container, signal string) error {
|
||||
func (f *fakeClient) ContainerKill(ctx context.Context, containerID, signal string) error {
|
||||
if f.containerKillFunc != nil {
|
||||
return f.containerKillFunc(ctx, container, signal)
|
||||
return f.containerKillFunc(ctx, containerID, signal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if len(args) > 1 {
|
||||
options.reference = args[1]
|
||||
}
|
||||
return runCommit(dockerCli, &options)
|
||||
return runCommit(cmd.Context(), dockerCli, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container commit, docker commit",
|
||||
@ -56,9 +56,7 @@ func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(dockerCli command.Cli, options *commitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runCommit(ctx context.Context, dockerCli command.Cli, options *commitOptions) error {
|
||||
response, err := dockerCli.Client().ContainerCommit(ctx, options.container, container.CommitOptions{
|
||||
Reference: options.reference,
|
||||
Comment: options.comment,
|
||||
|
||||
@ -151,7 +151,7 @@ func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// User did not specify "quiet" flag; suppress output if no terminal is attached
|
||||
opts.quiet = !dockerCli.Out().IsTerminal()
|
||||
}
|
||||
return runCopy(dockerCli, opts)
|
||||
return runCopy(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container cp, docker cp",
|
||||
@ -169,7 +169,7 @@ func progressHumanSize(n int64) string {
|
||||
return units.HumanSizeWithPrecision(float64(n), 3)
|
||||
}
|
||||
|
||||
func runCopy(dockerCli command.Cli, opts copyOptions) error {
|
||||
func runCopy(ctx context.Context, dockerCli command.Cli, opts copyOptions) error {
|
||||
srcContainer, srcPath := splitCpArg(opts.source)
|
||||
destContainer, destPath := splitCpArg(opts.destination)
|
||||
|
||||
@ -191,8 +191,6 @@ func runCopy(dockerCli command.Cli, opts copyOptions) error {
|
||||
copyConfig.container = destContainer
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
switch direction {
|
||||
case fromContainer:
|
||||
return copyFromContainer(ctx, dockerCli, copyConfig)
|
||||
@ -246,7 +244,6 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
|
||||
linkTarget, rebaseName = archive.GetRebaseName(srcPath, linkTarget)
|
||||
srcPath = linkTarget
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -41,7 +42,7 @@ func TestRunCopyWithInvalidArguments(t *testing.T) {
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
err := runCopy(test.NewFakeCli(nil), testcase.options)
|
||||
err := runCopy(context.TODO(), test.NewFakeCli(nil), testcase.options)
|
||||
assert.Error(t, err, testcase.expectedErr)
|
||||
})
|
||||
}
|
||||
@ -58,7 +59,7 @@ func TestRunCopyFromContainerToStdout(t *testing.T) {
|
||||
}
|
||||
options := copyOptions{source: "container:/path", destination: "-"}
|
||||
cli := test.NewFakeCli(fakeClient)
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(tarContent, cli.OutBuffer().String()))
|
||||
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
|
||||
@ -78,7 +79,7 @@ func TestRunCopyFromContainerToFilesystem(t *testing.T) {
|
||||
}
|
||||
options := copyOptions{source: "container:/path", destination: destDir.Path(), quiet: true}
|
||||
cli := test.NewFakeCli(fakeClient)
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("", cli.OutBuffer().String()))
|
||||
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
|
||||
@ -106,7 +107,7 @@ func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.
|
||||
destination: destDir.Join("missing", "foo"),
|
||||
}
|
||||
cli := test.NewFakeCli(fakeClient)
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
assert.ErrorContains(t, err, destDir.Join("missing"))
|
||||
}
|
||||
|
||||
@ -119,7 +120,7 @@ func TestRunCopyToContainerFromFileWithTrailingSlash(t *testing.T) {
|
||||
destination: "container:/path",
|
||||
}
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
|
||||
expectedError := "not a directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
@ -134,7 +135,7 @@ func TestRunCopyToContainerSourceDoesNotExist(t *testing.T) {
|
||||
destination: "container:/path",
|
||||
}
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
expected := "no such file or directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
expected = "cannot find the file specified"
|
||||
@ -193,7 +194,7 @@ func TestSplitCpArg(t *testing.T) {
|
||||
func TestRunCopyFromContainerToFilesystemIrregularDestination(t *testing.T) {
|
||||
options := copyOptions{source: "container:/dev/null", destination: "/dev/random"}
|
||||
cli := test.NewFakeCli(nil)
|
||||
err := runCopy(cli, options)
|
||||
err := runCopy(context.TODO(), cli, options)
|
||||
assert.Assert(t, err != nil)
|
||||
expected := `"/dev/random" must be a directory or a regular file`
|
||||
assert.ErrorContains(t, err, expected)
|
||||
|
||||
@ -55,7 +55,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runCreate(dockerCli, cmd.Flags(), &options, copts)
|
||||
return runCreate(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container create, docker create",
|
||||
@ -80,7 +80,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptions, copts *containerOptions) error {
|
||||
func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, options *createOptions, copts *containerOptions) error {
|
||||
if err := validatePullOpt(options.pull); err != nil {
|
||||
reportError(dockerCli.Err(), "create", err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
@ -104,7 +104,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio
|
||||
reportError(dockerCli.Err(), "create", err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
id, err := createContainer(context.Background(), dockerCli, containerCfg, options)
|
||||
id, err := createContainer(ctx, dockerCli, containerCfg, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -113,15 +113,15 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio
|
||||
}
|
||||
|
||||
// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa).
|
||||
func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *createOptions) error {
|
||||
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
|
||||
func pullImage(ctx context.Context, dockerCli command.Cli, img string, options *createOptions) error {
|
||||
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, types.ImageCreateOptions{
|
||||
responseBody, err := dockerCli.Client().ImageCreate(ctx, img, types.ImageCreateOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
Platform: opts.platform,
|
||||
Platform: options.platform,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -129,7 +129,7 @@ func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *c
|
||||
defer responseBody.Close()
|
||||
|
||||
out := dockerCli.Err()
|
||||
if opts.quiet {
|
||||
if options.quiet {
|
||||
out = io.Discard
|
||||
}
|
||||
return jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(out), nil)
|
||||
@ -185,7 +185,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *containerConfig, opts *createOptions) (containerID string, err error) {
|
||||
func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *containerConfig, options *createOptions) (containerID string, err error) {
|
||||
config := containerCfg.Config
|
||||
hostConfig := containerCfg.HostConfig
|
||||
networkingConfig := containerCfg.NetworkingConfig
|
||||
@ -211,7 +211,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
if named, ok := ref.(reference.Named); ok {
|
||||
namedRef = reference.TagNameOnly(named)
|
||||
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !opts.untrusted {
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !options.untrusted {
|
||||
var err error
|
||||
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
|
||||
if err != nil {
|
||||
@ -222,7 +222,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
}
|
||||
|
||||
pullAndTagImage := func() error {
|
||||
if err := pullImage(ctx, dockerCli, config.Image, opts); err != nil {
|
||||
if err := pullImage(ctx, dockerCli, config.Image, options); err != nil {
|
||||
return err
|
||||
}
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
|
||||
@ -236,15 +236,15 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
// create. It will produce an error if you try to set a platform on older API
|
||||
// versions, so check the API version here to maintain backwards
|
||||
// compatibility for CLI users.
|
||||
if opts.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
|
||||
p, err := platforms.Parse(opts.platform)
|
||||
if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
|
||||
p, err := platforms.Parse(options.platform)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error parsing specified platform")
|
||||
}
|
||||
platform = &p
|
||||
}
|
||||
|
||||
if opts.pull == PullImageAlways {
|
||||
if options.pull == PullImageAlways {
|
||||
if err := pullAndTagImage(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -252,11 +252,11 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
|
||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.Out().GetTtySize()
|
||||
|
||||
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, opts.name)
|
||||
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
|
||||
if err != nil {
|
||||
// Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior.
|
||||
if errdefs.IsNotFound(err) && namedRef != nil && opts.pull == PullImageMissing {
|
||||
if !opts.quiet {
|
||||
if errdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
|
||||
if !options.quiet {
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
fmt.Fprintf(dockerCli.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
}
|
||||
@ -266,7 +266,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
}
|
||||
|
||||
var retryErr error
|
||||
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, opts.name)
|
||||
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
|
||||
if retryErr != nil {
|
||||
return "", retryErr
|
||||
}
|
||||
|
||||
@ -181,6 +181,7 @@ func TestCreateContainerImagePullPolicyInvalid(t *testing.T) {
|
||||
t.Run(tc.PullPolicy, func(t *testing.T) {
|
||||
dockerCli := test.NewFakeCli(&fakeClient{})
|
||||
err := runCreate(
|
||||
context.TODO(),
|
||||
dockerCli,
|
||||
&pflag.FlagSet{},
|
||||
&createOptions{pull: tc.PullPolicy},
|
||||
@ -223,7 +224,7 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig,
|
||||
@ -233,8 +234,8 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
||||
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
|
||||
},
|
||||
}, test.EnableContentTrust)
|
||||
cli.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewCreateCommand(cli)
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
@ -323,7 +324,7 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
|
||||
}
|
||||
sort.Strings(expected)
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig,
|
||||
@ -335,7 +336,7 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
|
||||
return container.CreateResponse{}, nil
|
||||
},
|
||||
})
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
fakeCLI.SetConfigFile(&configfile.ConfigFile{
|
||||
Proxies: map[string]configfile.ProxyConfig{
|
||||
"default": {
|
||||
HTTPProxy: "httpProxy",
|
||||
@ -346,7 +347,7 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
cmd := NewCreateCommand(cli)
|
||||
cmd := NewCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs([]string{"image:tag"})
|
||||
err := cmd.Execute()
|
||||
|
||||
@ -25,7 +25,7 @@ func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runDiff(dockerCli, &opts)
|
||||
return runDiff(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container diff, docker diff",
|
||||
@ -34,12 +34,10 @@ func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runDiff(dockerCli command.Cli, opts *diffOptions) error {
|
||||
func runDiff(ctx context.Context, dockerCli command.Cli, opts *diffOptions) error {
|
||||
if opts.container == "" {
|
||||
return errors.New("Container name cannot be empty")
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
changes, err := dockerCli.Client().ContainerDiff(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -52,7 +52,7 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
container = args[0]
|
||||
options.Command = args[1:]
|
||||
return RunExec(context.Background(), dockerCli, container, options)
|
||||
return RunExec(cmd.Context(), dockerCli, container, options)
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(container types.Container) bool {
|
||||
return container.State != "paused"
|
||||
|
||||
@ -192,18 +192,16 @@ func TestRunExec(t *testing.T) {
|
||||
|
||||
for _, testcase := range testcases {
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
cli := test.NewFakeCli(&testcase.client)
|
||||
fakeCLI := test.NewFakeCli(&testcase.client)
|
||||
|
||||
err := RunExec(context.Background(), cli, "thecontainer", testcase.options)
|
||||
err := RunExec(context.TODO(), fakeCLI, "thecontainer", testcase.options)
|
||||
if testcase.expectedError != "" {
|
||||
assert.ErrorContains(t, err, testcase.expectedError)
|
||||
} else {
|
||||
if !assert.Check(t, err) {
|
||||
return
|
||||
}
|
||||
} else if !assert.Check(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Check(t, is.Equal(testcase.expectedOut, cli.OutBuffer().String()))
|
||||
assert.Check(t, is.Equal(testcase.expectedErr, cli.ErrBuffer().String()))
|
||||
assert.Check(t, is.Equal(testcase.expectedOut, fakeCLI.OutBuffer().String()))
|
||||
assert.Check(t, is.Equal(testcase.expectedErr, fakeCLI.ErrBuffer().String()))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -264,8 +262,8 @@ func TestNewExecCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc})
|
||||
cmd := NewExecCommand(cli)
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc})
|
||||
cmd := NewExecCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
|
||||
@ -26,7 +26,7 @@ func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runExport(dockerCli, opts)
|
||||
return runExport(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container export, docker export",
|
||||
@ -41,7 +41,7 @@ func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runExport(dockerCli command.Cli, opts exportOptions) error {
|
||||
func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) error {
|
||||
if opts.output == "" && dockerCli.Out().IsTerminal() {
|
||||
return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect")
|
||||
}
|
||||
@ -52,7 +52,7 @@ func runExport(dockerCli command.Cli, opts exportOptions) error {
|
||||
|
||||
clnt := dockerCli.Client()
|
||||
|
||||
responseBody, err := clnt.ContainerExport(context.Background(), opts.container)
|
||||
responseBody, err := clnt.ContainerExport(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -14,8 +14,7 @@ const (
|
||||
|
||||
// NewDiffFormat returns a format for use with a diff Context
|
||||
func NewDiffFormat(source string) formatter.Format {
|
||||
switch source {
|
||||
case formatter.TableFormatKey:
|
||||
if source == formatter.TableFormatKey {
|
||||
return defaultDiffTableFormat
|
||||
}
|
||||
return formatter.Format(source)
|
||||
|
||||
@ -24,7 +24,7 @@ const (
|
||||
pidsHeader = "PIDS" // Used only on Linux
|
||||
)
|
||||
|
||||
// StatsEntry represents represents the statistics data collected from a container
|
||||
// StatsEntry represents the statistics data collected from a container
|
||||
type StatsEntry struct {
|
||||
Container string
|
||||
Name string
|
||||
@ -116,9 +116,9 @@ func NewStats(container string) *Stats {
|
||||
}
|
||||
|
||||
// statsFormatWrite renders the context for a list of containers statistics
|
||||
func statsFormatWrite(ctx formatter.Context, Stats []StatsEntry, osType string, trunc bool) error {
|
||||
func statsFormatWrite(ctx formatter.Context, stats []StatsEntry, osType string, trunc bool) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, cstats := range Stats {
|
||||
for _, cstats := range stats {
|
||||
statsCtx := &statsContext{
|
||||
s: cstats,
|
||||
os: osType,
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
@ -27,7 +30,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.refs = args
|
||||
return runInspect(dockerCli, opts)
|
||||
return runInspect(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
}
|
||||
@ -39,11 +42,10 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
getRefFunc := func(ref string) (interface{}, []byte, error) {
|
||||
getRefFunc := func(ref string) (any, []byte, error) {
|
||||
return client.ContainerInspectWithRaw(ctx, ref, opts.size)
|
||||
}
|
||||
return inspect.Inspect(dockerCli.Out(), opts.refs, opts.format, getRefFunc)
|
||||
|
||||
@ -28,7 +28,7 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runKill(dockerCli, &opts)
|
||||
return runKill(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container kill, docker kill",
|
||||
@ -41,9 +41,8 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(dockerCli command.Cli, opts *killOptions) error {
|
||||
func runKill(ctx context.Context, dockerCli command.Cli, opts *killOptions) error {
|
||||
var errs []string
|
||||
ctx := context.Background()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
|
||||
return dockerCli.Client().ContainerKill(ctx, container, opts.signal)
|
||||
})
|
||||
|
||||
@ -29,7 +29,7 @@ type psOptions struct {
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -38,7 +38,7 @@ func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.sizeChanged = cmd.Flags().Changed("size")
|
||||
return runPs(dockerCli, &options)
|
||||
return runPs(cmd.Context(), dockerCLI, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"category-top": "3",
|
||||
@ -61,28 +61,28 @@ func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := *NewPsCommand(dockerCli)
|
||||
func newListCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := *NewPsCommand(dockerCLI)
|
||||
cmd.Aliases = []string{"ps", "list"}
|
||||
cmd.Use = "ls [OPTIONS]"
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func buildContainerListOptions(opts *psOptions) (*container.ListOptions, error) {
|
||||
options := &container.ListOptions{
|
||||
All: opts.all,
|
||||
Limit: opts.last,
|
||||
Size: opts.size,
|
||||
Filters: opts.filter.Value(),
|
||||
func buildContainerListOptions(options *psOptions) (*container.ListOptions, error) {
|
||||
listOptions := &container.ListOptions{
|
||||
All: options.all,
|
||||
Limit: options.last,
|
||||
Size: options.size,
|
||||
Filters: options.filter.Value(),
|
||||
}
|
||||
|
||||
if opts.nLatest && opts.last == -1 {
|
||||
options.Limit = 1
|
||||
if options.nLatest && options.last == -1 {
|
||||
listOptions.Limit = 1
|
||||
}
|
||||
|
||||
// always validate template when `--format` is used, for consistency
|
||||
if len(opts.format) > 0 {
|
||||
tmpl, err := templates.NewParse("", opts.format)
|
||||
if len(options.format) > 0 {
|
||||
tmpl, err := templates.NewParse("", options.format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse template")
|
||||
}
|
||||
@ -97,7 +97,7 @@ func buildContainerListOptions(opts *psOptions) (*container.ListOptions, error)
|
||||
|
||||
// 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 {
|
||||
if !options.quiet && !listOptions.Size && !options.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
|
||||
@ -106,22 +106,20 @@ func buildContainerListOptions(opts *psOptions) (*container.ListOptions, error)
|
||||
// because calculating the size is a costly operation.
|
||||
|
||||
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
|
||||
options.Size = true
|
||||
listOptions.Size = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
return listOptions, nil
|
||||
}
|
||||
|
||||
func runPs(dockerCli command.Cli, options *psOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error {
|
||||
if len(options.format) == 0 {
|
||||
// load custom psFormat from CLI config (if any)
|
||||
options.format = dockerCli.ConfigFile().PsFormat
|
||||
options.format = dockerCLI.ConfigFile().PsFormat
|
||||
} else if options.quiet {
|
||||
_, _ = dockerCli.Err().Write([]byte("WARNING: Ignoring custom format, because both --format and --quiet are set.\n"))
|
||||
_, _ = dockerCLI.Err().Write([]byte("WARNING: Ignoring custom format, because both --format and --quiet are set.\n"))
|
||||
}
|
||||
|
||||
listOptions, err := buildContainerListOptions(options)
|
||||
@ -129,13 +127,13 @@ func runPs(dockerCli command.Cli, options *psOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
containers, err := dockerCli.Client().ContainerList(ctx, *listOptions)
|
||||
containers, err := dockerCLI.Client().ContainerList(ctx, *listOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containerCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Output: dockerCLI.Out(),
|
||||
Format: formatter.NewContainerFormat(options.format, options.quiet, listOptions.Size),
|
||||
Trunc: !options.noTrunc,
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runLogs(dockerCli, &opts)
|
||||
return runLogs(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container logs, docker logs",
|
||||
@ -52,9 +52,7 @@ func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runLogs(ctx context.Context, dockerCli command.Cli, opts *logsOptions) error {
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -46,13 +47,11 @@ func TestRunLogs(t *testing.T) {
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
cli := test.NewFakeCli(&testcase.client)
|
||||
|
||||
err := runLogs(cli, testcase.options)
|
||||
err := runLogs(context.TODO(), cli, testcase.options)
|
||||
if testcase.expectedError != "" {
|
||||
assert.ErrorContains(t, err, testcase.expectedError)
|
||||
} else {
|
||||
if !assert.Check(t, err) {
|
||||
return
|
||||
}
|
||||
} else if !assert.Check(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Check(t, is.Equal(testcase.expectedOut, cli.OutBuffer().String()))
|
||||
assert.Check(t, is.Equal(testcase.expectedErr, cli.ErrBuffer().String()))
|
||||
|
||||
@ -28,6 +28,20 @@ import (
|
||||
cdi "tags.cncf.io/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO(thaJeztah): define these in the API-types, or query available defaults
|
||||
// from the daemon, or require "local" profiles to be an absolute path or
|
||||
// relative paths starting with "./". The daemon-config has consts for this
|
||||
// but we don't want to import that package:
|
||||
// https://github.com/moby/moby/blob/v23.0.0/daemon/config/config.go#L63-L67
|
||||
|
||||
// seccompProfileDefault is the built-in default seccomp profile.
|
||||
seccompProfileDefault = "builtin"
|
||||
// seccompProfileUnconfined is a special profile name for seccomp to use an
|
||||
// "unconfined" seccomp profile.
|
||||
seccompProfileUnconfined = "unconfined"
|
||||
)
|
||||
|
||||
var deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
|
||||
|
||||
// containerOptions is a data object with all the options for creating a container
|
||||
@ -914,16 +928,23 @@ func parseSecurityOpts(securityOpts []string) ([]string, error) {
|
||||
// "no-new-privileges" is the only option that does not require a value.
|
||||
return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
|
||||
}
|
||||
if k == "seccomp" && v != "unconfined" {
|
||||
f, err := os.ReadFile(v)
|
||||
if err != nil {
|
||||
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", v, err)
|
||||
if k == "seccomp" {
|
||||
switch v {
|
||||
case seccompProfileDefault, seccompProfileUnconfined:
|
||||
// known special names for built-in profiles, nothing to do.
|
||||
default:
|
||||
// value may be a filename, in which case we send the profile's
|
||||
// content if it's valid JSON.
|
||||
f, err := os.ReadFile(v)
|
||||
if err != nil {
|
||||
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", v, err)
|
||||
}
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, f); err != nil {
|
||||
return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", v, err)
|
||||
}
|
||||
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
||||
}
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, f); err != nil {
|
||||
return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", v, err)
|
||||
}
|
||||
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -198,7 +198,7 @@ func TestParseWithVolumes(t *testing.T) {
|
||||
t.Fatalf("Error parsing volume flags, %q should not mount-bind anything. Received %v", tryit, hostConfig.Binds)
|
||||
} else if _, exists := config.Volumes[arr[0]]; !exists {
|
||||
t.Fatalf("Error parsing volume flags, %s is missing from volumes. Received %v", arr[0], config.Volumes)
|
||||
} else if _, exists := config.Volumes[arr[1]]; !exists {
|
||||
} else if _, exists := config.Volumes[arr[1]]; !exists { //nolint:govet // ignore shadow-check
|
||||
t.Fatalf("Error parsing volume flags, %s is missing from volumes. Received %v", arr[1], config.Volumes)
|
||||
}
|
||||
|
||||
@ -1011,10 +1011,8 @@ func TestValidateDevice(t *testing.T) {
|
||||
for path, expectedError := range invalid {
|
||||
if _, err := validateDevice(path, runtime.GOOS); err == nil {
|
||||
t.Fatalf("ValidateDevice(`%q`) should have failed validation", path)
|
||||
} else {
|
||||
if err.Error() != expectedError {
|
||||
t.Fatalf("ValidateDevice(`%q`) error should contain %q, got %q", path, expectedError, err.Error())
|
||||
}
|
||||
} else if err.Error() != expectedError {
|
||||
t.Fatalf("ValidateDevice(`%q`) error should contain %q, got %q", path, expectedError, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runPause(dockerCli, &opts)
|
||||
return runPause(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container pause, docker pause",
|
||||
@ -38,9 +38,7 @@ func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runPause(dockerCli command.Cli, opts *pauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runPause(ctx context.Context, dockerCli command.Cli, opts *pauseOptions) error {
|
||||
var errs []string
|
||||
errChan := parallelOperation(ctx, opts.containers, dockerCli.Client().ContainerPause)
|
||||
for _, container := range opts.containers {
|
||||
|
||||
@ -36,7 +36,7 @@ func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if len(args) > 1 {
|
||||
opts.port = args[1]
|
||||
}
|
||||
return runPort(dockerCli, &opts)
|
||||
return runPort(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container port, docker port",
|
||||
@ -52,9 +52,7 @@ func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// TODO(thaJeztah): currently this defaults to show the TCP port if no
|
||||
// proto is specified. We should consider changing this to "any" protocol
|
||||
// for the given private port.
|
||||
func runPort(dockerCli command.Cli, opts *portOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) error {
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -26,7 +26,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "Remove all stopped containers",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
spaceReclaimed, output, err := runPrune(dockerCli, options)
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCli, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -50,14 +50,14 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
const warning = `WARNING! This will remove all stopped containers.
|
||||
Are you sure you want to continue?`
|
||||
|
||||
func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
|
||||
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().ContainersPrune(context.Background(), pruneFilters)
|
||||
report, err := dockerCli.Client().ContainersPrune(ctx, pruneFilters)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
@ -75,6 +75,6 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
|
||||
// RunPrune calls the Container Prune API
|
||||
// This returns the amount of space reclaimed and a detailed output string
|
||||
func RunPrune(dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(dockerCli, pruneOptions{force: true, filter: filter})
|
||||
func RunPrune(ctx context.Context, dockerCli command.Cli, _ bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return runPrune(ctx, dockerCli, pruneOptions{force: true, filter: filter})
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.oldName = args[0]
|
||||
opts.newName = args[1]
|
||||
return runRename(dockerCli, &opts)
|
||||
return runRename(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rename, docker rename",
|
||||
@ -38,9 +38,7 @@ func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRename(dockerCli command.Cli, opts *renameOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runRename(ctx context.Context, dockerCli command.Cli, opts *renameOptions) error {
|
||||
oldName := strings.TrimSpace(opts.oldName)
|
||||
newName := strings.TrimSpace(opts.newName)
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
opts.timeoutChanged = cmd.Flags().Changed("time")
|
||||
return runRestart(dockerCli, &opts)
|
||||
return runRestart(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container restart, docker restart",
|
||||
@ -46,8 +46,7 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRestart(dockerCli command.Cli, opts *restartOptions) error {
|
||||
ctx := context.Background()
|
||||
func runRestart(ctx context.Context, dockerCli command.Cli, opts *restartOptions) error {
|
||||
var errs []string
|
||||
var timeout *int
|
||||
if opts.timeoutChanged {
|
||||
|
||||
@ -33,7 +33,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRm(dockerCli, &opts)
|
||||
return runRm(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rm, docker container remove, docker rm",
|
||||
@ -48,9 +48,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(dockerCli command.Cli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runRm(ctx context.Context, dockerCli command.Cli, opts *rmOptions) error {
|
||||
var errs []string
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, ctrID string) error {
|
||||
ctrID = strings.Trim(ctrID, "/")
|
||||
|
||||
@ -42,7 +42,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runRun(dockerCli, cmd.Flags(), &options, copts)
|
||||
return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
||||
},
|
||||
ValidArgsFunction: completion.ImageNames(dockerCli),
|
||||
Annotations: map[string]string{
|
||||
@ -89,7 +89,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
func runRun(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
if err := validatePullOpt(ropts.pull); err != nil {
|
||||
reportError(dockerCli.Err(), "run", err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
@ -114,18 +114,18 @@ func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copt
|
||||
reportError(dockerCli.Err(), "run", err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
return runContainer(dockerCli, ropts, copts, containerCfg)
|
||||
return runContainer(ctx, dockerCli, ropts, copts, containerCfg)
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerCfg *containerConfig) error {
|
||||
func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOptions, copts *containerOptions, containerCfg *containerConfig) error {
|
||||
config := containerCfg.Config
|
||||
stdout, stderr := dockerCli.Out(), dockerCli.Err()
|
||||
apiClient := dockerCli.Client()
|
||||
|
||||
config.ArgsEscaped = false
|
||||
|
||||
if !opts.detach {
|
||||
if !runOpts.detach {
|
||||
if err := dockerCli.In().CheckTty(config.AttachStdin, config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -140,15 +140,15 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
||||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
ctx, cancelFun := context.WithCancel(ctx)
|
||||
defer cancelFun()
|
||||
|
||||
containerID, err := createContainer(ctx, dockerCli, containerCfg, &opts.createOptions)
|
||||
containerID, err := createContainer(ctx, dockerCli, containerCfg, &runOpts.createOptions)
|
||||
if err != nil {
|
||||
reportError(stderr, "run", err.Error(), true)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
if opts.sigProxy {
|
||||
if runOpts.sigProxy {
|
||||
sigc := notifyAllSignals()
|
||||
go ForwardAllSignals(ctx, apiClient, containerID, sigc)
|
||||
defer signal.StopCatch(sigc)
|
||||
@ -169,8 +169,8 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
||||
attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
|
||||
if attach {
|
||||
detachKeys := dockerCli.ConfigFile().DetachKeys
|
||||
if opts.detachKeys != "" {
|
||||
detachKeys = opts.detachKeys
|
||||
if runOpts.detachKeys != "" {
|
||||
detachKeys = runOpts.detachKeys
|
||||
}
|
||||
|
||||
closeFn, err := attachContainer(ctx, dockerCli, containerID, &errCh, config, container.AttachOptions{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -18,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRunLabel(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *specs.Platform, _ string) (container.CreateResponse, error) {
|
||||
return container.CreateResponse{
|
||||
ID: "id",
|
||||
@ -26,7 +27,7 @@ func TestRunLabel(t *testing.T) {
|
||||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRunCommand(cli)
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd.SetArgs([]string{"--detach=true", "--label", "foo", "busybox"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
@ -58,7 +59,7 @@ func TestRunCommandWithContentTrustErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig,
|
||||
@ -68,13 +69,13 @@ func TestRunCommandWithContentTrustErrors(t *testing.T) {
|
||||
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
|
||||
},
|
||||
}, test.EnableContentTrust)
|
||||
cli.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewRunCommand(cli)
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOut(io.Discard)
|
||||
err := cmd.Execute()
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, is.Contains(cli.ErrBuffer().String(), tc.expectedError))
|
||||
assert.Assert(t, is.Contains(fakeCLI.ErrBuffer().String(), tc.expectedError))
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +98,7 @@ func TestRunContainerImagePullPolicyInvalid(t *testing.T) {
|
||||
t.Run(tc.PullPolicy, func(t *testing.T) {
|
||||
dockerCli := test.NewFakeCli(&fakeClient{})
|
||||
err := runRun(
|
||||
context.TODO(),
|
||||
dockerCli,
|
||||
&pflag.FlagSet{},
|
||||
&runOptions{createOptions: createOptions{pull: tc.PullPolicy}},
|
||||
|
||||
@ -45,7 +45,7 @@ func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Containers = args
|
||||
return RunStart(dockerCli, &opts)
|
||||
return RunStart(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container start, docker start",
|
||||
@ -72,11 +72,12 @@ func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// RunStart executes a `start` command
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) error {
|
||||
ctx, cancelFun := context.WithCancel(ctx)
|
||||
defer cancelFun()
|
||||
|
||||
if opts.Attach || opts.OpenStdin {
|
||||
switch {
|
||||
case opts.Attach || opts.OpenStdin:
|
||||
// We're going to attach to a container.
|
||||
// 1. Ensure we only have one container.
|
||||
if len(opts.Containers) > 1 {
|
||||
@ -180,7 +181,8 @@ func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
||||
if status := <-statusChan; status != 0 {
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
} else if opts.Checkpoint != "" {
|
||||
return nil
|
||||
case opts.Checkpoint != "":
|
||||
if len(opts.Containers) > 1 {
|
||||
return errors.New("you cannot restore multiple containers at once")
|
||||
}
|
||||
@ -189,14 +191,11 @@ func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
||||
CheckpointID: opts.Checkpoint,
|
||||
CheckpointDir: opts.CheckpointDir,
|
||||
})
|
||||
|
||||
} else {
|
||||
default:
|
||||
// We're not going to attach to anything.
|
||||
// Start as many containers as we want.
|
||||
return startContainersWithoutAttachments(ctx, dockerCli, opts.Containers)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startContainersWithoutAttachments(ctx context.Context, dockerCli command.Cli, containers []string) error {
|
||||
|
||||
@ -21,83 +21,94 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type statsOptions struct {
|
||||
all bool
|
||||
noStream bool
|
||||
noTrunc bool
|
||||
format string
|
||||
containers []string
|
||||
// StatsOptions defines options for [RunStats].
|
||||
type StatsOptions struct {
|
||||
// All allows including both running and stopped containers. The default
|
||||
// is to only include running containers.
|
||||
All bool
|
||||
|
||||
// NoStream disables streaming stats. If enabled, stats are collected once,
|
||||
// and the result is printed.
|
||||
NoStream bool
|
||||
|
||||
// NoTrunc disables truncating the output. The default is to truncate
|
||||
// output such as container-IDs.
|
||||
NoTrunc bool
|
||||
|
||||
// Format is a custom template to use for presenting the stats.
|
||||
// Refer to [flagsHelper.FormatHelp] for accepted formats.
|
||||
Format string
|
||||
|
||||
// Containers is the list of container names or IDs to include in the stats.
|
||||
// If empty, all containers are included. It is mutually exclusive with the
|
||||
// Filters option, and an error is produced if both are set.
|
||||
Containers []string
|
||||
|
||||
// Filters provides optional filters to filter the list of containers and their
|
||||
// associated container-events to include in the stats if no list of containers
|
||||
// is set. If no filter is provided, all containers are included. Filters and
|
||||
// Containers are currently mutually exclusive, and setting both options
|
||||
// produces an error.
|
||||
//
|
||||
// These filters are used both to collect the initial list of containers and
|
||||
// to refresh the list of containers based on container-events, accepted
|
||||
// filters are limited to the intersection of filters accepted by "events"
|
||||
// and "container list".
|
||||
//
|
||||
// Currently only "label" / "label=value" filters are accepted. Additional
|
||||
// filter options may be added in future (within the constraints described
|
||||
// above), but may require daemon-side validation as the list of accepted
|
||||
// filters can differ between daemon- and API versions.
|
||||
Filters *filters.Args
|
||||
}
|
||||
|
||||
// NewStatsCommand creates a new cobra.Command for `docker stats`
|
||||
func NewStatsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts statsOptions
|
||||
// NewStatsCommand creates a new [cobra.Command] for "docker stats".
|
||||
func NewStatsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := StatsOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "stats [OPTIONS] [CONTAINER...]",
|
||||
Short: "Display a live stream of container(s) resource usage statistics",
|
||||
Args: cli.RequiresMinArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runStats(dockerCli, &opts)
|
||||
options.Containers = args
|
||||
return RunStats(cmd.Context(), dockerCLI, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container stats, docker stats",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)")
|
||||
flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
|
||||
flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.BoolVarP(&options.All, "all", "a", false, "Show all containers (default shows just running)")
|
||||
flags.BoolVar(&options.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result")
|
||||
flags.BoolVar(&options.NoTrunc, "no-trunc", false, "Do not truncate output")
|
||||
flags.StringVar(&options.Format, "format", "", flagsHelper.FormatHelp)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runStats displays a live stream of resource usage statistics for one or more containers.
|
||||
// acceptedStatsFilters is the list of filters accepted by [RunStats] (through
|
||||
// the [StatsOptions.Filters] option).
|
||||
//
|
||||
// TODO(thaJeztah): don't hard-code the list of accept filters, and expand
|
||||
// to the intersection of filters accepted by both "container list" and
|
||||
// "system events". Validating filters may require an initial API call
|
||||
// to both endpoints ("container list" and "system events").
|
||||
var acceptedStatsFilters = map[string]bool{
|
||||
"label": true,
|
||||
}
|
||||
|
||||
// RunStats displays a live stream of resource usage statistics for one or more containers.
|
||||
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
showAll := len(opts.containers) == 0
|
||||
closeChan := make(chan error)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// monitorContainerEvents watches for container creation and removal (only
|
||||
// used when calling `docker stats` without arguments).
|
||||
monitorContainerEvents := func(started chan<- struct{}, c chan events.Message, stopped <-chan struct{}) {
|
||||
f := filters.NewArgs()
|
||||
f.Add("type", "container")
|
||||
options := types.EventsOptions{
|
||||
Filters: f,
|
||||
}
|
||||
|
||||
eventq, errq := dockerCli.Client().Events(ctx, options)
|
||||
|
||||
// Whether we successfully subscribed to eventq or not, we can now
|
||||
// unblock the main goroutine.
|
||||
close(started)
|
||||
defer close(c)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopped:
|
||||
return
|
||||
case event := <-eventq:
|
||||
c <- event
|
||||
case err := <-errq:
|
||||
closeChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
// Get the daemonOSType if not set already
|
||||
if daemonOSType == "" {
|
||||
svctx := context.Background()
|
||||
sv, err := dockerCli.Client().ServerVersion(svctx)
|
||||
sv, err := apiClient.ServerVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -106,57 +117,82 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
|
||||
// waitFirst is a WaitGroup to wait first stat data's reach for each container
|
||||
waitFirst := &sync.WaitGroup{}
|
||||
|
||||
// closeChan is a non-buffered channel used to collect errors from goroutines.
|
||||
closeChan := make(chan error)
|
||||
cStats := stats{}
|
||||
// getContainerList simulates creation event for all previously existing
|
||||
// containers (only used when calling `docker stats` without arguments).
|
||||
getContainerList := func() {
|
||||
options := container.ListOptions{
|
||||
All: opts.all,
|
||||
}
|
||||
cs, err := dockerCli.Client().ContainerList(ctx, options)
|
||||
if err != nil {
|
||||
closeChan <- err
|
||||
}
|
||||
for _, ctr := range cs {
|
||||
s := NewStats(ctr.ID[:12])
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showAll := len(options.Containers) == 0
|
||||
if showAll {
|
||||
// If no names were specified, start a long running goroutine which
|
||||
// If no names were specified, start a long-running goroutine which
|
||||
// monitors container events. We make sure we're subscribed before
|
||||
// retrieving the list of running containers to avoid a race where we
|
||||
// would "miss" a creation.
|
||||
started := make(chan struct{})
|
||||
|
||||
if options.Filters == nil {
|
||||
f := filters.NewArgs()
|
||||
options.Filters = &f
|
||||
}
|
||||
|
||||
if err := options.Filters.Validate(acceptedStatsFilters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eh := command.InitEventHandler()
|
||||
eh.Handle(events.ActionCreate, func(e events.Message) {
|
||||
if opts.all {
|
||||
if options.All {
|
||||
eh.Handle(events.ActionCreate, func(e events.Message) {
|
||||
s := NewStats(e.Actor.ID[:12])
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst)
|
||||
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
eh.Handle(events.ActionStart, func(e events.Message) {
|
||||
s := NewStats(e.Actor.ID[:12])
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst)
|
||||
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
|
||||
}
|
||||
})
|
||||
|
||||
eh.Handle(events.ActionDie, func(e events.Message) {
|
||||
if !opts.all {
|
||||
if !options.All {
|
||||
eh.Handle(events.ActionDie, func(e events.Message) {
|
||||
cStats.remove(e.Actor.ID[:12])
|
||||
})
|
||||
}
|
||||
|
||||
// monitorContainerEvents watches for container creation and removal (only
|
||||
// used when calling `docker stats` without arguments).
|
||||
monitorContainerEvents := func(started chan<- struct{}, c chan events.Message, stopped <-chan struct{}) {
|
||||
// Create a copy of the custom filters so that we don't mutate
|
||||
// the original set of filters. Custom filters are used both
|
||||
// to list containers and to filter events, but the "type" filter
|
||||
// is not valid for filtering containers.
|
||||
f := options.Filters.Clone()
|
||||
f.Add("type", string(events.ContainerEventType))
|
||||
eventChan, errChan := apiClient.Events(ctx, types.EventsOptions{
|
||||
Filters: f,
|
||||
})
|
||||
|
||||
// Whether we successfully subscribed to eventChan or not, we can now
|
||||
// unblock the main goroutine.
|
||||
close(started)
|
||||
defer close(c)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopped:
|
||||
return
|
||||
case event := <-eventChan:
|
||||
c <- event
|
||||
case err := <-errChan:
|
||||
closeChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
eventChan := make(chan events.Message)
|
||||
go eh.Watch(eventChan)
|
||||
@ -165,20 +201,42 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
defer close(stopped)
|
||||
<-started
|
||||
|
||||
// Start a short-lived goroutine to retrieve the initial list of
|
||||
// containers.
|
||||
getContainerList()
|
||||
// Fetch the initial list of containers and collect stats for them.
|
||||
// After the initial list was collected, we start listening for events
|
||||
// to refresh the list of containers.
|
||||
cs, err := apiClient.ContainerList(ctx, container.ListOptions{
|
||||
All: options.All,
|
||||
Filters: *options.Filters,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ctr := range cs {
|
||||
s := NewStats(ctr.ID[:12])
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
|
||||
}
|
||||
}
|
||||
|
||||
// make sure each container get at least one valid stat data
|
||||
waitFirst.Wait()
|
||||
} else {
|
||||
// Artificially send creation events for the containers we were asked to
|
||||
// monitor (same code path than we use when monitoring all containers).
|
||||
for _, name := range opts.containers {
|
||||
s := NewStats(name)
|
||||
// TODO(thaJeztah): re-implement options.Containers as a filter so that
|
||||
// only a single code-path is needed, and custom filters can be combined
|
||||
// with a list of container names/IDs.
|
||||
|
||||
if options.Filters != nil && options.Filters.Len() > 0 {
|
||||
return fmt.Errorf("filtering is not supported when specifying a list of containers")
|
||||
}
|
||||
|
||||
// Create the list of containers, and start collecting stats for all
|
||||
// containers passed.
|
||||
for _, ctr := range options.Containers {
|
||||
s := NewStats(ctr)
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst)
|
||||
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,22 +259,22 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
format := opts.format
|
||||
format := options.Format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCli.ConfigFile().StatsFormat) > 0 {
|
||||
format = dockerCli.ConfigFile().StatsFormat
|
||||
if len(dockerCLI.ConfigFile().StatsFormat) > 0 {
|
||||
format = dockerCLI.ConfigFile().StatsFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
statsCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Output: dockerCLI.Out(),
|
||||
Format: NewStatsFormat(format, daemonOSType),
|
||||
}
|
||||
cleanScreen := func() {
|
||||
if !opts.noStream {
|
||||
fmt.Fprint(dockerCli.Out(), "\033[2J")
|
||||
fmt.Fprint(dockerCli.Out(), "\033[H")
|
||||
if !options.NoStream {
|
||||
_, _ = fmt.Fprint(dockerCLI.Out(), "\033[2J")
|
||||
_, _ = fmt.Fprint(dockerCLI.Out(), "\033[H")
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,28 +283,28 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
cleanScreen()
|
||||
ccstats := []StatsEntry{}
|
||||
var ccStats []StatsEntry
|
||||
cStats.mu.RLock()
|
||||
for _, c := range cStats.cs {
|
||||
ccstats = append(ccstats, c.GetStatistics())
|
||||
ccStats = append(ccStats, c.GetStatistics())
|
||||
}
|
||||
cStats.mu.RUnlock()
|
||||
if err = statsFormatWrite(statsCtx, ccstats, daemonOSType, !opts.noTrunc); err != nil {
|
||||
if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
|
||||
break
|
||||
}
|
||||
if len(cStats.cs) == 0 && !showAll {
|
||||
break
|
||||
}
|
||||
if opts.noStream {
|
||||
if options.NoStream {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case err, ok := <-closeChan:
|
||||
if ok {
|
||||
if err != nil {
|
||||
// this is suppressing "unexpected EOF" in the cli when the
|
||||
// daemon restarts so it shutdowns cleanly
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
// Suppress "unexpected EOF" errors in the CLI so that
|
||||
// it shuts down cleanly when the daemon restarts.
|
||||
if errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
||||
@ -206,9 +206,9 @@ func calculateBlockIO(blkio types.BlkioStats) (uint64, uint64) {
|
||||
}
|
||||
switch bioEntry.Op[0] {
|
||||
case 'r', 'R':
|
||||
blkRead = blkRead + bioEntry.Value
|
||||
blkRead += bioEntry.Value
|
||||
case 'w', 'W':
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
blkWrite += bioEntry.Value
|
||||
}
|
||||
}
|
||||
return blkRead, blkWrite
|
||||
|
||||
@ -32,7 +32,7 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
opts.timeoutChanged = cmd.Flags().Changed("time")
|
||||
return runStop(dockerCli, &opts)
|
||||
return runStop(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container stop, docker stop",
|
||||
@ -46,13 +46,13 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(dockerCli command.Cli, opts *stopOptions) error {
|
||||
func runStop(ctx context.Context, dockerCli command.Cli, opts *stopOptions) error {
|
||||
var timeout *int
|
||||
if opts.timeoutChanged {
|
||||
timeout = &opts.timeout
|
||||
}
|
||||
|
||||
errChan := parallelOperation(context.Background(), opts.containers, func(ctx context.Context, id string) error {
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error {
|
||||
return dockerCli.Client().ContainerStop(ctx, id, container.StopOptions{
|
||||
Signal: opts.signal,
|
||||
Timeout: timeout,
|
||||
|
||||
@ -29,7 +29,7 @@ func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
opts.args = args[1:]
|
||||
return runTop(dockerCli, &opts)
|
||||
return runTop(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container top, docker top",
|
||||
@ -43,9 +43,7 @@ func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runTop(dockerCli command.Cli, opts *topOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runTop(ctx context.Context, dockerCli command.Cli, opts *topOptions) error {
|
||||
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// resizeTtyTo resizes tty to specific height and width
|
||||
func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) error {
|
||||
func resizeTtyTo(ctx context.Context, apiClient client.ContainerAPIClient, id string, height, width uint, isExec bool) error {
|
||||
if height == 0 && width == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -28,9 +28,9 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin
|
||||
|
||||
var err error
|
||||
if isExec {
|
||||
err = client.ContainerExecResize(ctx, id, options)
|
||||
err = apiClient.ContainerExecResize(ctx, id, options)
|
||||
} else {
|
||||
err = client.ContainerResize(ctx, id, options)
|
||||
err = apiClient.ContainerResize(ctx, id, options)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@ -27,7 +27,7 @@ func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runUnpause(dockerCli, &opts)
|
||||
return runUnpause(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container unpause, docker unpause",
|
||||
@ -39,9 +39,7 @@ func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnpause(dockerCli command.Cli, opts *unpauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runUnpause(ctx context.Context, dockerCli command.Cli, opts *unpauseOptions) error {
|
||||
var errs []string
|
||||
errChan := parallelOperation(ctx, opts.containers, dockerCli.Client().ContainerUnpause)
|
||||
for _, container := range opts.containers {
|
||||
|
||||
@ -47,7 +47,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.containers = args
|
||||
options.nFlag = cmd.Flags().NFlag()
|
||||
return runUpdate(dockerCli, &options)
|
||||
return runUpdate(cmd.Context(), dockerCli, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container update, docker update",
|
||||
@ -86,7 +86,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdate(dockerCli command.Cli, options *updateOptions) error {
|
||||
func runUpdate(ctx context.Context, dockerCli command.Cli, options *updateOptions) error {
|
||||
var err error
|
||||
|
||||
if options.nFlag == 0 {
|
||||
@ -126,8 +126,6 @@ func runUpdate(dockerCli command.Cli, options *updateOptions) error {
|
||||
RestartPolicy: restartPolicy,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var (
|
||||
warns []string
|
||||
errs []string
|
||||
|
||||
@ -81,18 +81,16 @@ func legacyWaitExitOrRemoved(ctx context.Context, apiClient client.APIClient, co
|
||||
}
|
||||
if !waitRemove {
|
||||
stopProcessing = true
|
||||
} else {
|
||||
} else if versions.LessThan(apiClient.ClientVersion(), "1.25") {
|
||||
// If we are talking to an older daemon, `AutoRemove` is not supported.
|
||||
// We need to fall back to the old behavior, which is client-side removal
|
||||
if versions.LessThan(apiClient.ClientVersion(), "1.25") {
|
||||
go func() {
|
||||
removeErr = apiClient.ContainerRemove(ctx, containerID, container.RemoveOptions{RemoveVolumes: true})
|
||||
if removeErr != nil {
|
||||
logrus.Errorf("error removing container: %v", removeErr)
|
||||
cancel() // cancel the event Q
|
||||
}
|
||||
}()
|
||||
}
|
||||
go func() {
|
||||
removeErr = apiClient.ContainerRemove(ctx, containerID, container.RemoveOptions{RemoveVolumes: true})
|
||||
if removeErr != nil {
|
||||
logrus.Errorf("error removing container: %v", removeErr)
|
||||
cancel() // cancel the event Q
|
||||
}
|
||||
}()
|
||||
}
|
||||
case "detach":
|
||||
exitCode = 0
|
||||
@ -129,7 +127,7 @@ func legacyWaitExitOrRemoved(ctx context.Context, apiClient client.APIClient, co
|
||||
return statusChan
|
||||
}
|
||||
|
||||
func parallelOperation(ctx context.Context, containers []string, op func(ctx context.Context, container string) error) chan error {
|
||||
func parallelOperation(ctx context.Context, containers []string, op func(ctx context.Context, containerID string) error) chan error {
|
||||
if len(containers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runWait(dockerCli, &opts)
|
||||
return runWait(cmd.Context(), dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container wait, docker wait",
|
||||
@ -37,9 +37,7 @@ func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(dockerCli command.Cli, opts *waitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func runWait(ctx context.Context, dockerCli command.Cli, opts *waitOptions) error {
|
||||
var errs []string
|
||||
for _, container := range opts.containers {
|
||||
resultC, errC := dockerCli.Client().ContainerWait(ctx, container, "")
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -10,12 +13,12 @@ import (
|
||||
// DockerContext is a typed representation of what we put in Context metadata
|
||||
type DockerContext struct {
|
||||
Description string
|
||||
AdditionalFields map[string]interface{}
|
||||
AdditionalFields map[string]any
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON marshalling
|
||||
func (dc DockerContext) MarshalJSON() ([]byte, error) {
|
||||
s := map[string]interface{}{}
|
||||
s := map[string]any{}
|
||||
if dc.Description != "" {
|
||||
s["Description"] = dc.Description
|
||||
}
|
||||
@ -29,7 +32,7 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements custom JSON marshalling
|
||||
func (dc *DockerContext) UnmarshalJSON(payload []byte) error {
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal(payload, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -39,7 +42,7 @@ func (dc *DockerContext) UnmarshalJSON(payload []byte) error {
|
||||
dc.Description = v.(string)
|
||||
default:
|
||||
if dc.AdditionalFields == nil {
|
||||
dc.AdditionalFields = make(map[string]interface{})
|
||||
dc.AdditionalFields = make(map[string]any)
|
||||
}
|
||||
dc.AdditionalFields[k] = v
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
@ -36,7 +39,7 @@ func longCreateDescription() string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
opts := &CreateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] CONTEXT",
|
||||
@ -44,7 +47,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Name = args[0]
|
||||
return RunCreate(dockerCli, opts)
|
||||
return RunCreate(dockerCLI, opts)
|
||||
},
|
||||
Long: longCreateDescription(),
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
@ -57,23 +60,23 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunCreate creates a Docker context
|
||||
func RunCreate(cli command.Cli, o *CreateOptions) error {
|
||||
s := cli.ContextStore()
|
||||
func RunCreate(dockerCLI command.Cli, o *CreateOptions) error {
|
||||
s := dockerCLI.ContextStore()
|
||||
err := checkContextNameForCreation(s, o.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case o.From == "" && o.Docker == nil:
|
||||
err = createFromExistingContext(s, cli.CurrentContext(), o)
|
||||
err = createFromExistingContext(s, dockerCLI.CurrentContext(), o)
|
||||
case o.From != "":
|
||||
err = createFromExistingContext(s, o.From, o)
|
||||
default:
|
||||
err = createNewContext(s, o)
|
||||
}
|
||||
if err == nil {
|
||||
fmt.Fprintln(cli.Out(), o.Name)
|
||||
fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.Name)
|
||||
fmt.Fprintln(dockerCLI.Out(), o.Name)
|
||||
fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", o.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -87,7 +90,7 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
|
||||
return errors.Wrap(err, "unable to create docker endpoint config")
|
||||
}
|
||||
contextMetadata := store.Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
Endpoints: map[string]any{
|
||||
docker.DockerEndpoint: dockerEP,
|
||||
},
|
||||
Metadata: command.DockerContext{
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
@ -16,15 +19,15 @@ func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) *test.FakeCli {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
storeConfig := store.NewConfig(
|
||||
func() interface{} { return &command.DockerContext{} },
|
||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
||||
func() any { return &command.DockerContext{} },
|
||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() any { return &docker.EndpointMeta{} }),
|
||||
)
|
||||
store := &command.ContextStoreWithDefault{
|
||||
contextStore := &command.ContextStoreWithDefault{
|
||||
Store: store.New(dir, storeConfig),
|
||||
Resolver: func() (*command.DefaultContext, error) {
|
||||
return &command.DefaultContext{
|
||||
Meta: store.Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
Endpoints: map[string]any{
|
||||
docker.DockerEndpoint: docker.EndpointMeta{
|
||||
Host: "unix:///var/run/docker.sock",
|
||||
},
|
||||
@ -42,7 +45,7 @@ func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) *test.FakeCli {
|
||||
for _, o := range opts {
|
||||
o(result)
|
||||
}
|
||||
result.SetContextStore(store)
|
||||
result.SetContextStore(contextStore)
|
||||
return result
|
||||
}
|
||||
|
||||
@ -104,6 +107,7 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
|
||||
t.Helper()
|
||||
assert.Equal(t, n+"\n", cli.OutBuffer().String())
|
||||
assert.Equal(t, fmt.Sprintf("Successfully created context %q\n", n), cli.ErrBuffer().String())
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
@ -40,7 +43,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
getRefFunc := func(ref string) (interface{}, []byte, error) {
|
||||
getRefFunc := func(ref string) (any, []byte, error) {
|
||||
c, err := dockerCli.ContextStore().GetMetadata(ref)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@ -17,7 +17,7 @@ func TestInspect(t *testing.T) {
|
||||
}))
|
||||
expected := string(golden.Get(t, "inspect.golden"))
|
||||
si := cli.ContextStore().GetStorageInfo("current")
|
||||
expected = strings.Replace(expected, "<METADATA_PATH>", strings.Replace(si.MetadataPath, `\`, `\\`, -1), 1)
|
||||
expected = strings.Replace(expected, "<TLS_PATH>", strings.Replace(si.TLSPath, `\`, `\\`, -1), 1)
|
||||
expected = strings.Replace(expected, "<METADATA_PATH>", strings.ReplaceAll(si.MetadataPath, `\`, `\\`), 1)
|
||||
expected = strings.Replace(expected, "<TLS_PATH>", strings.ReplaceAll(si.TLSPath, `\`, `\\`), 1)
|
||||
assert.Equal(t, cli.OutBuffer().String(), expected)
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
||||
var (
|
||||
curContext = dockerCli.CurrentContext()
|
||||
curFound bool
|
||||
contexts []*formatter.ClientContext
|
||||
contexts = make([]*formatter.ClientContext, 0, len(contextMap))
|
||||
)
|
||||
for _, rawMeta := range contextMap {
|
||||
isCurrent := rawMeta.Name == curContext
|
||||
|
||||
@ -52,11 +52,11 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunUpdate updates a Docker context
|
||||
func RunUpdate(cli command.Cli, o *UpdateOptions) error {
|
||||
func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
|
||||
if err := store.ValidateContextName(o.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
s := cli.ContextStore()
|
||||
s := dockerCLI.ContextStore()
|
||||
c, err := s.GetMetadata(o.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -93,8 +93,8 @@ func RunUpdate(cli command.Cli, o *UpdateOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(cli.Out(), o.Name)
|
||||
fmt.Fprintf(cli.Err(), "Successfully updated context %q\n", o.Name)
|
||||
fmt.Fprintln(dockerCLI.Out(), o.Name)
|
||||
fmt.Fprintf(dockerCLI.Err(), "Successfully updated context %q\n", o.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -10,7 +13,7 @@ import (
|
||||
func TestDockerContextMetadataKeepAdditionalFields(t *testing.T) {
|
||||
c := DockerContext{
|
||||
Description: "test",
|
||||
AdditionalFields: map[string]interface{}{
|
||||
AdditionalFields: map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -44,7 +47,9 @@ type EndpointDefaultResolver interface {
|
||||
// the lack of a default (e.g. because the config file which
|
||||
// would contain it is missing). If there is no default then
|
||||
// returns nil, nil, nil.
|
||||
ResolveDefault() (interface{}, *store.EndpointTLSData, error)
|
||||
//
|
||||
//nolint:dupword // ignore "Duplicate words (nil,) found"
|
||||
ResolveDefault() (any, *store.EndpointTLSData, error)
|
||||
}
|
||||
|
||||
// ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
|
||||
@ -53,7 +58,7 @@ func ResolveDefaultContext(opts *cliflags.ClientOptions, config store.Config) (*
|
||||
Endpoints: make(map[string]store.EndpointTLSData),
|
||||
}
|
||||
contextMetadata := store.Metadata{
|
||||
Endpoints: make(map[string]interface{}),
|
||||
Endpoints: make(map[string]any),
|
||||
Metadata: DockerContext{
|
||||
Description: "",
|
||||
},
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -23,14 +26,14 @@ type testContext struct {
|
||||
Bar string `json:"another_very_recognizable_field_name"`
|
||||
}
|
||||
|
||||
var testCfg = store.NewConfig(func() interface{} { return &testContext{} },
|
||||
store.EndpointTypeGetter("ep1", func() interface{} { return &endpoint{} }),
|
||||
store.EndpointTypeGetter("ep2", func() interface{} { return &endpoint{} }),
|
||||
var testCfg = store.NewConfig(func() any { return &testContext{} },
|
||||
store.EndpointTypeGetter("ep1", func() any { return &endpoint{} }),
|
||||
store.EndpointTypeGetter("ep2", func() any { return &endpoint{} }),
|
||||
)
|
||||
|
||||
func testDefaultMetadata() store.Metadata {
|
||||
return store.Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
Endpoints: map[string]any{
|
||||
"ep1": endpoint{Foo: "bar"},
|
||||
},
|
||||
Metadata: testContext{Bar: "baz"},
|
||||
@ -149,7 +152,7 @@ func TestErrCreateDefault(t *testing.T) {
|
||||
meta := testDefaultMetadata()
|
||||
s := testStore(t, meta, store.ContextTLSData{})
|
||||
err := s.CreateOrUpdate(store.Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
Endpoints: map[string]any{
|
||||
"ep1": endpoint{Foo: "bar"},
|
||||
},
|
||||
Metadata: testContext{Bar: "baz"},
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -171,13 +171,13 @@ func (c *buildCacheContext) LastUsedSince() string {
|
||||
}
|
||||
|
||||
func (c *buildCacheContext) UsageCount() string {
|
||||
return fmt.Sprintf("%d", c.v.UsageCount)
|
||||
return strconv.Itoa(c.v.UsageCount)
|
||||
}
|
||||
|
||||
func (c *buildCacheContext) InUse() string {
|
||||
return fmt.Sprintf("%t", c.v.InUse)
|
||||
return strconv.FormatBool(c.v.InUse)
|
||||
}
|
||||
|
||||
func (c *buildCacheContext) Shared() string {
|
||||
return fmt.Sprintf("%t", c.v.Shared)
|
||||
return strconv.FormatBool(c.v.Shared)
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -86,7 +89,7 @@ type ContainerContext struct {
|
||||
// used in the template. It's currently only used to detect use of the .Size
|
||||
// field which (if used) automatically sets the '--size' option when making
|
||||
// the API call.
|
||||
FieldsUsed map[string]interface{}
|
||||
FieldsUsed map[string]any
|
||||
}
|
||||
|
||||
// NewContainerContext creates a new context for rendering containers
|
||||
@ -226,7 +229,7 @@ func (c *ContainerContext) Status() string {
|
||||
// Size returns the container's size and virtual size (e.g. "2B (virtual 21.5MB)")
|
||||
func (c *ContainerContext) Size() string {
|
||||
if c.FieldsUsed == nil {
|
||||
c.FieldsUsed = map[string]interface{}{}
|
||||
c.FieldsUsed = map[string]any{}
|
||||
}
|
||||
c.FieldsUsed["Size"] = struct{}{}
|
||||
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
|
||||
@ -245,7 +248,7 @@ func (c *ContainerContext) Labels() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var joinLabels []string
|
||||
joinLabels := make([]string, 0, len(c.c.Labels))
|
||||
for k, v := range c.c.Labels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
@ -265,7 +268,7 @@ func (c *ContainerContext) Label(name string) string {
|
||||
// If the trunc option is set, names can be truncated (ellipsized).
|
||||
func (c *ContainerContext) Mounts() string {
|
||||
var name string
|
||||
var mounts []string
|
||||
mounts := make([]string, 0, len(c.c.Mounts))
|
||||
for _, m := range c.c.Mounts {
|
||||
if m.Name == "" {
|
||||
name = m.Source
|
||||
@ -289,7 +292,7 @@ func (c *ContainerContext) LocalVolumes() string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d", count)
|
||||
return strconv.Itoa(count)
|
||||
}
|
||||
|
||||
// Networks returns a comma-separated string of networks that the container is
|
||||
@ -299,7 +302,7 @@ func (c *ContainerContext) Networks() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
networks := []string{}
|
||||
networks := make([]string, 0, len(c.c.NetworkSettings.Networks))
|
||||
for k := range c.c.NetworkSettings.Networks {
|
||||
networks = append(networks, k)
|
||||
}
|
||||
@ -316,7 +319,7 @@ func DisplayablePorts(ports []types.Port) string {
|
||||
last uint16
|
||||
}
|
||||
groupMap := make(map[string]*portGroup)
|
||||
var result []string
|
||||
var result []string //nolint:prealloc
|
||||
var hostMappings []string
|
||||
var groupMapKeys []string
|
||||
sort.Slice(ports, func(i, j int) bool {
|
||||
@ -331,7 +334,7 @@ func DisplayablePorts(ports []types.Port) string {
|
||||
hostMappings = append(hostMappings, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
|
||||
continue
|
||||
}
|
||||
portKey = fmt.Sprintf("%s/%s", port.IP, port.Type)
|
||||
portKey = port.IP + "/" + port.Type
|
||||
}
|
||||
group := groupMap[portKey]
|
||||
|
||||
@ -372,7 +375,7 @@ func formGroup(key string, start, last uint16) string {
|
||||
if ip != "" {
|
||||
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", group, groupType)
|
||||
return group + "/" + groupType
|
||||
}
|
||||
|
||||
func comparePorts(i, j types.Port) bool {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -265,7 +268,6 @@ size: 0B
|
||||
assert.Equal(t, out.String(), tc.expected)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,7 +342,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||
{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unix, State: "running"},
|
||||
}
|
||||
expectedCreated := time.Unix(unix, 0).String()
|
||||
expectedJSONs := []map[string]interface{}{
|
||||
expectedJSONs := []map[string]any{
|
||||
{
|
||||
"Command": "\"\"",
|
||||
"CreatedAt": expectedCreated,
|
||||
@ -381,7 +383,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||
}
|
||||
for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
||||
msg := fmt.Sprintf("Output: line %d: %s", i, line)
|
||||
var m map[string]interface{}
|
||||
var m map[string]any
|
||||
err := json.Unmarshal([]byte(line), &m)
|
||||
assert.NilError(t, err, msg)
|
||||
assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg)
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import "strings"
|
||||
@ -22,7 +25,7 @@ const (
|
||||
|
||||
// SubContext defines what Context implementation should provide
|
||||
type SubContext interface {
|
||||
FullHeader() interface{}
|
||||
FullHeader() any
|
||||
}
|
||||
|
||||
// SubHeaderContext is a map destined to formatter header (table format)
|
||||
@ -39,10 +42,10 @@ func (c SubHeaderContext) Label(name string) string {
|
||||
|
||||
// HeaderContext provides the subContext interface for managing headers
|
||||
type HeaderContext struct {
|
||||
Header interface{}
|
||||
Header any
|
||||
}
|
||||
|
||||
// FullHeader returns the header as an interface
|
||||
func (c *HeaderContext) FullHeader() interface{} {
|
||||
func (c *HeaderContext) FullHeader() any {
|
||||
return c.Header
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ func Ellipsis(s string, maxDisplayWidth int) string {
|
||||
}
|
||||
|
||||
var (
|
||||
display []int
|
||||
display = make([]int, 0, len(rs))
|
||||
displayWidth int
|
||||
)
|
||||
for _, r := range rs {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -51,7 +54,7 @@ type Context struct {
|
||||
|
||||
// internal element
|
||||
finalFormat string
|
||||
header interface{}
|
||||
header any
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -25,7 +28,7 @@ type fakeSubContext struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (f fakeSubContext) FullHeader() interface{} {
|
||||
func (f fakeSubContext) FullHeader() any {
|
||||
return map[string]string{"Name": "NAME"}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
@ -26,11 +26,11 @@ type ImageContext struct {
|
||||
Digest bool
|
||||
}
|
||||
|
||||
func isDangling(image image.Summary) bool {
|
||||
if len(image.RepoTags) == 0 && len(image.RepoDigests) == 0 {
|
||||
func isDangling(img image.Summary) bool {
|
||||
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {
|
||||
return true
|
||||
}
|
||||
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
|
||||
return len(img.RepoTags) == 1 && img.RepoTags[0] == "<none>:<none>" && len(img.RepoDigests) == 1 && img.RepoDigests[0] == "<none>@<none>"
|
||||
}
|
||||
|
||||
// NewImageFormat returns a format for rendering an ImageContext
|
||||
@ -88,18 +88,18 @@ func needDigest(ctx ImageContext) bool {
|
||||
}
|
||||
|
||||
func imageFormat(ctx ImageContext, images []image.Summary, format func(subContext SubContext) error) error {
|
||||
for _, image := range images {
|
||||
for _, img := range images {
|
||||
formatted := []*imageContext{}
|
||||
if isDangling(image) {
|
||||
if isDangling(img) {
|
||||
formatted = append(formatted, &imageContext{
|
||||
trunc: ctx.Trunc,
|
||||
i: image,
|
||||
i: img,
|
||||
repo: "<none>",
|
||||
tag: "<none>",
|
||||
digest: "<none>",
|
||||
})
|
||||
} else {
|
||||
formatted = imageFormatTaggedAndDigest(ctx, image)
|
||||
formatted = imageFormatTaggedAndDigest(ctx, img)
|
||||
}
|
||||
for _, imageCtx := range formatted {
|
||||
if err := format(imageCtx); err != nil {
|
||||
@ -110,12 +110,12 @@ func imageFormat(ctx ImageContext, images []image.Summary, format func(subContex
|
||||
return nil
|
||||
}
|
||||
|
||||
func imageFormatTaggedAndDigest(ctx ImageContext, image image.Summary) []*imageContext {
|
||||
func imageFormatTaggedAndDigest(ctx ImageContext, img image.Summary) []*imageContext {
|
||||
repoTags := map[string][]string{}
|
||||
repoDigests := map[string][]string{}
|
||||
images := []*imageContext{}
|
||||
|
||||
for _, refString := range image.RepoTags {
|
||||
for _, refString := range img.RepoTags {
|
||||
ref, err := reference.ParseNormalizedNamed(refString)
|
||||
if err != nil {
|
||||
continue
|
||||
@ -125,7 +125,7 @@ func imageFormatTaggedAndDigest(ctx ImageContext, image image.Summary) []*imageC
|
||||
repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
|
||||
}
|
||||
}
|
||||
for _, refString := range image.RepoDigests {
|
||||
for _, refString := range img.RepoDigests {
|
||||
ref, err := reference.ParseNormalizedNamed(refString)
|
||||
if err != nil {
|
||||
continue
|
||||
@ -139,7 +139,7 @@ func imageFormatTaggedAndDigest(ctx ImageContext, image image.Summary) []*imageC
|
||||
addImage := func(repo, tag, digest string) {
|
||||
images = append(images, &imageContext{
|
||||
trunc: ctx.Trunc,
|
||||
i: image,
|
||||
i: img,
|
||||
repo: repo,
|
||||
tag: tag,
|
||||
digest: digest,
|
||||
@ -166,7 +166,6 @@ func imageFormatTaggedAndDigest(ctx ImageContext, image image.Summary) []*imageC
|
||||
for _, dgst := range digests {
|
||||
addImage(repo, tag, dgst)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +255,7 @@ func (c *imageContext) Containers() string {
|
||||
if c.i.Containers == -1 {
|
||||
return "N/A"
|
||||
}
|
||||
return fmt.Sprintf("%d", c.i.Containers)
|
||||
return strconv.FormatInt(c.i.Containers, 10)
|
||||
}
|
||||
|
||||
// VirtualSize shows the virtual size of the image and all of its parent
|
||||
|
||||
@ -72,7 +72,7 @@ func TestImageContext(t *testing.T) {
|
||||
{
|
||||
imageCtx: imageContext{i: image.Summary{Size: 10000}},
|
||||
expValue: "10kB",
|
||||
call: ctx.VirtualSize, //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
|
||||
call: ctx.VirtualSize, //nolint:nolintlint,staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
|
||||
},
|
||||
{
|
||||
imageCtx: imageContext{i: image.Summary{SharedSize: 10000}},
|
||||
@ -148,7 +148,7 @@ image tag2 imageID2 N/A 0B
|
||||
Format: NewImageFormat("table {{.Repository}}", false, false),
|
||||
},
|
||||
},
|
||||
"REPOSITORY\nimage\nimage\n<none>\n",
|
||||
"REPOSITORY\nimage\nimage\n<none>\n", //nolint:dupword // ignore "Duplicate words (image) found"
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@ -169,7 +169,7 @@ image <none>
|
||||
Format: NewImageFormat("table {{.Repository}}", true, false),
|
||||
},
|
||||
},
|
||||
"REPOSITORY\nimage\nimage\n<none>\n",
|
||||
"REPOSITORY\nimage\nimage\n<none>\n", //nolint:dupword // ignore "Duplicate words (image) found"
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@ -284,7 +284,7 @@ image_id: imageID3
|
||||
Format: NewImageFormat("{{.Repository}}", false, false),
|
||||
},
|
||||
},
|
||||
"image\nimage\n<none>\n",
|
||||
"image\nimage\n<none>\n", //nolint:dupword // ignore "Duplicate words (image) found"
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@ -293,7 +293,7 @@ image_id: imageID3
|
||||
},
|
||||
Digest: true,
|
||||
},
|
||||
"image\nimage\n<none>\n",
|
||||
"image\nimage\n<none>\n", //nolint:dupword // ignore "Duplicate words (image) found"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -10,7 +13,7 @@ import (
|
||||
|
||||
// MarshalJSON marshals x into json
|
||||
// It differs a bit from encoding/json MarshalJSON function for formatter
|
||||
func MarshalJSON(x interface{}) ([]byte, error) {
|
||||
func MarshalJSON(x any) ([]byte, error) {
|
||||
m, err := marshalMap(x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -18,8 +21,8 @@ func MarshalJSON(x interface{}) ([]byte, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// marshalMap marshals x to map[string]interface{}
|
||||
func marshalMap(x interface{}) (map[string]interface{}, error) {
|
||||
// marshalMap marshals x to map[string]any
|
||||
func marshalMap(x any) (map[string]any, error) {
|
||||
val := reflect.ValueOf(x)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return nil, errors.Errorf("expected a pointer to a struct, got %v", val.Kind())
|
||||
@ -32,7 +35,7 @@ func marshalMap(x interface{}) (map[string]interface{}, error) {
|
||||
return nil, errors.Errorf("expected a pointer to a struct, got a pointer to %v", valElem.Kind())
|
||||
}
|
||||
typ := val.Type()
|
||||
m := make(map[string]interface{})
|
||||
m := make(map[string]any)
|
||||
for i := 0; i < val.NumMethod(); i++ {
|
||||
k, v, err := marshalForMethod(typ.Method(i), val.Method(i))
|
||||
if err != nil {
|
||||
@ -49,7 +52,7 @@ var unmarshallableNames = map[string]struct{}{"FullHeader": {}}
|
||||
|
||||
// marshalForMethod returns the map key and the map value for marshalling the method.
|
||||
// It returns ("", nil, nil) for valid but non-marshallable parameter. (e.g. "unexportedFunc()")
|
||||
func marshalForMethod(typ reflect.Method, val reflect.Value) (string, interface{}, error) {
|
||||
func marshalForMethod(typ reflect.Method, val reflect.Value) (string, any, error) {
|
||||
if val.Kind() != reflect.Func {
|
||||
return "", nil, errors.Errorf("expected func, got %v", val.Kind())
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -33,7 +36,7 @@ func (d *dummy) FullHeader() string {
|
||||
return "FullHeader(should not be marshalled)"
|
||||
}
|
||||
|
||||
var dummyExpected = map[string]interface{}{
|
||||
var dummyExpected = map[string]any{
|
||||
"Func1": "Func1",
|
||||
"Func4": 4,
|
||||
"Func5": dummyType("Func5"),
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
// based on https://github.com/golang/go/blob/master/src/text/tabwriter/tabwriter.go Last modified 690ac40 on 31 Jan
|
||||
|
||||
//nolint:gocyclo,nakedret,revive,stylecheck,unused // ignore linting errors, so that we can stick close to upstream
|
||||
//nolint:gocyclo,nakedret,stylecheck,unused // ignore linting errors, so that we can stick close to upstream
|
||||
package tabwriter
|
||||
|
||||
import (
|
||||
@ -202,7 +202,7 @@ const (
|
||||
//
|
||||
// minwidth minimal cell width including any padding
|
||||
// tabwidth width of tab characters (equivalent number of spaces)
|
||||
// padding padding added to a cell before computing its width
|
||||
// padding the padding added to a cell before computing its width
|
||||
// padchar ASCII char used for padding
|
||||
// if padchar == '\t', the Writer will assume that the
|
||||
// width of a '\t' in the formatted output is tabwidth,
|
||||
@ -576,18 +576,16 @@ func (b *Writer) Write(buf []byte) (n int, err error) {
|
||||
b.startEscape(ch)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if ch == b.endChar {
|
||||
// inside escape
|
||||
if ch == b.endChar {
|
||||
// end of tag/entity
|
||||
j := i + 1
|
||||
if ch == Escape && b.flags&StripEscape != 0 {
|
||||
j = i // strip Escape
|
||||
}
|
||||
b.append(buf[n:j])
|
||||
n = i + 1 // ch consumed
|
||||
b.endEscape()
|
||||
// end of tag/entity
|
||||
j := i + 1
|
||||
if ch == Escape && b.flags&StripEscape != 0 {
|
||||
j = i // strip Escape
|
||||
}
|
||||
b.append(buf[n:j])
|
||||
n = i + 1 // ch consumed
|
||||
b.endEscape()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -36,6 +37,7 @@ func (b *buffer) Write(buf []byte) (written int, err error) {
|
||||
func (b *buffer) String() string { return string(b.a) }
|
||||
|
||||
func write(t *testing.T, testname string, w *Writer, src string) {
|
||||
t.Helper()
|
||||
written, err := io.WriteString(w, src)
|
||||
if err != nil {
|
||||
t.Errorf("--- test: %s\n--- src:\n%q\n--- write error: %v\n", testname, src, err)
|
||||
@ -46,6 +48,7 @@ func write(t *testing.T, testname string, w *Writer, src string) {
|
||||
}
|
||||
|
||||
func verify(t *testing.T, testname string, w *Writer, b *buffer, src, expected string) {
|
||||
t.Helper()
|
||||
err := w.Flush()
|
||||
if err != nil {
|
||||
t.Errorf("--- test: %s\n--- src:\n%q\n--- flush error: %v\n", testname, src, err)
|
||||
@ -58,6 +61,7 @@ func verify(t *testing.T, testname string, w *Writer, b *buffer, src, expected s
|
||||
}
|
||||
|
||||
func check(t *testing.T, testname string, minwidth, tabwidth, padding int, padchar byte, flags uint, src, expected string) {
|
||||
t.Helper()
|
||||
var b buffer
|
||||
b.init(1000)
|
||||
|
||||
@ -622,6 +626,7 @@ func (panicWriter) Write([]byte) (int, error) {
|
||||
}
|
||||
|
||||
func wantPanicString(t *testing.T, want string) {
|
||||
t.Helper()
|
||||
if e := recover(); e != nil {
|
||||
got, ok := e.(string)
|
||||
switch {
|
||||
@ -691,7 +696,7 @@ func BenchmarkPyramid(b *testing.B) {
|
||||
for _, x := range [...]int{10, 100, 1000} {
|
||||
// Build a line with x cells.
|
||||
line := bytes.Repeat([]byte("a\t"), x)
|
||||
b.Run(fmt.Sprintf("%d", x), func(b *testing.B) {
|
||||
b.Run(strconv.Itoa(x), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
@ -713,7 +718,7 @@ func BenchmarkRagged(b *testing.B) {
|
||||
lines[i] = bytes.Repeat([]byte("a\t"), w)
|
||||
}
|
||||
for _, h := range [...]int{10, 100, 1000} {
|
||||
b.Run(fmt.Sprintf("%d", h), func(b *testing.B) {
|
||||
b.Run(strconv.Itoa(h), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
|
||||
@ -100,7 +100,7 @@ func (c *volumeContext) Labels() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var joinLabels []string
|
||||
joinLabels := make([]string, 0, len(c.v.Labels))
|
||||
for k, v := range c.v.Labels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
@ -147,7 +150,7 @@ func TestVolumeContextWriteJSON(t *testing.T) {
|
||||
{Driver: "foo", Name: "foobar_baz"},
|
||||
{Driver: "bar", Name: "foobar_bar"},
|
||||
}
|
||||
expectedJSONs := []map[string]interface{}{
|
||||
expectedJSONs := []map[string]any{
|
||||
{"Availability": "N/A", "Driver": "foo", "Group": "N/A", "Labels": "", "Links": "N/A", "Mountpoint": "", "Name": "foobar_baz", "Scope": "", "Size": "N/A", "Status": "N/A"},
|
||||
{"Availability": "N/A", "Driver": "bar", "Group": "N/A", "Labels": "", "Links": "N/A", "Mountpoint": "", "Name": "foobar_bar", "Scope": "", "Size": "N/A", "Status": "N/A"},
|
||||
}
|
||||
@ -158,7 +161,7 @@ func TestVolumeContextWriteJSON(t *testing.T) {
|
||||
}
|
||||
for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
||||
msg := fmt.Sprintf("Output: line %d: %s", i, line)
|
||||
var m map[string]interface{}
|
||||
var m map[string]any
|
||||
err := json.Unmarshal([]byte(line), &m)
|
||||
assert.NilError(t, err, msg)
|
||||
assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg)
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.19
|
||||
|
||||
package idresolver
|
||||
|
||||
import (
|
||||
@ -17,20 +20,21 @@ type IDResolver struct {
|
||||
}
|
||||
|
||||
// New creates a new IDResolver.
|
||||
func New(client client.APIClient, noResolve bool) *IDResolver {
|
||||
func New(apiClient client.APIClient, noResolve bool) *IDResolver {
|
||||
return &IDResolver{
|
||||
client: client,
|
||||
client: apiClient,
|
||||
noResolve: noResolve,
|
||||
cache: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string, error) {
|
||||
func (r *IDResolver) get(ctx context.Context, t any, id string) (string, error) {
|
||||
switch t.(type) {
|
||||
case swarm.Node:
|
||||
node, _, err := r.client.NodeInspectWithRaw(ctx, id)
|
||||
if err != nil {
|
||||
return id, nil
|
||||
// TODO(thaJeztah): should error-handling be more specific, or is it ok to ignore any error?
|
||||
return id, nil //nolint:nilerr // ignore nil-error being returned, as this is a best-effort.
|
||||
}
|
||||
if node.Spec.Annotations.Name != "" {
|
||||
return node.Spec.Annotations.Name, nil
|
||||
@ -42,7 +46,8 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string,
|
||||
case swarm.Service:
|
||||
service, _, err := r.client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return id, nil
|
||||
// TODO(thaJeztah): should error-handling be more specific, or is it ok to ignore any error?
|
||||
return id, nil //nolint:nilerr // ignore nil-error being returned, as this is a best-effort.
|
||||
}
|
||||
return service.Spec.Annotations.Name, nil
|
||||
default:
|
||||
@ -53,7 +58,7 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string,
|
||||
// Resolve will attempt to resolve an ID to a Name by querying the manager.
|
||||
// Results are stored into a cache.
|
||||
// If the `-n` flag is used in the command-line, resolution is disabled.
|
||||
func (r *IDResolver) Resolve(ctx context.Context, t interface{}, id string) (string, error) {
|
||||
func (r *IDResolver) Resolve(ctx context.Context, t any, id string) (string, error) {
|
||||
if r.noResolve {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.context = args[0]
|
||||
return runBuild(dockerCli, options)
|
||||
return runBuild(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"category-top": "4",
|
||||
@ -172,7 +172,7 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) error {
|
||||
var (
|
||||
err error
|
||||
buildCtx io.ReadCloser
|
||||
@ -272,7 +272,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var resolvedTags []*resolvedTag
|
||||
|
||||
@ -234,7 +234,8 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read
|
||||
// getWithStatusError does an http.Get() and returns an error if the
|
||||
// status code is 4xx or 5xx.
|
||||
func getWithStatusError(url string) (resp *http.Response, err error) {
|
||||
if resp, err = http.Get(url); err != nil { //nolint:gosec // Ignore G107: Potential HTTP request made with variable url
|
||||
//#nosec G107 -- Ignore G107: Potential HTTP request made with variable url
|
||||
if resp, err = http.Get(url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode < http.StatusBadRequest {
|
||||
|
||||
@ -23,16 +23,19 @@ func prepareEmpty(_ *testing.T) string {
|
||||
}
|
||||
|
||||
func prepareNoFiles(t *testing.T) string {
|
||||
t.Helper()
|
||||
return createTestTempDir(t)
|
||||
}
|
||||
|
||||
func prepareOneFile(t *testing.T) string {
|
||||
t.Helper()
|
||||
contextDir := createTestTempDir(t)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
return contextDir
|
||||
}
|
||||
|
||||
func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) string, excludes []string) {
|
||||
t.Helper()
|
||||
contextDir := prepare(t)
|
||||
err := ValidateContextDirectory(contextDir, excludes)
|
||||
assert.NilError(t, err)
|
||||
@ -250,6 +253,7 @@ func createTestTempFile(t *testing.T, dir, filename, contents string) string {
|
||||
// This function is meant to be executed as a deferred call.
|
||||
// When an error occurs, it terminates the test.
|
||||
func chdir(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
workingDirectory, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
assert.NilError(t, os.Chdir(dir))
|
||||
|
||||
@ -25,8 +25,8 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||
t.Setenv("DOCKER_BUILDKIT", "0")
|
||||
buffer := new(bytes.Buffer)
|
||||
fakeBuild := newFakeBuild()
|
||||
fakeImageBuild := func(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||
tee := io.TeeReader(context, buffer)
|
||||
fakeImageBuild := func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||
tee := io.TeeReader(buildContext, buffer)
|
||||
gzipReader, err := gzip.NewReader(tee)
|
||||
assert.NilError(t, err)
|
||||
return fakeBuild.build(ctx, gzipReader, options)
|
||||
@ -48,7 +48,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||
options.dockerfileName = "-"
|
||||
options.context = dir.Path()
|
||||
options.untrusted = true
|
||||
assert.NilError(t, runBuild(cli, options))
|
||||
assert.NilError(t, runBuild(context.TODO(), cli, options))
|
||||
|
||||
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "foo"}
|
||||
assert.DeepEqual(t, expected, fakeBuild.filenames(t))
|
||||
@ -75,7 +75,7 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) {
|
||||
options := newBuildOptions()
|
||||
options.context = dir.Path()
|
||||
options.untrusted = true
|
||||
assert.NilError(t, runBuild(cli, options))
|
||||
assert.NilError(t, runBuild(context.TODO(), cli, options))
|
||||
|
||||
headers := fakeBuild.headers(t)
|
||||
expected := []*tar.Header{
|
||||
@ -110,7 +110,7 @@ COPY data /data
|
||||
options.context = dir.Path()
|
||||
options.dockerfileName = df.Path()
|
||||
options.untrusted = true
|
||||
assert.NilError(t, runBuild(cli, options))
|
||||
assert.NilError(t, runBuild(context.TODO(), cli, options))
|
||||
|
||||
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "data"}
|
||||
assert.DeepEqual(t, expected, fakeBuild.filenames(t))
|
||||
@ -170,7 +170,7 @@ RUN echo hello world
|
||||
options := newBuildOptions()
|
||||
options.context = tmpDir.Join("context-link")
|
||||
options.untrusted = true
|
||||
assert.NilError(t, runBuild(cli, options))
|
||||
assert.NilError(t, runBuild(context.TODO(), cli, options))
|
||||
|
||||
assert.DeepEqual(t, fakeBuild.filenames(t), []string{"Dockerfile"})
|
||||
}
|
||||
@ -184,8 +184,8 @@ func newFakeBuild() *fakeBuild {
|
||||
return &fakeBuild{}
|
||||
}
|
||||
|
||||
func (f *fakeBuild) build(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||
f.context = tar.NewReader(context)
|
||||
func (f *fakeBuild) build(_ context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||
f.context = tar.NewReader(buildContext)
|
||||
f.options = options
|
||||
body := new(bytes.Buffer)
|
||||
return types.ImageBuildResponse{Body: io.NopCloser(body)}, nil
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user