mirror of
https://github.com/moby/moby.git
synced 2026-01-14 17:36:01 +00:00
Compare commits
155 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1328a0a61c | ||
|
|
e7cb7cfc60 | ||
|
|
9e8243de60 | ||
|
|
9e6bd0772d | ||
|
|
0fb65cdc92 | ||
|
|
e01bbe8173 | ||
|
|
40b0fcd12f | ||
|
|
eda5359d4c | ||
|
|
a35f7ee1c2 | ||
|
|
af82d154e8 | ||
|
|
0d4fb6cf12 | ||
|
|
9c43d5c05a | ||
|
|
b5b32e9e71 | ||
|
|
6279f51613 | ||
|
|
9b16b8eb86 | ||
|
|
465e60b8a8 | ||
|
|
598d40a75d | ||
|
|
c0df2ee1fa | ||
|
|
9182ce9276 | ||
|
|
55b913bc54 | ||
|
|
86c2194cdf | ||
|
|
936337c8e2 | ||
|
|
63fe1ae04a | ||
|
|
815555d4eb | ||
|
|
322189a2c8 | ||
|
|
6c105c5413 | ||
|
|
540b931d36 | ||
|
|
7567b1c11b | ||
|
|
3c0f804cb2 | ||
|
|
03ca59364f | ||
|
|
793e141745 | ||
|
|
279b80d5cb | ||
|
|
3bb688291c | ||
|
|
fa0400740a | ||
|
|
311d95d180 | ||
|
|
009d994c11 | ||
|
|
f312b5ad19 | ||
|
|
d08a1aeac6 | ||
|
|
c60b124abb | ||
|
|
828f2121d4 | ||
|
|
ef6170a59f | ||
|
|
554b1e5585 | ||
|
|
f9ada51dae | ||
|
|
37632e9e19 | ||
|
|
b53e352970 | ||
|
|
9cdf20b80f | ||
|
|
3db5ae1e38 | ||
|
|
f26b1272b9 | ||
|
|
5b4239b6db | ||
|
|
ac20cc9d94 | ||
|
|
037f5ec1fd | ||
|
|
47f6acd997 | ||
|
|
6a7fd30a85 | ||
|
|
ea852019b6 | ||
|
|
b41516c070 | ||
|
|
687efd562a | ||
|
|
fe212b5963 | ||
|
|
1a92ba6573 | ||
|
|
5d3fbceff0 | ||
|
|
cb0e56ff03 | ||
|
|
2bc990f6aa | ||
|
|
536302f223 | ||
|
|
20989cf1c1 | ||
|
|
4fea1a51c0 | ||
|
|
842783ebd2 | ||
|
|
eea623a1e1 | ||
|
|
ac6750e3e7 | ||
|
|
da41730314 | ||
|
|
72d0c877bf | ||
|
|
32d039ea96 | ||
|
|
ae2b3666c5 | ||
|
|
7e895c4888 | ||
|
|
8798e4c0b6 | ||
|
|
b449180f06 | ||
|
|
eb75e3ff8e | ||
|
|
907eb136e2 | ||
|
|
6ce2dd6866 | ||
|
|
0ad1672781 | ||
|
|
fca8ba8b0a | ||
|
|
67640713cc | ||
|
|
25ee4b891c | ||
|
|
fba410bb66 | ||
|
|
f4e7362ba2 | ||
|
|
0ee31c2bca | ||
|
|
116e9be754 | ||
|
|
b0492570e7 | ||
|
|
07635aa60e | ||
|
|
d839fdc508 | ||
|
|
18e21f82a9 | ||
|
|
ab34280ac3 | ||
|
|
8f67ed81aa | ||
|
|
bed0abf9ca | ||
|
|
f4657eae7d | ||
|
|
a379e026c9 | ||
|
|
673020119f | ||
|
|
1725cc5f92 | ||
|
|
d1aa20efb7 | ||
|
|
e2ccfc19cc | ||
|
|
e35f6fbd08 | ||
|
|
64b5835e95 | ||
|
|
f2954d7622 | ||
|
|
548f37a132 | ||
|
|
5cc674870c | ||
|
|
d10756f356 | ||
|
|
20b867b732 | ||
|
|
fe0619e49f | ||
|
|
68e7b988b1 | ||
|
|
26d766450c | ||
|
|
c98179d3c7 | ||
|
|
573ebdba6e | ||
|
|
b9f01eea45 | ||
|
|
a51e65b334 | ||
|
|
347be7f928 | ||
|
|
58a26e4fda | ||
|
|
d5c9f26e3d | ||
|
|
2dc6b50863 | ||
|
|
79de67d989 | ||
|
|
43e3116aa7 | ||
|
|
b523cec06b | ||
|
|
045dfe9e09 | ||
|
|
1c867c02ef | ||
|
|
e6ff860b96 | ||
|
|
76894512ab | ||
|
|
e23be1cc51 | ||
|
|
46cfa2e34c | ||
|
|
5fbbb68dad | ||
|
|
448e7bed7d | ||
|
|
4bb779015a | ||
|
|
42d92e8903 | ||
|
|
d5de9f7779 | ||
|
|
bb03b5b86e | ||
|
|
d6abda0710 | ||
|
|
75c70b08b5 | ||
|
|
8617cd0570 | ||
|
|
e9e21b6bf1 | ||
|
|
d363536dca | ||
|
|
43c2952a31 | ||
|
|
c0ee655462 | ||
|
|
751d19fd3c | ||
|
|
583e29949e | ||
|
|
1dcd83acb4 | ||
|
|
97280b92b5 | ||
|
|
190138d0ec | ||
|
|
440d3b00fe | ||
|
|
6aeb5db52a | ||
|
|
5ccc9357db | ||
|
|
b19284a8de | ||
|
|
14e65c6ff6 | ||
|
|
8c6145e6ba | ||
|
|
d67439d7b6 | ||
|
|
69f337c285 | ||
|
|
03bc5f4f1c | ||
|
|
1fcbc7cf93 | ||
|
|
cad80da86f | ||
|
|
d3acc23a0c |
18
.github/workflows/.dco.yml
vendored
18
.github/workflows/.dco.yml
vendored
@@ -3,6 +3,15 @@ name: .dco
|
||||
|
||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
@@ -11,23 +20,24 @@ env:
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Dump context
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log(JSON.stringify(context, null, 2));
|
||||
-
|
||||
name: Get base ref
|
||||
id: base-ref
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
|
||||
70
.github/workflows/.windows.yml
vendored
70
.github/workflows/.windows.yml
vendored
@@ -3,6 +3,15 @@ name: .windows
|
||||
|
||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
@@ -15,7 +24,7 @@ on:
|
||||
default: false
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.20.10"
|
||||
GO_VERSION: "1.23.9"
|
||||
GOTESTLIST_VERSION: v0.3.1
|
||||
TESTSTAT_VERSION: v0.1.3
|
||||
WINDOWS_BASE_IMAGE: mcr.microsoft.com/windows/servercore
|
||||
@@ -29,6 +38,7 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}\go
|
||||
GOBIN: ${{ github.workspace }}\go\bin
|
||||
@@ -39,7 +49,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
@@ -58,7 +68,7 @@ jobs:
|
||||
}
|
||||
-
|
||||
name: Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
@@ -96,7 +106,7 @@ jobs:
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\containerd\bin\containerd-shim-runhcs-v1.exe" ${{ env.BIN_OUT }}\
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{ inputs.os }}
|
||||
path: ${{ env.BIN_OUT }}/*
|
||||
@@ -105,7 +115,7 @@ jobs:
|
||||
|
||||
unit-test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 120
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}\go
|
||||
GOBIN: ${{ github.workspace }}\go\bin
|
||||
@@ -115,7 +125,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
@@ -135,7 +145,7 @@ jobs:
|
||||
}
|
||||
-
|
||||
name: Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
@@ -166,7 +176,7 @@ jobs:
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: inputs.send_coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
||||
directory: bundles
|
||||
@@ -175,25 +185,27 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.os }}-unit-reports
|
||||
path: ${{ env.GOPATH }}\src\github.com\docker\docker\bundles\*
|
||||
retention-days: 1
|
||||
|
||||
unit-test-report:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
if: always()
|
||||
needs:
|
||||
- unit-test
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.os }}-unit-reports
|
||||
path: /tmp/artifacts
|
||||
@@ -208,15 +220,16 @@ jobs:
|
||||
|
||||
integration-test-prepare:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
outputs:
|
||||
matrix: ${{ steps.tests.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
@@ -241,7 +254,7 @@ jobs:
|
||||
|
||||
integration-test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 120
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- build
|
||||
- integration-test-prepare
|
||||
@@ -262,7 +275,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
@@ -271,7 +284,7 @@ jobs:
|
||||
Get-ChildItem Env: | Out-String
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-${{ inputs.os }}
|
||||
path: ${{ env.BIN_OUT }}
|
||||
@@ -285,6 +298,8 @@ jobs:
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
Write-Output "${{ env.BIN_OUT }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
$testName = ([System.BitConverter]::ToString((New-Object System.Security.Cryptography.SHA256Managed).ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${{ matrix.test }}"))) -replace '-').ToLower()
|
||||
echo "TESTREPORTS_NAME=$testName" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
-
|
||||
# removes docker service that is currently installed on the runner. we
|
||||
# could use Uninstall-Package but not yet available on Windows runners.
|
||||
@@ -390,7 +405,7 @@ jobs:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
@@ -415,7 +430,7 @@ jobs:
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: inputs.send_coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
||||
directory: bundles
|
||||
@@ -461,13 +476,15 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.os }}-integration-reports-${{ matrix.runtime }}
|
||||
name: ${{ inputs.os }}-${{ inputs.storage }}-integration-reports-${{ matrix.runtime }}-${{ env.TESTREPORTS_NAME }}
|
||||
path: ${{ env.GOPATH }}\src\github.com\docker\docker\bundles\*
|
||||
retention-days: 1
|
||||
|
||||
integration-test-report:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
if: always()
|
||||
needs:
|
||||
- integration-test
|
||||
@@ -480,15 +497,16 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.os }}-integration-reports-${{ matrix.runtime }}
|
||||
path: /tmp/artifacts
|
||||
path: /tmp/reports
|
||||
pattern: ${{ inputs.os }}-${{ inputs.storage }}-integration-reports-${{ matrix.runtime }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
@@ -496,4 +514,4 @@ jobs:
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/artifacts -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
39
.github/workflows/buildkit.yml
vendored
39
.github/workflows/buildkit.yml
vendored
@@ -1,5 +1,14 @@
|
||||
name: buildkit
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -20,24 +29,25 @@ jobs:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: binary
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary
|
||||
path: ${{ env.DESTDIR }}
|
||||
@@ -45,8 +55,8 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
@@ -63,9 +73,14 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: moby
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: BuildKit ref
|
||||
run: |
|
||||
@@ -73,20 +88,20 @@ jobs:
|
||||
working-directory: moby
|
||||
-
|
||||
name: Checkout BuildKit ${{ env.BUILDKIT_REF }}
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "moby/buildkit"
|
||||
ref: ${{ env.BUILDKIT_REF }}
|
||||
path: buildkit
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Download binary artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binary
|
||||
path: ./buildkit/build/moby/
|
||||
|
||||
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@@ -1,5 +1,14 @@
|
||||
name: ci
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -22,7 +31,8 @@ jobs:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
strategy:
|
||||
@@ -34,15 +44,15 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
-
|
||||
@@ -53,17 +63,10 @@ jobs:
|
||||
name: Check artifacts
|
||||
run: |
|
||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: ${{ env.DESTDIR }}
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
prepare-cross:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
@@ -71,7 +74,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
@@ -84,7 +87,8 @@ jobs:
|
||||
echo ${{ steps.platforms.outputs.matrix }}
|
||||
|
||||
cross:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
- prepare-cross
|
||||
@@ -95,7 +99,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
@@ -105,10 +109,10 @@ jobs:
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: all
|
||||
set: |
|
||||
@@ -121,11 +125,3 @@ jobs:
|
||||
name: Check artifacts
|
||||
run: |
|
||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cross-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ env.DESTDIR }}
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
161
.github/workflows/test.yml
vendored
161
.github/workflows/test.yml
vendored
@@ -1,5 +1,14 @@
|
||||
name: test
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -15,7 +24,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.20.10"
|
||||
GO_VERSION: "1.23.9"
|
||||
GOTESTLIST_VERSION: v0.3.1
|
||||
TESTSTAT_VERSION: v0.1.3
|
||||
ITG_CLI_MATRIX_SIZE: 6
|
||||
@@ -27,7 +36,8 @@ jobs:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build-dev:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
strategy:
|
||||
@@ -45,13 +55,13 @@ jobs:
|
||||
fi
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -60,7 +70,8 @@ jobs:
|
||||
*.output=type=cacheonly
|
||||
|
||||
validate-prepare:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
@@ -68,7 +79,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: scripts
|
||||
@@ -81,8 +92,8 @@ jobs:
|
||||
echo ${{ steps.scripts.outputs.matrix }}
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-prepare
|
||||
- build-dev
|
||||
@@ -93,7 +104,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
@@ -101,10 +112,10 @@ jobs:
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -115,23 +126,23 @@ jobs:
|
||||
make -o build validate-${{ matrix.script }}
|
||||
|
||||
unit:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -151,7 +162,7 @@ jobs:
|
||||
tree -nh /tmp/reports
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles
|
||||
env_vars: RUNNER_OS
|
||||
@@ -159,13 +170,14 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unit-reports
|
||||
name: test-reports-unit-${{ inputs.storage }}
|
||||
path: /tmp/reports/*
|
||||
retention-days: 1
|
||||
|
||||
unit-report:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
@@ -173,14 +185,14 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: unit-reports
|
||||
name: test-reports-unit-${{ inputs.storage }}
|
||||
path: /tmp/reports
|
||||
-
|
||||
name: Install teststat
|
||||
@@ -192,23 +204,23 @@ jobs:
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
docker-py:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -216,7 +228,7 @@ jobs:
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make -o build test-docker-py
|
||||
make TEST_SKIP_INTEGRATION_CLI=1 -o build test-docker-py
|
||||
-
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
@@ -234,29 +246,29 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docker-py-reports
|
||||
name: test-reports-docker-py-${{ inputs.storage }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-flaky:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -277,17 +289,20 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-20.04
|
||||
- ubuntu-22.04
|
||||
- ubuntu-24.04
|
||||
mode:
|
||||
- ""
|
||||
- rootless
|
||||
- systemd
|
||||
#- rootless-systemd FIXME: https://github.com/moby/moby/issues/44084
|
||||
exclude:
|
||||
- os: ubuntu-24.04
|
||||
mode: rootless # FIXME: https://github.com/moby/moby/pull/49579#issuecomment-2698622223
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
@@ -305,10 +320,10 @@ jobs:
|
||||
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -324,10 +339,12 @@ jobs:
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
reportsPath="/tmp/reports/${{ matrix.os }}"
|
||||
reportsName=${{ matrix.os }}
|
||||
if [ -n "${{ matrix.mode }}" ]; then
|
||||
reportsPath="$reportsPath-${{ matrix.mode }}"
|
||||
reportsName="$reportsName-${{ matrix.mode }}"
|
||||
fi
|
||||
reportsPath="/tmp/reports/$reportsName"
|
||||
echo "TESTREPORTS_NAME=$reportsName" >> $GITHUB_ENV
|
||||
mkdir -p bundles $reportsPath
|
||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
||||
tar -xzf /tmp/reports.tar.gz -C $reportsPath
|
||||
@@ -335,7 +352,7 @@ jobs:
|
||||
tree -nh $reportsPath
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles/test-integration
|
||||
env_vars: RUNNER_OS
|
||||
@@ -348,13 +365,13 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: integration-reports
|
||||
name: test-reports-integration-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-report:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
@@ -362,15 +379,16 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: integration-reports
|
||||
path: /tmp/reports
|
||||
pattern: test-reports-integration-${{ inputs.storage }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
@@ -381,7 +399,7 @@ jobs:
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
integration-cli-prepare:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
@@ -389,10 +407,10 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
@@ -417,7 +435,7 @@ jobs:
|
||||
echo ${{ steps.tests.outputs.matrix }}
|
||||
|
||||
integration-cli:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
@@ -429,16 +447,16 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
@@ -455,7 +473,9 @@ jobs:
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
reportsPath=/tmp/reports/$(echo -n "${{ matrix.test }}" | sha256sum | cut -d " " -f 1)
|
||||
reportsName=$(echo -n "${{ matrix.test }}" | sha256sum | cut -d " " -f 1)
|
||||
reportsPath=/tmp/reports/$reportsName
|
||||
echo "TESTREPORTS_NAME=$reportsName" >> $GITHUB_ENV
|
||||
mkdir -p bundles $reportsPath
|
||||
echo "${{ matrix.test }}" | tr -s '|' '\n' | tee -a "$reportsPath/tests.txt"
|
||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
||||
@@ -464,7 +484,7 @@ jobs:
|
||||
tree -nh $reportsPath
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles/test-integration
|
||||
env_vars: RUNNER_OS
|
||||
@@ -477,13 +497,13 @@ jobs:
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: integration-cli-reports
|
||||
name: test-reports-integration-cli-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-cli-report:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
@@ -491,15 +511,16 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: integration-cli-reports
|
||||
path: /tmp/reports
|
||||
pattern: test-reports-integration-cli-${{ inputs.storage }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
@@ -510,7 +531,8 @@ jobs:
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
prepare-smoke:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
@@ -518,7 +540,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
@@ -531,7 +553,8 @@ jobs:
|
||||
echo ${{ steps.platforms.outputs.matrix }}
|
||||
|
||||
smoke:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- prepare-smoke
|
||||
strategy:
|
||||
@@ -541,7 +564,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
@@ -549,13 +572,13 @@ jobs:
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: binary-smoketest
|
||||
set: |
|
||||
|
||||
9
.github/workflows/windows-2019.yml
vendored
9
.github/workflows/windows-2019.yml
vendored
@@ -1,5 +1,14 @@
|
||||
name: windows-2019
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
9
.github/workflows/windows-2022.yml
vendored
9
.github/workflows/windows-2022.yml
vendored
@@ -1,5 +1,14 @@
|
||||
name: windows-2022
|
||||
|
||||
# Default to 'contents: read', which grants actions to read commits.
|
||||
#
|
||||
# If any permission is set, any permission not included in the list is
|
||||
# implicitly set to "none".
|
||||
#
|
||||
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
68
Dockerfile
68
Dockerfile
@@ -1,15 +1,14 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.20.10
|
||||
ARG BASE_DEBIAN_DISTRO="bullseye"
|
||||
ARG GO_VERSION=1.23.9
|
||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
||||
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG XX_VERSION=1.6.1
|
||||
|
||||
ARG VPNKIT_VERSION=0.5.0
|
||||
ARG DOCKERCLI_VERSION=v17.06.2-ce
|
||||
|
||||
ARG SYSTEMD="false"
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG DOCKER_STATIC=1
|
||||
|
||||
# cross compilation helper
|
||||
@@ -27,13 +26,12 @@ FROM --platform=$BUILDPLATFORM ${GOLANG_IMAGE} AS base
|
||||
COPY --from=xx / /
|
||||
RUN echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
||||
ARG APT_MIRROR
|
||||
RUN test -n "$APT_MIRROR" && sed -ri "s/(httpredir|deb|security).debian.org/${APT_MIRROR}/g" /etc/apt/sources.list || true
|
||||
ARG DEBIAN_FRONTEND
|
||||
RUN test -n "$APT_MIRROR" && sed -ri "s#(httpredir|deb|security).debian.org#${APT_MIRROR}#g" /etc/apt/sources.list.d/debian.sources || true
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y file
|
||||
ENV GO111MODULE=off
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
FROM base AS criu
|
||||
ARG DEBIAN_FRONTEND
|
||||
ADD --chmod=0644 https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_11/Release.key /etc/apt/trusted.gpg.d/criu.gpg.asc
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-criu-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-criu-aptcache,target=/var/cache/apt \
|
||||
@@ -108,7 +106,6 @@ EOT
|
||||
# See also frozenImages in "testutil/environment/protect.go" (which needs to
|
||||
# be updated when adding images to this list)
|
||||
FROM debian:${BASE_DEBIAN_DISTRO} AS frozen-images
|
||||
ARG DEBIAN_FRONTEND
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-frozen-images-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-frozen-images-aptcache,target=/var/cache/apt \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
@@ -122,7 +119,7 @@ ARG TARGETVARIANT
|
||||
RUN /download-frozen-image-v2.sh /build \
|
||||
busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
|
||||
busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \
|
||||
debian:bullseye-slim@sha256:dacf278785a4daa9de07596ec739dbc07131e189942772210709c5c0777e8437 \
|
||||
debian:bookworm-slim@sha256:2bc5c236e9b262645a323e9088dfa3bb1ecb16cc75811daf40a23a824d665be9 \
|
||||
hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \
|
||||
arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1
|
||||
|
||||
@@ -191,17 +188,19 @@ RUN git init . && git remote add origin "https://github.com/containerd/container
|
||||
# When updating the binary version you may also need to update the vendor
|
||||
# version to pick up bug fixes or new APIs, however, usually the Go packages
|
||||
# are built from a commit from the master branch.
|
||||
ARG CONTAINERD_VERSION=v1.6.22
|
||||
ARG CONTAINERD_VERSION=v1.6.38
|
||||
RUN git fetch -q --depth 1 origin "${CONTAINERD_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS containerd-build
|
||||
WORKDIR /go/src/github.com/containerd/containerd
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-containerd-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-containerd-aptcache,target=/var/cache/apt \
|
||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
||||
gcc libbtrfs-dev libsecret-1-dev
|
||||
gcc \
|
||||
libbtrfs-dev \
|
||||
libsecret-1-dev \
|
||||
pkg-config
|
||||
ARG DOCKER_STATIC
|
||||
RUN --mount=from=containerd-src,src=/usr/src/containerd,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=containerd-build-$TARGETPLATFORM <<EOT
|
||||
@@ -222,7 +221,7 @@ FROM binary-dummy AS containerd-windows
|
||||
FROM containerd-${TARGETOS} AS containerd
|
||||
|
||||
FROM base AS golangci_lint
|
||||
ARG GOLANGCI_LINT_VERSION=v1.54.2
|
||||
ARG GOLANGCI_LINT_VERSION=v1.64.5
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
GOBIN=/build/ GO111MODULE=on go install "github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}" \
|
||||
@@ -279,17 +278,20 @@ RUN git init . && git remote add origin "https://github.com/opencontainers/runc.
|
||||
# that is used. If you need to update runc, open a pull request in the containerd
|
||||
# project first, and update both after that is merged. When updating RUNC_VERSION,
|
||||
# consider updating runc in vendor.mod accordingly.
|
||||
ARG RUNC_VERSION=v1.1.9
|
||||
ARG RUNC_VERSION=v1.1.12
|
||||
RUN git fetch -q --depth 1 origin "${RUNC_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS runc-build
|
||||
WORKDIR /go/src/github.com/opencontainers/runc
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-runc-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-runc-aptcache,target=/var/cache/apt \
|
||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
||||
dpkg-dev gcc libc6-dev libseccomp-dev
|
||||
dpkg-dev \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
libseccomp-dev \
|
||||
pkg-config
|
||||
ARG DOCKER_STATIC
|
||||
RUN --mount=from=runc-src,src=/usr/src/runc,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=runc-build-$TARGETPLATFORM <<EOT
|
||||
@@ -316,7 +318,6 @@ RUN git fetch -q --depth 1 origin "${TINI_VERSION}" +refs/tags/*:refs/tags/* &&
|
||||
|
||||
FROM base AS tini-build
|
||||
WORKDIR /go/src/github.com/krallin/tini
|
||||
ARG DEBIAN_FRONTEND
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \
|
||||
apt-get update && apt-get install -y --no-install-recommends cmake
|
||||
@@ -324,7 +325,9 @@ ARG TARGETPLATFORM
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \
|
||||
xx-apt-get install -y --no-install-recommends \
|
||||
gcc libc6-dev
|
||||
gcc \
|
||||
libc6-dev \
|
||||
pkg-config
|
||||
RUN --mount=from=tini-src,src=/usr/src/tini,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=tini-build-$TARGETPLATFORM <<EOT
|
||||
set -e
|
||||
@@ -349,12 +352,13 @@ RUN git fetch -q --depth 1 origin "${ROOTLESSKIT_VERSION}" +refs/tags/*:refs/tag
|
||||
|
||||
FROM base AS rootlesskit-build
|
||||
WORKDIR /go/src/github.com/rootless-containers/rootlesskit
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-rootlesskit-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-rootlesskit-aptcache,target=/var/cache/apt \
|
||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
||||
gcc libc6-dev
|
||||
gcc \
|
||||
libc6-dev \
|
||||
pkg-config
|
||||
ENV GO111MODULE=on
|
||||
ARG DOCKER_STATIC
|
||||
RUN --mount=from=rootlesskit-src,src=/usr/src/rootlesskit,rw \
|
||||
@@ -422,7 +426,11 @@ RUN git fetch -q --depth 1 origin "${CONTAINERUTILITY_VERSION}" +refs/tags/*:ref
|
||||
FROM base AS containerutil-build
|
||||
WORKDIR /usr/src/containerutil
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apt-get install -y --no-install-recommends gcc g++ libc6-dev
|
||||
RUN xx-apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
g++ \
|
||||
libc6-dev \
|
||||
pkg-config
|
||||
RUN --mount=from=containerutil-src,src=/usr/src/containerutil,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=containerutil-build-$TARGETPLATFORM <<EOT
|
||||
set -e
|
||||
@@ -477,13 +485,9 @@ RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
||||
dbus-user-session \
|
||||
systemd \
|
||||
systemd-sysv
|
||||
RUN mkdir -p hack \
|
||||
&& curl -o hack/dind-systemd https://raw.githubusercontent.com/AkihiroSuda/containerized-systemd/b70bac0daeea120456764248164c21684ade7d0d/docker-entrypoint.sh \
|
||||
&& chmod +x hack/dind-systemd
|
||||
ENTRYPOINT ["hack/dind-systemd"]
|
||||
|
||||
FROM dev-systemd-${SYSTEMD} AS dev-base
|
||||
ARG DEBIAN_FRONTEND
|
||||
RUN groupadd -r docker
|
||||
RUN useradd --create-home --gid docker unprivilegeduser \
|
||||
&& mkdir -p /home/unprivilegeduser/.local/share/docker \
|
||||
@@ -517,9 +521,6 @@ RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
||||
net-tools \
|
||||
patch \
|
||||
pigz \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel \
|
||||
sudo \
|
||||
thin-provisioning-tools \
|
||||
uidmap \
|
||||
@@ -534,8 +535,6 @@ RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
||||
RUN update-alternatives --set iptables /usr/sbin/iptables-legacy || true \
|
||||
&& update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true \
|
||||
&& update-alternatives --set arptables /usr/sbin/arptables-legacy || true
|
||||
ARG YAMLLINT_VERSION=1.27.1
|
||||
RUN pip3 install yamllint==${YAMLLINT_VERSION}
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \
|
||||
apt-get update && apt-get install --no-install-recommends -y \
|
||||
@@ -547,14 +546,14 @@ RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
||||
libseccomp-dev \
|
||||
libsecret-1-dev \
|
||||
libsystemd-dev \
|
||||
libudev-dev
|
||||
libudev-dev \
|
||||
yamllint
|
||||
|
||||
FROM base AS build
|
||||
COPY --from=gowinres /build/ /usr/local/bin/
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV GO111MODULE=off
|
||||
ENV CGO_ENABLED=1
|
||||
ARG DEBIAN_FRONTEND
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-build-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-build-aptcache,target=/var/cache/apt \
|
||||
apt-get update && apt-get install --no-install-recommends -y \
|
||||
@@ -573,7 +572,8 @@ RUN --mount=type=cache,sharing=locked,id=moby-build-aptlib,target=/var/lib/apt \
|
||||
libseccomp-dev \
|
||||
libsecret-1-dev \
|
||||
libsystemd-dev \
|
||||
libudev-dev
|
||||
libudev-dev \
|
||||
pkg-config
|
||||
ARG DOCKER_BUILDTAGS
|
||||
ARG DOCKER_DEBUG
|
||||
ARG DOCKER_GITCOMMIT=HEAD
|
||||
@@ -630,7 +630,7 @@ COPY --from=build /build /
|
||||
# smoke tests
|
||||
# usage:
|
||||
# > docker buildx bake binary-smoketest
|
||||
FROM --platform=$TARGETPLATFORM base AS smoketest
|
||||
FROM base AS smoketest
|
||||
WORKDIR /usr/local/bin
|
||||
COPY --from=build /build .
|
||||
RUN <<EOT
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.20.5
|
||||
ARG GO_VERSION=1.23.9
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS base
|
||||
ENV GO111MODULE=off
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
|
||||
# This represents the bare minimum required to build and test Docker.
|
||||
|
||||
ARG GO_VERSION=1.20.10
|
||||
ARG GO_VERSION=1.23.9
|
||||
|
||||
ARG BASE_DEBIAN_DISTRO="bullseye"
|
||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
||||
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
|
||||
|
||||
FROM ${GOLANG_IMAGE}
|
||||
ENV GO111MODULE=off
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||
# allow replacing debian mirror
|
||||
ARG APT_MIRROR
|
||||
RUN test -n "$APT_MIRROR" && sed -ri "s#(httpredir|deb|security).debian.org#${APT_MIRROR}#g" /etc/apt/sources.list.d/debian.sources || true
|
||||
|
||||
# Compile and runtime deps
|
||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies
|
||||
|
||||
@@ -165,10 +165,10 @@ FROM microsoft/windowsservercore
|
||||
# Use PowerShell as the default shell
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
ARG GO_VERSION=1.20.10
|
||||
ARG GO_VERSION=1.23.9
|
||||
ARG GOTESTSUM_VERSION=v1.8.2
|
||||
ARG GOWINRES_VERSION=v0.3.0
|
||||
ARG CONTAINERD_VERSION=v1.6.22
|
||||
ARG CONTAINERD_VERSION=v1.6.38
|
||||
|
||||
# Environment variable notes:
|
||||
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
|
||||
@@ -179,6 +179,7 @@ ENV GO_VERSION=${GO_VERSION} `
|
||||
GIT_VERSION=2.11.1 `
|
||||
GOPATH=C:\gopath `
|
||||
GO111MODULE=off `
|
||||
GOTOOLCHAIN=local `
|
||||
FROM_DOCKERFILE=1 `
|
||||
GOTESTSUM_VERSION=${GOTESTSUM_VERSION} `
|
||||
GOWINRES_VERSION=${GOWINRES_VERSION}
|
||||
@@ -258,14 +259,11 @@ RUN `
|
||||
Remove-Item C:\gitsetup.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading containerd; `
|
||||
Install-Package -Force 7Zip4PowerShell; `
|
||||
$location='https://github.com/containerd/containerd/releases/download/'+$Env:CONTAINERD_VERSION+'/containerd-'+$Env:CONTAINERD_VERSION.TrimStart('v')+'-windows-amd64.tar.gz'; `
|
||||
Download-File $location C:\containerd.tar.gz; `
|
||||
New-Item -Path C:\containerd -ItemType Directory; `
|
||||
Expand-7Zip C:\containerd.tar.gz C:\; `
|
||||
Expand-7Zip C:\containerd.tar C:\containerd; `
|
||||
tar -xzf C:\containerd.tar.gz -C C:\containerd; `
|
||||
Remove-Item C:\containerd.tar.gz; `
|
||||
Remove-Item C:\containerd.tar; `
|
||||
`
|
||||
# Ensure all directories exist that we will require below....
|
||||
$srcDir = """$Env:GOPATH`\src\github.com\docker\docker\bundles"""; `
|
||||
|
||||
12
Jenkinsfile
vendored
12
Jenkinsfile
vendored
@@ -60,6 +60,15 @@ pipeline {
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("Load kernel modules") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
sudo modprobe -va br_netfilter
|
||||
sudo systemctl restart docker.service
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage("Print info") {
|
||||
steps {
|
||||
sh 'docker version'
|
||||
@@ -78,9 +87,6 @@ pipeline {
|
||||
}
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
'''
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
|
||||
@@ -49,7 +49,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
if p := r.FormValue("platform"); p != "" {
|
||||
sp, err := platforms.Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
platform = &sp
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ func (s *snapshotter) GetDiffIDs(ctx context.Context, key string) ([]layer.DiffI
|
||||
}
|
||||
|
||||
func (s *snapshotter) EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) {
|
||||
s.layerCreateLocker.Lock(key)
|
||||
defer s.layerCreateLocker.Unlock(key)
|
||||
|
||||
diffIDs, err := s.GetDiffIDs(ctx, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/locker"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
@@ -48,10 +49,11 @@ type checksumCalculator interface {
|
||||
type snapshotter struct {
|
||||
opt Opt
|
||||
|
||||
refs map[string]layer.Layer
|
||||
db *bolt.DB
|
||||
mu sync.Mutex
|
||||
reg graphIDRegistrar
|
||||
refs map[string]layer.Layer
|
||||
db *bolt.DB
|
||||
mu sync.Mutex
|
||||
reg graphIDRegistrar
|
||||
layerCreateLocker *locker.Locker
|
||||
}
|
||||
|
||||
// NewSnapshotter creates a new snapshotter
|
||||
@@ -68,10 +70,11 @@ func NewSnapshotter(opt Opt, prevLM leases.Manager) (snapshot.Snapshotter, lease
|
||||
}
|
||||
|
||||
s := &snapshotter{
|
||||
opt: opt,
|
||||
db: db,
|
||||
refs: map[string]layer.Layer{},
|
||||
reg: reg,
|
||||
opt: opt,
|
||||
db: db,
|
||||
refs: map[string]layer.Layer{},
|
||||
reg: reg,
|
||||
layerCreateLocker: locker.New(),
|
||||
}
|
||||
|
||||
lm := newLeaseManager(s, prevLM)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
containerimageexp "github.com/docker/docker/builder/builder-next/exporter"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/daemon/images"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
@@ -304,7 +305,7 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
|
||||
// TODO: remove once opt.Options.Platform is of type specs.Platform
|
||||
_, err := platforms.Parse(opt.Options.Platform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
frontendAttrs["platform"] = opt.Options.Platform
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@ func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History,
|
||||
return nil, errors.Wrap(err, "failed to parse image config for patch")
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return nil, errors.New("null image config")
|
||||
}
|
||||
|
||||
var rootFS ocispec.RootFS
|
||||
rootFS.Type = "layers"
|
||||
rootFS.DiffIDs = append(rootFS.DiffIDs, dps...)
|
||||
|
||||
42
builder/builder-next/exporter/writer_test.go
Normal file
42
builder/builder-next/exporter/writer_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package containerimage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestPatchImageConfig(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
cfgJSON string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
cfgJSON: "{}",
|
||||
},
|
||||
{
|
||||
name: "history only",
|
||||
cfgJSON: `{"history": []}`,
|
||||
},
|
||||
{
|
||||
name: "rootfs only",
|
||||
cfgJSON: `{"rootfs": {}}`,
|
||||
},
|
||||
{
|
||||
name: "null",
|
||||
cfgJSON: "null",
|
||||
err: "null image config",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := patchImageConfig([]byte(tc.cfgJSON), nil, nil, nil, nil)
|
||||
if tc.err == "" {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package worker
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/moby/buildkit/client"
|
||||
)
|
||||
@@ -30,12 +31,12 @@ func DefaultGCPolicy(p string, defaultKeepBytes int64) []client.PruneInfo {
|
||||
// if build cache uses more than 512MB delete the most easily reproducible data after it has not been used for 2 days
|
||||
{
|
||||
Filter: []string{"type==source.local,type==exec.cachemount,type==source.git.checkout"},
|
||||
KeepDuration: 48 * 3600, // 48h
|
||||
KeepDuration: 48 * time.Hour,
|
||||
KeepBytes: tempCacheKeepBytes,
|
||||
},
|
||||
// remove any data not used for 60 days
|
||||
{
|
||||
KeepDuration: 60 * 24 * 3600, // 60d
|
||||
KeepDuration: 60 * 24 * time.Hour,
|
||||
KeepBytes: keep,
|
||||
},
|
||||
// keep the unshared build cache under cap
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -89,7 +90,7 @@ type ImageCacheBuilder interface {
|
||||
type ImageCache interface {
|
||||
// GetCache returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
GetCache(parentID string, cfg *container.Config) (imageID string, err error)
|
||||
GetCache(parentID string, cfg *container.Config, platform ocispec.Platform) (imageID string, err error)
|
||||
}
|
||||
|
||||
// Image represents a Docker image used by the builder.
|
||||
|
||||
@@ -156,7 +156,7 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er
|
||||
if config.Platform != "" {
|
||||
sp, err := platforms.Parse(config.Platform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
b.platform = &sp
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -25,7 +24,7 @@ import (
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -75,7 +74,7 @@ type copier struct {
|
||||
source builder.Source
|
||||
pathCache pathCache
|
||||
download sourceDownloader
|
||||
platform *specs.Platform
|
||||
platform ocispec.Platform
|
||||
// for cleanup. TODO: having copier.cleanup() is error prone and hard to
|
||||
// follow. Code calling performCopy should manage the lifecycle of its params.
|
||||
// Copier should take override source as input, not imageMount.
|
||||
@@ -84,19 +83,7 @@ type copier struct {
|
||||
}
|
||||
|
||||
func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, imageSource *imageMount) copier {
|
||||
platform := req.builder.platform
|
||||
if platform == nil {
|
||||
// May be nil if not explicitly set in API/dockerfile
|
||||
platform = &specs.Platform{}
|
||||
}
|
||||
if platform.OS == "" {
|
||||
// Default to the dispatch requests operating system if not explicit in API/dockerfile
|
||||
platform.OS = req.state.operatingSystem
|
||||
}
|
||||
if platform.OS == "" {
|
||||
// This is a failsafe just in case. Shouldn't be hit.
|
||||
platform.OS = runtime.GOOS
|
||||
}
|
||||
platform := req.builder.getPlatform(req.state)
|
||||
|
||||
return copier{
|
||||
source: req.source,
|
||||
|
||||
@@ -164,17 +164,17 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
||||
|
||||
p, err := platforms.Parse(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse platform %s", v)
|
||||
return errors.Wrapf(errdefs.InvalidParameter(err), "failed to parse platform %s", v)
|
||||
}
|
||||
platform = &p
|
||||
}
|
||||
|
||||
image, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
|
||||
img, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := d.state
|
||||
if err := state.beginStage(cmd.Name, image); err != nil {
|
||||
if err := state.beginStage(cmd.Name, img); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(state.runConfig.OnBuild) > 0 {
|
||||
@@ -345,9 +345,16 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
saveCmd = prependEnvOnCmd(d.state.buildArgs, buildArgs, cmdFromArgs)
|
||||
}
|
||||
|
||||
cacheArgsEscaped := argsEscaped
|
||||
// ArgsEscaped is not persisted in the committed image on Windows.
|
||||
// Use the original from previous build steps for cache probing.
|
||||
if d.state.operatingSystem == "windows" {
|
||||
cacheArgsEscaped = stateRunConfig.ArgsEscaped
|
||||
}
|
||||
|
||||
runConfigForCacheProbe := copyRunConfig(stateRunConfig,
|
||||
withCmd(saveCmd),
|
||||
withArgsEscaped(argsEscaped),
|
||||
withArgsEscaped(cacheArgsEscaped),
|
||||
withEntrypointOverride(saveCmd, nil))
|
||||
if hit, err := d.builder.probeCache(d.state, runConfigForCacheProbe); err != nil || hit {
|
||||
return err
|
||||
|
||||
@@ -3,6 +3,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
// cache.
|
||||
type ImageProber interface {
|
||||
Reset()
|
||||
Probe(parentID string, runConfig *container.Config) (string, error)
|
||||
Probe(parentID string, runConfig *container.Config, platform ocispec.Platform) (string, error)
|
||||
}
|
||||
|
||||
type imageProber struct {
|
||||
@@ -37,11 +38,11 @@ func (c *imageProber) Reset() {
|
||||
|
||||
// Probe checks if cache match can be found for current build instruction.
|
||||
// It returns the cachedID if there is a hit, and the empty string on miss
|
||||
func (c *imageProber) Probe(parentID string, runConfig *container.Config) (string, error) {
|
||||
func (c *imageProber) Probe(parentID string, runConfig *container.Config, platform ocispec.Platform) (string, error) {
|
||||
if c.cacheBusted {
|
||||
return "", nil
|
||||
}
|
||||
cacheID, err := c.cache.GetCache(parentID, runConfig)
|
||||
cacheID, err := c.cache.GetCache(parentID, runConfig, platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -58,6 +59,6 @@ type nopProber struct{}
|
||||
|
||||
func (c *nopProber) Reset() {}
|
||||
|
||||
func (c *nopProber) Probe(_ string, _ *container.Config) (string, error) {
|
||||
func (c *nopProber) Probe(_ string, _ *container.Config, _ ocispec.Platform) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -364,7 +365,7 @@ func getShell(c *container.Config, os string) []string {
|
||||
}
|
||||
|
||||
func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.Config) (bool, error) {
|
||||
cachedID, err := b.imageProber.Probe(dispatchState.imageID, runConfig)
|
||||
cachedID, err := b.imageProber.Probe(dispatchState.imageID, runConfig, b.getPlatform(dispatchState))
|
||||
if cachedID == "" || err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -424,3 +425,17 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
|
||||
}
|
||||
return hc
|
||||
}
|
||||
|
||||
func (b *Builder) getPlatform(state *dispatchState) specs.Platform {
|
||||
// May be nil if not explicitly set in API/dockerfile
|
||||
out := platforms.DefaultSpec()
|
||||
if b.platform != nil {
|
||||
out = *b.platform
|
||||
}
|
||||
|
||||
if state.operatingSystem != "" {
|
||||
out.OS = state.operatingSystem
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -63,7 +64,7 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState)
|
||||
|
||||
optionsPlatform, err := platforms.Parse(builder.options.Platform)
|
||||
if err != nil {
|
||||
return idtools.Identity{}, err
|
||||
return idtools.Identity{}, errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
runConfig := copyRunConfig(state.runConfig,
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// MockBackend implements the builder.Backend interface for unit testing
|
||||
@@ -111,7 +112,7 @@ type mockImageCache struct {
|
||||
getCacheFunc func(parentID string, cfg *container.Config) (string, error)
|
||||
}
|
||||
|
||||
func (mic *mockImageCache) GetCache(parentID string, cfg *container.Config) (string, error) {
|
||||
func (mic *mockImageCache) GetCache(parentID string, cfg *container.Config, _ ocispec.Platform) (string, error) {
|
||||
if mic.getCacheFunc != nil {
|
||||
return mic.getCacheFunc(parentID, cfg)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ func downloadRemote(remoteURL string) (string, io.ReadCloser, error) {
|
||||
// GetWithStatusError does an http.Get() and returns an error if the
|
||||
// status code is 4xx or 5xx.
|
||||
func GetWithStatusError(address string) (resp *http.Response, err error) {
|
||||
// #nosec G107
|
||||
if resp, err = http.Get(address); err != nil {
|
||||
resp, err = http.Get(address) // #nosec G107 -- ignore G107: Potential HTTP request made with variable url
|
||||
if err != nil {
|
||||
if uerr, ok := err.(*url.Error); ok {
|
||||
if derr, ok := uerr.Err.(*net.DNSError); ok && !derr.IsTimeout {
|
||||
return nil, errdefs.NotFound(err)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Generate `event_messages.bin`
|
||||
|
||||
```console
|
||||
$ docker run --rm -it -v "$(pwd):/winresources" debian:bullseye bash
|
||||
$ docker run --rm -it -v "$(pwd):/winresources" debian:bookworm-slim bash
|
||||
root@9ad2260f6ebc:/# apt-get update -y && apt-get install -y binutils-mingw-w64-x86-64
|
||||
root@9ad2260f6ebc:/# x86_64-w64-mingw32-windmc -v /winresources/event_messages.mc
|
||||
root@9ad2260f6ebc:/# mv MSG00001.bin /winresources/event_messages.bin
|
||||
|
||||
@@ -66,8 +66,12 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
|
||||
//
|
||||
// If there's a JSON parsing error, read the real error message
|
||||
// off the body and send it to the client.
|
||||
_, _ = io.ReadAll(io.LimitReader(stream, containerWaitErrorMsgLimit))
|
||||
errC <- errors.New(responseText.String())
|
||||
if errors.As(err, new(*json.SyntaxError)) {
|
||||
_, _ = io.ReadAll(io.LimitReader(stream, containerWaitErrorMsgLimit))
|
||||
errC <- errors.New(responseText.String())
|
||||
} else {
|
||||
errC <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"testing/iotest"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestContainerWaitError(t *testing.T) {
|
||||
@@ -117,6 +120,46 @@ func TestContainerWaitProxyInterruptLong(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerWaitErrorHandling(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
rdr io.Reader
|
||||
exp error
|
||||
}{
|
||||
{name: "invalid json", rdr: strings.NewReader(`{]`), exp: errors.New("{]")},
|
||||
{name: "context canceled", rdr: iotest.ErrReader(context.Canceled), exp: context.Canceled},
|
||||
{name: "context deadline exceeded", rdr: iotest.ErrReader(context.DeadlineExceeded), exp: context.DeadlineExceeded},
|
||||
{name: "connection reset", rdr: iotest.ErrReader(syscall.ECONNRESET), exp: syscall.ECONNRESET},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
client := &Client{
|
||||
version: "1.30",
|
||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(test.rdr),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
resultC, errC := client.ContainerWait(ctx, "container_id", "")
|
||||
select {
|
||||
case err := <-errC:
|
||||
if err.Error() != test.exp.Error() {
|
||||
t.Fatalf("ContainerWait() errC = %v; want %v", err, test.exp)
|
||||
}
|
||||
return
|
||||
case result := <-resultC:
|
||||
t.Fatalf("expected to not get a wait result, got %d", result.StatusCode)
|
||||
return
|
||||
}
|
||||
// Unexpected - we should not reach this line
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleClient_ContainerWait_withTimeout() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -2,6 +2,7 @@ package stream // import "github.com/docker/docker/container/stream"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -91,24 +92,24 @@ func (c *Config) NewNopInputPipe() {
|
||||
|
||||
// CloseStreams ensures that the configured streams are properly closed.
|
||||
func (c *Config) CloseStreams() error {
|
||||
var errors []string
|
||||
var errs []string
|
||||
|
||||
if c.stdin != nil {
|
||||
if err := c.stdin.Close(); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("error close stdin: %s", err))
|
||||
errs = append(errs, fmt.Sprintf("error close stdin: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.stdout.Clean(); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("error close stdout: %s", err))
|
||||
errs = append(errs, fmt.Sprintf("error close stdout: %s", err))
|
||||
}
|
||||
|
||||
if err := c.stderr.Clean(); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("error close stderr: %s", err))
|
||||
errs = append(errs, fmt.Sprintf("error close stderr: %s", err))
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf(strings.Join(errors, "\n"))
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -59,30 +59,11 @@ fetch_blob() {
|
||||
shift
|
||||
local curlArgs=("$@")
|
||||
|
||||
local curlHeaders
|
||||
curlHeaders="$(
|
||||
curl -S "${curlArgs[@]}" \
|
||||
-H "Authorization: Bearer $token" \
|
||||
"$registryBase/v2/$image/blobs/$digest" \
|
||||
-o "$targetFile" \
|
||||
-D-
|
||||
)"
|
||||
curlHeaders="$(echo "$curlHeaders" | tr -d '\r')"
|
||||
if grep -qE "^HTTP/[0-9].[0-9] 3" <<< "$curlHeaders"; then
|
||||
rm -f "$targetFile"
|
||||
|
||||
local blobRedirect
|
||||
blobRedirect="$(echo "$curlHeaders" | awk -F ': ' 'tolower($1) == "location" { print $2; exit }')"
|
||||
if [ -z "$blobRedirect" ]; then
|
||||
echo >&2 "error: failed fetching '$image' blob '$digest'"
|
||||
echo "$curlHeaders" | head -1 >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
curl -fSL "${curlArgs[@]}" \
|
||||
"$blobRedirect" \
|
||||
-o "$targetFile"
|
||||
fi
|
||||
curl -L -S "${curlArgs[@]}" \
|
||||
-H "Authorization: Bearer $token" \
|
||||
"$registryBase/v2/$image/blobs/$digest" \
|
||||
-o "$targetFile" \
|
||||
-D-
|
||||
}
|
||||
|
||||
# handle 'application/vnd.docker.distribution.manifest.v2+json' manifest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y gcc libc6-dev --no-install-recommends
|
||||
|
||||
COPY . /usr/src/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y gcc libc6-dev --no-install-recommends
|
||||
|
||||
COPY . /usr/src/
|
||||
|
||||
@@ -279,6 +279,19 @@ func (n *nodeRunner) handleNodeExit(node *swarmnode.Node) {
|
||||
close(n.done)
|
||||
select {
|
||||
case <-n.ready:
|
||||
// there is a case where a node can be promoted to manager while
|
||||
// another node is leaving the cluster. the node being promoted, by
|
||||
// random chance, picks the IP of the node being demoted as the one it
|
||||
// tries to connect to. in this case, the promotion will fail, and the
|
||||
// whole swarm Node object packs it in.
|
||||
//
|
||||
// when the Node object is relaunched by this code, because it has
|
||||
// joinAddr in the config, it attempts again to connect to the same
|
||||
// no-longer-manager node, and crashes again. this continues forever.
|
||||
//
|
||||
// to avoid this case, in this block, we remove JoinAddr from the
|
||||
// config.
|
||||
n.config.joinAddr = ""
|
||||
n.enableReconnectWatcher()
|
||||
default:
|
||||
if n.repeatedRun {
|
||||
|
||||
@@ -384,6 +384,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
|
||||
|
||||
func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
|
||||
var err error
|
||||
var originResolvConfPath string
|
||||
|
||||
// Set the correct paths for /etc/hosts and /etc/resolv.conf, based on the
|
||||
// networking-mode of the container. Note that containers with "container"
|
||||
@@ -397,8 +398,8 @@ func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container
|
||||
*sboxOptions = append(
|
||||
*sboxOptions,
|
||||
libnetwork.OptionOriginHostsPath("/etc/hosts"),
|
||||
libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"),
|
||||
)
|
||||
originResolvConfPath = "/etc/resolv.conf"
|
||||
case container.HostConfig.NetworkMode.IsUserDefined():
|
||||
// The container uses a user-defined network. We use the embedded DNS
|
||||
// server for container name resolution and to act as a DNS forwarder
|
||||
@@ -411,10 +412,7 @@ func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container
|
||||
// If systemd-resolvd is used, the "upstream" DNS servers can be found in
|
||||
// /run/systemd/resolve/resolv.conf. We do not query those DNS servers
|
||||
// directly, as they can be dynamically reconfigured.
|
||||
*sboxOptions = append(
|
||||
*sboxOptions,
|
||||
libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"),
|
||||
)
|
||||
originResolvConfPath = "/etc/resolv.conf"
|
||||
default:
|
||||
// For other situations, such as the default bridge network, container
|
||||
// discovery / name resolution is handled through /etc/hosts, and no
|
||||
@@ -427,12 +425,16 @@ func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container
|
||||
// DNS servers on the host can be dynamically updated.
|
||||
//
|
||||
// Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf)
|
||||
*sboxOptions = append(
|
||||
*sboxOptions,
|
||||
libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()),
|
||||
)
|
||||
originResolvConfPath = daemon.configStore.GetResolvConf()
|
||||
}
|
||||
|
||||
// Allow tests to point at their own resolv.conf file.
|
||||
if envPath := os.Getenv("DOCKER_TEST_RESOLV_CONF_PATH"); envPath != "" {
|
||||
logrus.Infof("Using OriginResolvConfPath from env: %s", envPath)
|
||||
originResolvConfPath = envPath
|
||||
}
|
||||
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(originResolvConfPath))
|
||||
|
||||
container.HostsPath, err = container.GetRootResourcePath("hosts")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -101,8 +101,8 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
|
||||
memory.DisableOOMKiller = config.OomKillDisable
|
||||
}
|
||||
|
||||
if config.KernelMemory != 0 {
|
||||
memory.Kernel = &config.KernelMemory
|
||||
if config.KernelMemory != 0 { //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
memory.Kernel = &config.KernelMemory //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
}
|
||||
|
||||
if config.KernelMemoryTCP != 0 {
|
||||
|
||||
@@ -138,7 +138,14 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, config ty
|
||||
container.RWLayer = nil
|
||||
}
|
||||
|
||||
if err := containerfs.EnsureRemoveAll(container.Root); err != nil {
|
||||
// Hold the container lock while deleting the container root directory
|
||||
// so that other goroutines don't attempt to concurrently open files
|
||||
// within it. Having any file open on Windows (without the
|
||||
// FILE_SHARE_DELETE flag) will block it from being deleted.
|
||||
container.Lock()
|
||||
err := containerfs.EnsureRemoveAll(container.Root)
|
||||
container.Unlock()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
|
||||
container.SetRemovalError(err)
|
||||
return err
|
||||
|
||||
@@ -412,7 +412,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
|
||||
return err
|
||||
}
|
||||
if lower != "" {
|
||||
if err := ioutils.AtomicWriteFile(path.Join(dir, lowerFile), []byte(lower), 0o666); err != nil {
|
||||
if err := ioutils.AtomicWriteFile(path.Join(dir, lowerFile), []byte(lower), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +250,9 @@ func (i *ImageService) CreateImage(config []byte, parent string) (builder.Image,
|
||||
return nil, errors.Wrapf(err, "failed to set parent %s", parent)
|
||||
}
|
||||
}
|
||||
if err := i.imageStore.SetBuiltLocally(id); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to mark image %s as built locally", id)
|
||||
}
|
||||
|
||||
return i.imageStore.Get(id)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ func (i *ImageService) CommitImage(c backend.CommitConfig) (image.ID, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := i.imageStore.SetBuiltLocally(id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if c.ParentImageID != "" {
|
||||
if err := i.imageStore.SetParent(id, image.ID(c.ParentImageID)); err != nil {
|
||||
|
||||
@@ -187,7 +187,7 @@ func messageToProto(msg *logger.Message, proto *logdriver.LogEntry, partial *log
|
||||
func protoToMessage(proto *logdriver.LogEntry) *logger.Message {
|
||||
msg := &logger.Message{
|
||||
Source: proto.Source,
|
||||
Timestamp: time.Unix(0, proto.TimeNano),
|
||||
Timestamp: time.Unix(0, proto.TimeNano).UTC(),
|
||||
}
|
||||
if proto.Partial {
|
||||
var md backend.PartialLogMetaData
|
||||
|
||||
@@ -66,7 +66,7 @@ func getTailReader(ctx context.Context, r loggerutils.SizeReaderAt, req int) (io
|
||||
}
|
||||
|
||||
if msgLen != binary.BigEndian.Uint32(buf) {
|
||||
return nil, 0, errdefs.DataLoss(errors.Wrap(err, "log message header and footer indicate different message sizes"))
|
||||
return nil, 0, errdefs.DataLoss(errors.New("log message header and footer indicate different message sizes"))
|
||||
}
|
||||
|
||||
found++
|
||||
|
||||
@@ -83,7 +83,7 @@ func setNvidiaGPUs(s *specs.Spec, dev *deviceInstance) error {
|
||||
if s.Hooks == nil {
|
||||
s.Hooks = &specs.Hooks{}
|
||||
}
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ //nolint:staticcheck // ignore SA1019
|
||||
Path: path,
|
||||
Args: []string{
|
||||
nvidiaHook,
|
||||
|
||||
@@ -72,7 +72,7 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
|
||||
if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
|
||||
target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe")
|
||||
shortNetCtlrID := stringid.TruncateID(daemon.netController.ID())
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ //nolint:staticcheck // ignore SA1019
|
||||
Path: target,
|
||||
Args: []string{
|
||||
"libnetwork-setkey",
|
||||
|
||||
@@ -59,8 +59,8 @@ func toContainerdResources(resources container.Resources) *libcontainerdtypes.Re
|
||||
if resources.MemoryReservation != 0 {
|
||||
memory.Reservation = &resources.MemoryReservation
|
||||
}
|
||||
if resources.KernelMemory != 0 {
|
||||
memory.Kernel = &resources.KernelMemory
|
||||
if resources.KernelMemory != 0 { //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
memory.Kernel = &resources.KernelMemory //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
}
|
||||
if resources.MemorySwap > 0 {
|
||||
memory.Swap = &resources.MemorySwap
|
||||
|
||||
31
hack/dind
31
hack/dind
@@ -11,8 +11,39 @@ set -e
|
||||
# Usage: dind CMD [ARG...]
|
||||
|
||||
# apparmor sucks and Docker needs to know that it's in a container (c) @tianon
|
||||
#
|
||||
# Set the container env-var, so that AppArmor is enabled in the daemon and
|
||||
# containerd when running docker-in-docker.
|
||||
#
|
||||
# see: https://github.com/containerd/containerd/blob/787943dc1027a67f3b52631e084db0d4a6be2ccc/pkg/apparmor/apparmor_linux.go#L29-L45
|
||||
# see: https://github.com/moby/moby/commit/de191e86321f7d3136ff42ff75826b8107399497
|
||||
export container=docker
|
||||
|
||||
# Allow AppArmor to work inside the container;
|
||||
#
|
||||
# aa-status
|
||||
# apparmor filesystem is not mounted.
|
||||
# apparmor module is loaded.
|
||||
#
|
||||
# mount -t securityfs none /sys/kernel/security
|
||||
#
|
||||
# aa-status
|
||||
# apparmor module is loaded.
|
||||
# 30 profiles are loaded.
|
||||
# 30 profiles are in enforce mode.
|
||||
# /snap/snapd/18357/usr/lib/snapd/snap-confine
|
||||
# ...
|
||||
#
|
||||
# Note: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/sensitive-mounts#sys-kernel-security
|
||||
#
|
||||
# ## /sys/kernel/security
|
||||
#
|
||||
# In /sys/kernel/security mounted the securityfs interface, which allows
|
||||
# configuration of Linux Security Modules. This allows configuration of
|
||||
# AppArmor policies, and so access to this may allow a container to disable
|
||||
# its MAC system.
|
||||
#
|
||||
# Given that we're running privileged already, this should not be an issue.
|
||||
if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then
|
||||
mount -t securityfs none /sys/kernel/security || {
|
||||
echo >&2 'Could not mount /sys/kernel/security.'
|
||||
|
||||
100
hack/dind-systemd
Executable file
100
hack/dind-systemd
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Set the container env-var, so that AppArmor is enabled in the daemon and
|
||||
# containerd when running docker-in-docker.
|
||||
#
|
||||
# see: https://github.com/containerd/containerd/blob/787943dc1027a67f3b52631e084db0d4a6be2ccc/pkg/apparmor/apparmor_linux.go#L29-L45
|
||||
# see: https://github.com/moby/moby/commit/de191e86321f7d3136ff42ff75826b8107399497
|
||||
container=docker
|
||||
export container
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo >&2 'ERROR: No command specified. You probably want to run `journalctl -f`, or maybe `bash`?'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -t 0 ]; then
|
||||
echo >&2 'ERROR: TTY needs to be enabled (`docker run -t ...`).'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Allow AppArmor to work inside the container;
|
||||
#
|
||||
# aa-status
|
||||
# apparmor filesystem is not mounted.
|
||||
# apparmor module is loaded.
|
||||
#
|
||||
# mount -t securityfs none /sys/kernel/security
|
||||
#
|
||||
# aa-status
|
||||
# apparmor module is loaded.
|
||||
# 30 profiles are loaded.
|
||||
# 30 profiles are in enforce mode.
|
||||
# /snap/snapd/18357/usr/lib/snapd/snap-confine
|
||||
# ...
|
||||
#
|
||||
# Note: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/sensitive-mounts#sys-kernel-security
|
||||
#
|
||||
# ## /sys/kernel/security
|
||||
#
|
||||
# In /sys/kernel/security mounted the securityfs interface, which allows
|
||||
# configuration of Linux Security Modules. This allows configuration of
|
||||
# AppArmor policies, and so access to this may allow a container to disable
|
||||
# its MAC system.
|
||||
#
|
||||
# Given that we're running privileged already, this should not be an issue.
|
||||
if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then
|
||||
mount -t securityfs none /sys/kernel/security || {
|
||||
echo >&2 'Could not mount /sys/kernel/security.'
|
||||
echo >&2 'AppArmor detection and --privileged mode might break.'
|
||||
}
|
||||
fi
|
||||
|
||||
env > /etc/docker-entrypoint-env
|
||||
|
||||
cat > /etc/systemd/system/docker-entrypoint.target << EOF
|
||||
[Unit]
|
||||
Description=the target for docker-entrypoint.service
|
||||
Requires=docker-entrypoint.service systemd-logind.service systemd-user-sessions.service
|
||||
EOF
|
||||
|
||||
quoted_args="$(printf " %q" "${@}")"
|
||||
echo "${quoted_args}" > /etc/docker-entrypoint-cmd
|
||||
|
||||
cat > /etc/systemd/system/docker-entrypoint.service << EOF
|
||||
[Unit]
|
||||
Description=docker-entrypoint.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -exc "source /etc/docker-entrypoint-cmd"
|
||||
# EXIT_STATUS is either an exit code integer or a signal name string, see systemd.exec(5)
|
||||
ExecStopPost=/bin/bash -ec "if echo \${EXIT_STATUS} | grep [A-Z] > /dev/null; then echo >&2 \"got signal \${EXIT_STATUS}\"; systemctl exit \$(( 128 + \$( kill -l \${EXIT_STATUS} ) )); else systemctl exit \${EXIT_STATUS}; fi"
|
||||
StandardInput=tty-force
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
WorkingDirectory=$(pwd)
|
||||
EnvironmentFile=/etc/docker-entrypoint-env
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl mask systemd-firstboot.service systemd-udevd.service
|
||||
systemctl unmask systemd-logind
|
||||
systemctl enable docker-entrypoint.service
|
||||
|
||||
systemd=
|
||||
if [ -x /lib/systemd/systemd ]; then
|
||||
systemd=/lib/systemd/systemd
|
||||
elif [ -x /usr/lib/systemd/systemd ]; then
|
||||
systemd=/usr/lib/systemd/systemd
|
||||
elif [ -x /sbin/init ]; then
|
||||
systemd=/sbin/init
|
||||
else
|
||||
echo >&2 'ERROR: systemd is not installed'
|
||||
exit 1
|
||||
fi
|
||||
systemd_args="--show-status=false --unit=docker-entrypoint.target"
|
||||
echo "$0: starting $systemd $systemd_args"
|
||||
exec $systemd $systemd_args
|
||||
@@ -15,7 +15,7 @@ set -e
|
||||
# the binary version you may also need to update the vendor version to pick up
|
||||
# bug fixes or new APIs, however, usually the Go packages are built from a
|
||||
# commit from the master branch.
|
||||
: "${CONTAINERD_VERSION:=v1.6.22}"
|
||||
: "${CONTAINERD_VERSION:=v1.6.38}"
|
||||
|
||||
install_containerd() (
|
||||
echo "Install containerd version $CONTAINERD_VERSION"
|
||||
|
||||
@@ -9,7 +9,7 @@ set -e
|
||||
# the containerd project first, and update both after that is merged.
|
||||
#
|
||||
# When updating RUNC_VERSION, consider updating runc in vendor.mod accordingly
|
||||
: "${RUNC_VERSION:=v1.1.9}"
|
||||
: "${RUNC_VERSION:=v1.1.12}"
|
||||
|
||||
install_runc() {
|
||||
RUNC_BUILDTAGS="${RUNC_BUILDTAGS:-"seccomp"}"
|
||||
|
||||
@@ -332,10 +332,26 @@ Function Run-UnitTests() {
|
||||
$pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration"
|
||||
$pkgList = $pkgList -replace "`r`n", " "
|
||||
|
||||
$jsonFilePath = $bundlesDir + "\go-test-report-unit-flaky-tests.json"
|
||||
$xmlFilePath = $bundlesDir + "\junit-report-unit-flaky-tests.xml"
|
||||
$coverageFilePath = $bundlesDir + "\coverage-report-unit-flaky-tests.txt"
|
||||
$goTestArg = "--rerun-fails=4 --format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath """ + "--packages=$pkgList" + """ -- " + $raceParm + " -coverprofile=$coverageFilePath -covermode=atomic -ldflags -w -a -test.timeout=10m -test.run=TestFlaky.*"
|
||||
Write-Host "INFO: Invoking unit tests run with $GOTESTSUM_LOCATION\gotestsum.exe $goTestArg"
|
||||
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$pinfo.FileName = "$GOTESTSUM_LOCATION\gotestsum.exe"
|
||||
$pinfo.WorkingDirectory = "$($PWD.Path)"
|
||||
$pinfo.UseShellExecute = $false
|
||||
$pinfo.Arguments = $goTestArg
|
||||
$p = New-Object System.Diagnostics.Process
|
||||
$p.StartInfo = $pinfo
|
||||
$p.Start() | Out-Null
|
||||
$p.WaitForExit()
|
||||
if ($p.ExitCode -ne 0) { Throw "Unit tests (flaky) failed" }
|
||||
|
||||
$jsonFilePath = $bundlesDir + "\go-test-report-unit-tests.json"
|
||||
$xmlFilePath = $bundlesDir + "\junit-report-unit-tests.xml"
|
||||
$coverageFilePath = $bundlesDir + "\coverage-report-unit-tests.txt"
|
||||
$goTestArg = "--format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- " + $raceParm + " -coverprofile=$coverageFilePath -covermode=atomic -ldflags -w -a """ + "-test.timeout=10m" + """ $pkgList"
|
||||
$goTestArg = "--format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- " + $raceParm + " -coverprofile=$coverageFilePath -covermode=atomic -ldflags -w -a -test.timeout=10m -test.skip=TestFlaky.*" + " $pkgList"
|
||||
Write-Host "INFO: Invoking unit tests run with $GOTESTSUM_LOCATION\gotestsum.exe $goTestArg"
|
||||
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$pinfo.FileName = "$GOTESTSUM_LOCATION\gotestsum.exe"
|
||||
@@ -353,7 +369,7 @@ Function Run-UnitTests() {
|
||||
Function Run-IntegrationTests() {
|
||||
$escRoot = [Regex]::Escape($root)
|
||||
$env:DOCKER_INTEGRATION_DAEMON_DEST = $bundlesDir + "\tmp"
|
||||
$dirs = go list -test -f '{{- if ne .ForTest `"`" -}}{{- .Dir -}}{{- end -}}' .\integration\...
|
||||
$dirs = go list -test -f '{{- if ne .ForTest "" -}}{{- .Dir -}}{{- end -}}' .\integration\...
|
||||
ForEach($dir in $dirs) {
|
||||
# Normalize directory name for using in the test results files.
|
||||
$normDir = $dir.Trim()
|
||||
|
||||
@@ -74,6 +74,18 @@ source "${MAKEDIR}/.go-autogen"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(go env GOARCH)" = "arm" ] && [ "$(go env GOARM)" = "5" ]; then
|
||||
# cross-compiling for arm/v5 fails on go1.22; a fix is included for this
|
||||
# in go1.23 (https://github.com/golang/go/issues/65290), but for go1.22
|
||||
# we can set the correct option manually.
|
||||
CGO_CFLAGS+=" -Wno-atomic-alignment"
|
||||
export CGO_CFLAGS
|
||||
|
||||
# Make sure libatomic is included on arm/v5, because clang does not auto-link it.
|
||||
# see https://github.com/moby/moby/pull/46982#issuecomment-2206992611
|
||||
export CGO_LDFLAGS="-latomic"
|
||||
fi
|
||||
|
||||
echo "Building $([ "$DOCKER_STATIC" = "1" ] && echo "static" || echo "dynamic") $DEST/$BINARY_FULLNAME ($PLATFORM_NAME)..."
|
||||
if [ -n "$DOCKER_DEBUG" ]; then
|
||||
set -x
|
||||
|
||||
@@ -67,12 +67,6 @@ if [ "$DOCKER_EXPERIMENTAL" ]; then
|
||||
fi
|
||||
|
||||
dockerd="dockerd"
|
||||
if [ -f "/sys/fs/cgroup/cgroup.controllers" ]; then
|
||||
if [ -z "$TEST_SKIP_INTEGRATION_CLI" ]; then
|
||||
echo >&2 '# cgroup v2 requires TEST_SKIP_INTEGRATION_CLI to be set'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$DOCKER_ROOTLESS" ]; then
|
||||
if [ -z "$TEST_SKIP_INTEGRATION_CLI" ]; then
|
||||
|
||||
@@ -38,15 +38,38 @@ if [ -n "${base_pkg_list}" ]; then
|
||||
${base_pkg_list}
|
||||
fi
|
||||
if [ -n "${libnetwork_pkg_list}" ]; then
|
||||
rerun_flaky=1
|
||||
|
||||
gotest_extra_flags="-skip=TestFlaky.*"
|
||||
# Custom -run passed, don't run flaky tests separately.
|
||||
if echo "$TESTFLAGS" | grep -Eq '(-run|-test.run)[= ]'; then
|
||||
rerun_flaky=0
|
||||
gotest_extra_flags=""
|
||||
fi
|
||||
|
||||
# libnetwork tests invoke iptables, and cannot be run in parallel. Execute
|
||||
# tests within /libnetwork with '-p=1' to run them sequentially. See
|
||||
# https://github.com/moby/moby/issues/42458#issuecomment-873216754 for details.
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report-libnetwork.json --junitfile=bundles/junit-report-libnetwork.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report-libnetwork.json --junitfile=bundles/junit-report-libnetwork.xml \
|
||||
-- "${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=bundles/coverage-libnetwork.out \
|
||||
-covermode=atomic \
|
||||
-p=1 \
|
||||
${gotest_extra_flags} \
|
||||
${TESTFLAGS} \
|
||||
${libnetwork_pkg_list}
|
||||
|
||||
if [ $rerun_flaky -eq 1 ]; then
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report-libnetwork-flaky.json --junitfile=bundles/junit-report-libnetwork-flaky.xml \
|
||||
--packages "${libnetwork_pkg_list}" \
|
||||
--rerun-fails=4 \
|
||||
-- "${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=bundles/coverage-libnetwork-flaky.out \
|
||||
-covermode=atomic \
|
||||
-p=1 \
|
||||
-test.run 'TestFlaky.*' \
|
||||
${TESTFLAGS}
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -37,6 +37,10 @@ linters-settings:
|
||||
# FIXME make sure all packages have a description. Currently, there's many packages without.
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
gosec:
|
||||
excludes:
|
||||
- G115 # G115: integer overflow conversion; (TODO: verify these: https://github.com/moby/moby/issues/48358)
|
||||
|
||||
issues:
|
||||
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
|
||||
exclude-use-default: false
|
||||
|
||||
@@ -25,7 +25,7 @@ else
|
||||
tee "${ROOTDIR}/go.mod" >&2 <<- EOF
|
||||
module github.com/docker/docker
|
||||
|
||||
go 1.19
|
||||
go 1.23
|
||||
EOF
|
||||
trap 'rm -f "${ROOTDIR}/go.mod"' EXIT
|
||||
fi
|
||||
|
||||
82
image/cache/cache.go
vendored
82
image/cache/cache.go
vendored
@@ -10,7 +10,9 @@ import (
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// NewLocal returns a local image cache, based on parent chain
|
||||
@@ -26,8 +28,8 @@ type LocalImageCache struct {
|
||||
}
|
||||
|
||||
// GetCache returns the image id found in the cache
|
||||
func (lic *LocalImageCache) GetCache(imgID string, config *containertypes.Config) (string, error) {
|
||||
return getImageIDAndError(getLocalCachedImage(lic.store, image.ID(imgID), config))
|
||||
func (lic *LocalImageCache) GetCache(imgID string, config *containertypes.Config, platform ocispec.Platform) (string, error) {
|
||||
return getImageIDAndError(getLocalCachedImage(lic.store, image.ID(imgID), config, platform))
|
||||
}
|
||||
|
||||
// New returns an image cache, based on history objects
|
||||
@@ -51,8 +53,8 @@ func (ic *ImageCache) Populate(image *image.Image) {
|
||||
}
|
||||
|
||||
// GetCache returns the image id found in the cache
|
||||
func (ic *ImageCache) GetCache(parentID string, cfg *containertypes.Config) (string, error) {
|
||||
imgID, err := ic.localImageCache.GetCache(parentID, cfg)
|
||||
func (ic *ImageCache) GetCache(parentID string, cfg *containertypes.Config, platform ocispec.Platform) (string, error) {
|
||||
imgID, err := ic.localImageCache.GetCache(parentID, cfg, platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -215,7 +217,23 @@ func getImageIDAndError(img *image.Image, err error) (string, error) {
|
||||
// of the image with imgID, that had the same config when it was
|
||||
// created. nil is returned if a child cannot be found. An error is
|
||||
// returned if the parent image cannot be found.
|
||||
func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *containertypes.Config) (*image.Image, error) {
|
||||
func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *containertypes.Config, platform ocispec.Platform) (*image.Image, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
isBuiltLocally := func(id image.ID) bool {
|
||||
builtLocally, err := imageStore.IsBuiltLocally(id)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"id": id,
|
||||
}).Warn("failed to check if image was built locally")
|
||||
return false
|
||||
}
|
||||
return builtLocally
|
||||
}
|
||||
|
||||
// Loop on the children of the given image and check the config
|
||||
getMatch := func(siblings []image.ID) (*image.Image, error) {
|
||||
var match *image.Image
|
||||
@@ -225,6 +243,26 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
|
||||
return nil, fmt.Errorf("unable to find image %q", id)
|
||||
}
|
||||
|
||||
if !isBuiltLocally(id) {
|
||||
continue
|
||||
}
|
||||
|
||||
imgPlatform := ocispec.Platform{
|
||||
Architecture: img.Architecture,
|
||||
OS: img.OS,
|
||||
OSVersion: img.OSVersion,
|
||||
OSFeatures: img.OSFeatures,
|
||||
Variant: img.Variant,
|
||||
}
|
||||
|
||||
// Discard old linux/amd64 images with empty platform.
|
||||
if imgPlatform.OS == "" && imgPlatform.Architecture == "" {
|
||||
continue
|
||||
}
|
||||
if !comparePlatform(platform, imgPlatform) {
|
||||
continue
|
||||
}
|
||||
|
||||
if compare(&img.ContainerConfig, config) {
|
||||
// check for the most up to date match
|
||||
if match == nil || match.Created.Before(img.Created) {
|
||||
@@ -238,11 +276,29 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
|
||||
// In this case, this is `FROM scratch`, which isn't an actual image.
|
||||
if imgID == "" {
|
||||
images := imageStore.Map()
|
||||
|
||||
var siblings []image.ID
|
||||
for id, img := range images {
|
||||
if img.Parent == imgID {
|
||||
siblings = append(siblings, id)
|
||||
if img.Parent != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !isBuiltLocally(id) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Do a quick initial filter on the Cmd to avoid adding all
|
||||
// non-local images with empty parent to the siblings slice and
|
||||
// performing a full config compare.
|
||||
//
|
||||
// config.Cmd is set to the current Dockerfile instruction so we
|
||||
// check it against the img.ContainerConfig.Cmd which is the
|
||||
// command of the last layer.
|
||||
if !strSliceEqual(img.ContainerConfig.Cmd, config.Cmd) {
|
||||
continue
|
||||
}
|
||||
|
||||
siblings = append(siblings, id)
|
||||
}
|
||||
return getMatch(siblings)
|
||||
}
|
||||
@@ -251,3 +307,15 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
|
||||
siblings := imageStore.Children(imgID)
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
func strSliceEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
195
image/cache/compare.go
vendored
195
image/cache/compare.go
vendored
@@ -1,45 +1,106 @@
|
||||
package cache // import "github.com/docker/docker/image/cache"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// compare two Config struct. Do not compare the "Image" nor "Hostname" fields
|
||||
// If OpenStdin is set, then it differs
|
||||
func compare(a, b *container.Config) bool {
|
||||
if a == nil || b == nil ||
|
||||
a.OpenStdin || b.OpenStdin {
|
||||
return false
|
||||
}
|
||||
if a.AttachStdout != b.AttachStdout ||
|
||||
a.AttachStderr != b.AttachStderr ||
|
||||
a.User != b.User ||
|
||||
a.OpenStdin != b.OpenStdin ||
|
||||
a.Tty != b.Tty {
|
||||
return false
|
||||
}
|
||||
// TODO: Remove once containerd image service directly uses the ImageCache and
|
||||
// LocalImageCache structs.
|
||||
func CompareConfig(a, b *container.Config) bool {
|
||||
return compare(a, b)
|
||||
}
|
||||
|
||||
if len(a.Cmd) != len(b.Cmd) ||
|
||||
len(a.Env) != len(b.Env) ||
|
||||
len(a.Labels) != len(b.Labels) ||
|
||||
len(a.ExposedPorts) != len(b.ExposedPorts) ||
|
||||
len(a.Entrypoint) != len(b.Entrypoint) ||
|
||||
len(a.Volumes) != len(b.Volumes) {
|
||||
return false
|
||||
}
|
||||
func comparePlatform(builderPlatform, imagePlatform ocispec.Platform) bool {
|
||||
// On Windows, only check the Major and Minor versions.
|
||||
// The Build and Revision compatibility depends on whether `process` or
|
||||
// `hyperv` isolation used.
|
||||
//
|
||||
// Fixes https://github.com/moby/moby/issues/47307
|
||||
if builderPlatform.OS == "windows" && imagePlatform.OS == builderPlatform.OS {
|
||||
// OSVersion format is:
|
||||
// Major.Minor.Build.Revision
|
||||
builderParts := strings.Split(builderPlatform.OSVersion, ".")
|
||||
imageParts := strings.Split(imagePlatform.OSVersion, ".")
|
||||
|
||||
for i := 0; i < len(a.Cmd); i++ {
|
||||
if a.Cmd[i] != b.Cmd[i] {
|
||||
return false
|
||||
// Major and minor must match.
|
||||
for i := 0; i < 2; i++ {
|
||||
if len(builderParts) > i && len(imageParts) > i && builderParts[i] != imageParts[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(builderParts) >= 3 && len(imageParts) >= 3 {
|
||||
// Keep only Major & Minor.
|
||||
builderParts[0] = imageParts[0]
|
||||
builderParts[1] = imageParts[1]
|
||||
imagePlatform.OSVersion = strings.Join(builderParts, ".")
|
||||
}
|
||||
}
|
||||
|
||||
return platforms.Only(builderPlatform).Match(imagePlatform)
|
||||
}
|
||||
|
||||
// compare two Config struct. Do not container-specific fields:
|
||||
// - Image
|
||||
// - Hostname
|
||||
// - Domainname
|
||||
// - MacAddress
|
||||
func compare(a, b *container.Config) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(a.Env) != len(b.Env) {
|
||||
return false
|
||||
}
|
||||
if len(a.Cmd) != len(b.Cmd) {
|
||||
return false
|
||||
}
|
||||
if len(a.Entrypoint) != len(b.Entrypoint) {
|
||||
return false
|
||||
}
|
||||
if len(a.Shell) != len(b.Shell) {
|
||||
return false
|
||||
}
|
||||
if len(a.ExposedPorts) != len(b.ExposedPorts) {
|
||||
return false
|
||||
}
|
||||
if len(a.Volumes) != len(b.Volumes) {
|
||||
return false
|
||||
}
|
||||
if len(a.Labels) != len(b.Labels) {
|
||||
return false
|
||||
}
|
||||
if len(a.OnBuild) != len(b.OnBuild) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a.Env); i++ {
|
||||
if a.Env[i] != b.Env[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range a.Labels {
|
||||
if v != b.Labels[k] {
|
||||
for i := 0; i < len(a.OnBuild); i++ {
|
||||
if a.OnBuild[i] != b.OnBuild[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(a.Cmd); i++ {
|
||||
if a.Cmd[i] != b.Cmd[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(a.Entrypoint); i++ {
|
||||
if a.Entrypoint[i] != b.Entrypoint[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(a.Shell); i++ {
|
||||
if a.Shell[i] != b.Shell[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -48,16 +109,84 @@ func compare(a, b *container.Config) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(a.Entrypoint); i++ {
|
||||
if a.Entrypoint[i] != b.Entrypoint[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for key := range a.Volumes {
|
||||
if _, exists := b.Volumes[key]; !exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range a.Labels {
|
||||
if v != b.Labels[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if a.AttachStdin != b.AttachStdin {
|
||||
return false
|
||||
}
|
||||
if a.AttachStdout != b.AttachStdout {
|
||||
return false
|
||||
}
|
||||
if a.AttachStderr != b.AttachStderr {
|
||||
return false
|
||||
}
|
||||
if a.NetworkDisabled != b.NetworkDisabled {
|
||||
return false
|
||||
}
|
||||
if a.Tty != b.Tty {
|
||||
return false
|
||||
}
|
||||
if a.OpenStdin != b.OpenStdin {
|
||||
return false
|
||||
}
|
||||
if a.StdinOnce != b.StdinOnce {
|
||||
return false
|
||||
}
|
||||
if a.ArgsEscaped != b.ArgsEscaped {
|
||||
return false
|
||||
}
|
||||
if a.User != b.User {
|
||||
return false
|
||||
}
|
||||
if a.WorkingDir != b.WorkingDir {
|
||||
return false
|
||||
}
|
||||
if a.StopSignal != b.StopSignal {
|
||||
return false
|
||||
}
|
||||
|
||||
if (a.StopTimeout == nil) != (b.StopTimeout == nil) {
|
||||
return false
|
||||
}
|
||||
if a.StopTimeout != nil && b.StopTimeout != nil {
|
||||
if *a.StopTimeout != *b.StopTimeout {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (a.Healthcheck == nil) != (b.Healthcheck == nil) {
|
||||
return false
|
||||
}
|
||||
if a.Healthcheck != nil && b.Healthcheck != nil {
|
||||
if a.Healthcheck.Interval != b.Healthcheck.Interval {
|
||||
return false
|
||||
}
|
||||
if a.Healthcheck.StartPeriod != b.Healthcheck.StartPeriod {
|
||||
return false
|
||||
}
|
||||
if a.Healthcheck.Timeout != b.Healthcheck.Timeout {
|
||||
return false
|
||||
}
|
||||
if a.Healthcheck.Retries != b.Healthcheck.Retries {
|
||||
return false
|
||||
}
|
||||
if len(a.Healthcheck.Test) != len(b.Healthcheck.Test) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a.Healthcheck.Test); i++ {
|
||||
if a.Healthcheck.Test[i] != b.Healthcheck.Test[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
74
image/cache/compare_test.go
vendored
74
image/cache/compare_test.go
vendored
@@ -1,11 +1,15 @@
|
||||
package cache // import "github.com/docker/docker/image/cache"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/go-connections/nat"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
// Just to make life easier
|
||||
@@ -124,3 +128,73 @@ func TestCompare(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlatformCompare(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
builder ocispec.Platform
|
||||
image ocispec.Platform
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "same os and arch",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "same os different arch",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
|
||||
image: ocispec.Platform{Architecture: "arm64", OS: runtime.GOOS},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "same os smaller host variant",
|
||||
builder: ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS},
|
||||
image: ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "same os higher host variant",
|
||||
builder: ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS},
|
||||
image: ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Test for https://github.com/moby/moby/issues/47307
|
||||
name: "different build and revision",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.22621"},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "different revision",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.1234"},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "different major",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "11.0.17763.5329"},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "different minor same osver",
|
||||
builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.1.17763.5329"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "different arch same osver",
|
||||
builder: ocispec.Platform{Architecture: "arm64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
|
||||
expected: false,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Check(t, is.Equal(comparePlatform(tc.builder, tc.image), tc.expected))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package image // import "github.com/docker/docker/image"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -24,6 +25,8 @@ type Store interface {
|
||||
GetParent(id ID) (ID, error)
|
||||
SetLastUpdated(id ID) error
|
||||
GetLastUpdated(id ID) (time.Time, error)
|
||||
SetBuiltLocally(id ID) error
|
||||
IsBuiltLocally(id ID) (bool, error)
|
||||
Children(id ID) []ID
|
||||
Map() map[ID]*Image
|
||||
Heads() map[ID]*Image
|
||||
@@ -291,6 +294,23 @@ func (is *store) GetLastUpdated(id ID) (time.Time, error) {
|
||||
return time.Parse(time.RFC3339Nano, string(bytes))
|
||||
}
|
||||
|
||||
// SetBuiltLocally sets whether image can be used as a builder cache
|
||||
func (is *store) SetBuiltLocally(id ID) error {
|
||||
return is.fs.SetMetadata(id.Digest(), "builtLocally", []byte{1})
|
||||
}
|
||||
|
||||
// IsBuiltLocally returns whether image can be used as a builder cache
|
||||
func (is *store) IsBuiltLocally(id ID) (bool, error) {
|
||||
bytes, err := is.fs.GetMetadata(id.Digest(), "builtLocally")
|
||||
if err != nil || len(bytes) == 0 {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return bytes[0] == 1, nil
|
||||
}
|
||||
|
||||
func (is *store) Children(id ID) []ID {
|
||||
is.RLock()
|
||||
defer is.RUnlock()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -44,7 +44,7 @@ func (s *DockerBenchmarkSuite) BenchmarkConcurrentContainerActions(c *testing.B)
|
||||
args = append(args, sleepCommandForDaemonPlatform()...)
|
||||
out, _, err := dockerCmdWithError(args...)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,29 +57,29 @@ func (s *DockerBenchmarkSuite) BenchmarkConcurrentContainerActions(c *testing.B)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
out, _, err = dockerCmdWithError("cp", id+":/tmp", tmpDir)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
return
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdWithError("kill", id)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdWithError("start", id)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdWithError("kill", id)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
}
|
||||
|
||||
// don't do an rm -f here since it can potentially ignore errors from the graphdriver
|
||||
out, _, err = dockerCmdWithError("rm", id)
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -89,7 +89,7 @@ func (s *DockerBenchmarkSuite) BenchmarkConcurrentContainerActions(c *testing.B)
|
||||
for i := 0; i < numIterations; i++ {
|
||||
out, _, err := dockerCmdWithError("ps")
|
||||
if err != nil {
|
||||
chErr <- fmt.Errorf(out)
|
||||
chErr <- errors.New(out)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -114,7 +114,7 @@ func (s *DockerBenchmarkSuite) BenchmarkLogsCLIRotateFollow(c *testing.B) {
|
||||
ch <- nil
|
||||
out, _, _ := dockerCmdWithError("logs", "-f", id)
|
||||
// if this returns at all, it's an error
|
||||
ch <- fmt.Errorf(out)
|
||||
ch <- errors.New(out)
|
||||
}()
|
||||
|
||||
<-ch
|
||||
|
||||
@@ -25,8 +25,8 @@ var expectedNetworkInterfaceStats = strings.Split("rx_bytes rx_dropped rx_errors
|
||||
|
||||
func (s *DockerAPISuite) TestAPIStatsNoStreamGetCpu(c *testing.T) {
|
||||
skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;usleep 100; do echo 'Hello'; done")
|
||||
|
||||
id := strings.TrimSpace(out)
|
||||
assert.NilError(c, waitRun(id))
|
||||
resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/icmd"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
type DockerCLIBuildSuite struct {
|
||||
@@ -3949,6 +3950,7 @@ func (s *DockerCLIBuildSuite) TestBuildEmptyStringVolume(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIBuildSuite) TestBuildContainerWithCgroupParent(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
cgroupParent := "test"
|
||||
data, err := os.ReadFile("/proc/self/cgroup")
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/icmd"
|
||||
"gotest.tools/v3/poll"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
const containerdSocket = "/var/run/docker/containerd/containerd.sock"
|
||||
@@ -861,8 +862,8 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *testing.T) {
|
||||
// which may happen if it was created with the same IP range.
|
||||
deleteInterface(c, "docker0")
|
||||
|
||||
bridgeName := "ext-bridge5"
|
||||
bridgeIP := "192.169.1.1/24"
|
||||
const bridgeName = "ext-bridge5"
|
||||
const bridgeIP = "192.169.1.1/24"
|
||||
|
||||
createInterface(c, "bridge", bridgeName, bridgeIP)
|
||||
defer deleteInterface(c, bridgeName)
|
||||
@@ -870,19 +871,30 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *testing.T) {
|
||||
d.StartWithBusybox(c, "--bridge", bridgeName, "--icc=false")
|
||||
defer d.Restart(c)
|
||||
|
||||
result := icmd.RunCommand("iptables", "-nvL", "FORWARD")
|
||||
result := icmd.RunCommand("sh", "-c", "iptables -vL FORWARD | grep DROP")
|
||||
result.Assert(c, icmd.Success)
|
||||
regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
|
||||
matched, _ := regexp.MatchString(regex, result.Combined())
|
||||
assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined()))
|
||||
|
||||
// strip whitespace and newlines to verify we only found a single DROP
|
||||
out := strings.TrimSpace(result.Stdout())
|
||||
assert.Assert(c, is.Equal(strings.Count(out, "\n"), 0), "only expected a single DROP rules")
|
||||
|
||||
// Column headers are stripped because of grep-ing, but should be:
|
||||
//
|
||||
// pkts bytes target prot opt in out source destination
|
||||
// 0 0 DROP all -- ext-bridge5 ext-bridge5 anywhere anywhere
|
||||
cols := strings.Fields(out)
|
||||
|
||||
expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"}
|
||||
assert.DeepEqual(c, cols, expected)
|
||||
|
||||
// Pinging another container must fail with --icc=false
|
||||
pingContainers(c, d, true)
|
||||
|
||||
ipStr := "192.171.1.1/24"
|
||||
ip, _, _ := net.ParseCIDR(ipStr)
|
||||
ifName := "icc-dummy"
|
||||
const cidr = "192.171.1.1/24"
|
||||
ip, _, _ := net.ParseCIDR(cidr)
|
||||
const ifName = "icc-dummy"
|
||||
|
||||
createInterface(c, "dummy", ifName, ipStr)
|
||||
createInterface(c, "dummy", ifName, cidr)
|
||||
defer deleteInterface(c, ifName)
|
||||
|
||||
// But, Pinging external or a Host interface must succeed
|
||||
@@ -899,8 +911,8 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *testing.T) {
|
||||
// which may happen if it was created with the same IP range.
|
||||
deleteInterface(c, "docker0")
|
||||
|
||||
bridgeName := "ext-bridge6"
|
||||
bridgeIP := "192.169.1.1/24"
|
||||
const bridgeName = "ext-bridge6"
|
||||
const bridgeIP = "192.169.1.1/24"
|
||||
|
||||
createInterface(c, "bridge", bridgeName, bridgeIP)
|
||||
defer deleteInterface(c, bridgeName)
|
||||
@@ -908,11 +920,22 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *testing.T) {
|
||||
d.StartWithBusybox(c, "--bridge", bridgeName, "--icc=false")
|
||||
defer d.Restart(c)
|
||||
|
||||
result := icmd.RunCommand("iptables", "-nvL", "FORWARD")
|
||||
result := icmd.RunCommand("sh", "-c", "iptables -vL FORWARD | grep DROP")
|
||||
result.Assert(c, icmd.Success)
|
||||
regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
|
||||
matched, _ := regexp.MatchString(regex, result.Combined())
|
||||
assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined()))
|
||||
|
||||
// strip whitespace and newlines to verify we only found a single DROP
|
||||
out := strings.TrimSpace(result.Stdout())
|
||||
assert.Assert(c, is.Equal(strings.Count(out, "\n"), 0), "only expected a single DROP rules")
|
||||
|
||||
// Column headers are stripped because of grep-ing, but should be:
|
||||
//
|
||||
// pkts bytes target prot opt in out source destination
|
||||
// 0 0 DROP all -- ext-bridge6 ext-bridge6 anywhere anywhere
|
||||
cols := strings.Fields(out)
|
||||
|
||||
expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"}
|
||||
assert.DeepEqual(c, cols, expected)
|
||||
|
||||
out, err := d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
|
||||
assert.NilError(c, err, out)
|
||||
|
||||
@@ -1700,8 +1723,11 @@ func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *testing.T) {
|
||||
defer mount.Unmount(testDir)
|
||||
|
||||
// create a 3MiB image (with a 2MiB ext4 fs) and mount it as graph root
|
||||
// Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:bullseye (which is what the test suite runs under if run from the Makefile)
|
||||
dockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0")
|
||||
//
|
||||
// Why in a container? Because `mount` sometimes behaves weirdly and often
|
||||
// fails outright on this test in debian:jessie (which is what the test suite
|
||||
// runs under if run from the Makefile at the time this patch was added).
|
||||
cli.DockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0")
|
||||
icmd.RunCommand("mkfs.ext4", "-F", filepath.Join(testDir, "testfs.img")).Assert(c, icmd.Success)
|
||||
|
||||
dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount/vfs && mount -n -t ext4 /test/testfs.img /test/test-mount/vfs")
|
||||
@@ -1711,7 +1737,7 @@ func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *testing.T) {
|
||||
defer s.d.Stop(c)
|
||||
|
||||
// pull a repository large enough to overfill the mounted filesystem
|
||||
pullOut, err := s.d.Cmd("pull", "debian:bullseye-slim")
|
||||
pullOut, err := s.d.Cmd("pull", "debian:bookworm-slim")
|
||||
assert.Assert(c, err != nil, pullOut)
|
||||
assert.Assert(c, strings.Contains(pullOut, "no space left on device"))
|
||||
}
|
||||
@@ -1777,6 +1803,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *testing.T)
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
cgroupParent := "test"
|
||||
name := "cgroup-test"
|
||||
|
||||
@@ -1571,7 +1571,7 @@ func (s *DockerCLINetworkSuite) TestEmbeddedDNSInvalidInput(c *testing.T) {
|
||||
dockerCmd(c, "network", "create", "-d", "bridge", "nw1")
|
||||
|
||||
// Sending garbage to embedded DNS shouldn't crash the daemon
|
||||
dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye-slim", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
|
||||
dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bookworm-slim", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
|
||||
}
|
||||
|
||||
func (s *DockerCLINetworkSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) {
|
||||
|
||||
@@ -2913,7 +2913,7 @@ func (s *DockerCLIRunSuite) TestRunUnshareProc(c *testing.T) {
|
||||
|
||||
go func() {
|
||||
name := "acidburn"
|
||||
out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
|
||||
out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bookworm-slim", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
|
||||
if err == nil ||
|
||||
!(strings.Contains(strings.ToLower(out), "permission denied") ||
|
||||
strings.Contains(strings.ToLower(out), "operation not permitted")) {
|
||||
@@ -2925,7 +2925,7 @@ func (s *DockerCLIRunSuite) TestRunUnshareProc(c *testing.T) {
|
||||
|
||||
go func() {
|
||||
name := "cereal"
|
||||
out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
||||
out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bookworm-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
||||
if err == nil ||
|
||||
!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
|
||||
strings.Contains(strings.ToLower(out), "permission denied") ||
|
||||
@@ -2939,7 +2939,7 @@ func (s *DockerCLIRunSuite) TestRunUnshareProc(c *testing.T) {
|
||||
/* Ensure still fails if running privileged with the default policy */
|
||||
go func() {
|
||||
name := "crashoverride"
|
||||
out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
||||
out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:bookworm-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
||||
if err == nil ||
|
||||
!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
|
||||
strings.Contains(strings.ToLower(out), "permission denied") ||
|
||||
@@ -3219,6 +3219,7 @@ func (s *DockerCLIRunSuite) TestRunWithUlimits(c *testing.T) {
|
||||
func (s *DockerCLIRunSuite) TestRunContainerWithCgroupParent(c *testing.T) {
|
||||
// Not applicable on Windows as uses Unix specific functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
// cgroup-parent relative path
|
||||
testRunContainerWithCgroupParent(c, "test", "cgroup-test")
|
||||
@@ -3254,6 +3255,7 @@ func testRunContainerWithCgroupParent(c *testing.T, cgroupParent, name string) {
|
||||
func (s *DockerCLIRunSuite) TestRunInvalidCgroupParent(c *testing.T) {
|
||||
// Not applicable on Windows as uses Unix specific functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
testRunInvalidCgroupParent(c, "../../../../../../../../SHOULD_NOT_EXIST", "SHOULD_NOT_EXIST", "cgroup-invalid-test")
|
||||
|
||||
@@ -3294,6 +3296,7 @@ func (s *DockerCLIRunSuite) TestRunContainerWithCgroupMountRO(c *testing.T) {
|
||||
// Not applicable on Windows as uses Unix specific functionality
|
||||
// --read-only + userns has remount issues
|
||||
testRequires(c, DaemonIsLinux, NotUserNamespace)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
filename := "/sys/fs/cgroup/devices/test123"
|
||||
out, _, err := dockerCmdWithError("run", "busybox", "touch", filename)
|
||||
@@ -4416,6 +4419,7 @@ func (s *DockerCLIRunSuite) TestRunHostnameInHostMode(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunAddDeviceCgroupRule(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
deviceRule := "c 7:128 rwm"
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/moby/sys/mount"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// #6509
|
||||
@@ -450,6 +451,7 @@ func (s *DockerCLIRunSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *te
|
||||
// "test" should be printed
|
||||
func (s *DockerCLIRunSuite) TestRunWithCPUQuota(c *testing.T) {
|
||||
testRequires(c, cpuCfsQuota)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
|
||||
out, _ := dockerCmd(c, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "cat", file)
|
||||
@@ -461,6 +463,7 @@ func (s *DockerCLIRunSuite) TestRunWithCPUQuota(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithCpuPeriod(c *testing.T) {
|
||||
testRequires(c, cpuCfsPeriod)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
|
||||
out, _ := dockerCmd(c, "run", "--cpu-period", "50000", "--name", "test", "busybox", "cat", file)
|
||||
@@ -491,6 +494,7 @@ func (s *DockerCLIRunSuite) TestRunWithInvalidCpuPeriod(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithCPUShares(c *testing.T) {
|
||||
testRequires(c, cpuShare)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/cpu/cpu.shares"
|
||||
out, _ := dockerCmd(c, "run", "--cpu-shares", "1000", "--name", "test", "busybox", "cat", file)
|
||||
@@ -511,6 +515,7 @@ func (s *DockerCLIRunSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *test
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithCpusetCpus(c *testing.T) {
|
||||
testRequires(c, cgroupCpuset)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/cpuset/cpuset.cpus"
|
||||
out, _ := dockerCmd(c, "run", "--cpuset-cpus", "0", "--name", "test", "busybox", "cat", file)
|
||||
@@ -522,6 +527,7 @@ func (s *DockerCLIRunSuite) TestRunWithCpusetCpus(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithCpusetMems(c *testing.T) {
|
||||
testRequires(c, cgroupCpuset)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/cpuset/cpuset.mems"
|
||||
out, _ := dockerCmd(c, "run", "--cpuset-mems", "0", "--name", "test", "busybox", "cat", file)
|
||||
@@ -533,6 +539,7 @@ func (s *DockerCLIRunSuite) TestRunWithCpusetMems(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithBlkioWeight(c *testing.T) {
|
||||
testRequires(c, blkioWeight)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/blkio/blkio.weight"
|
||||
out, _ := dockerCmd(c, "run", "--blkio-weight", "300", "--name", "test", "busybox", "cat", file)
|
||||
@@ -544,6 +551,7 @@ func (s *DockerCLIRunSuite) TestRunWithBlkioWeight(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithInvalidBlkioWeight(c *testing.T) {
|
||||
testRequires(c, blkioWeight)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
out, _, err := dockerCmdWithError("run", "--blkio-weight", "5", "busybox", "true")
|
||||
assert.ErrorContains(c, err, "", out)
|
||||
expected := "Range of blkio weight is from 10 to 1000"
|
||||
@@ -602,6 +610,7 @@ func (s *DockerCLIRunSuite) TestRunOOMExitCode(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithMemoryLimit(c *testing.T) {
|
||||
testRequires(c, memoryLimitSupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
|
||||
cli.DockerCmd(c, "run", "-m", "32M", "--name", "test", "busybox", "cat", file).Assert(c, icmd.Expected{
|
||||
@@ -646,6 +655,7 @@ func (s *DockerCLIRunSuite) TestRunWithSwappinessInvalid(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithMemoryReservation(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
|
||||
out, _ := dockerCmd(c, "run", "--memory-reservation", "200M", "--name", "test", "busybox", "cat", file)
|
||||
@@ -730,6 +740,8 @@ func (s *DockerCLIRunSuite) TestRunInvalidCpusetMemsFlagValue(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunInvalidCPUShares(c *testing.T) {
|
||||
testRequires(c, cpuShare, DaemonIsLinux)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
out, _, err := dockerCmdWithError("run", "--cpu-shares", "1", "busybox", "echo", "test")
|
||||
assert.ErrorContains(c, err, "", out)
|
||||
expected := "minimum allowed cpu-shares is 2"
|
||||
@@ -840,12 +852,12 @@ func (s *DockerCLIRunSuite) TestRunTmpfsMountsWithOptions(c *testing.T) {
|
||||
assert.Assert(c, strings.Contains(out, option))
|
||||
}
|
||||
|
||||
// We use debian:bullseye-slim as there is no findmnt in busybox. Also the output will be in the format of
|
||||
// We use debian:bookworm-slim as there is no findmnt in busybox. Also the output will be in the format of
|
||||
// TARGET PROPAGATION
|
||||
// /tmp shared
|
||||
// so we only capture `shared` here.
|
||||
expectedOptions = []string{"shared"}
|
||||
out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bullseye-slim", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
|
||||
out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bookworm-slim", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
|
||||
for _, option := range expectedOptions {
|
||||
assert.Assert(c, strings.Contains(out, option))
|
||||
}
|
||||
@@ -881,7 +893,7 @@ func (s *DockerCLIRunSuite) TestRunSysctls(c *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bullseye-slim unshare' exits with operation not permitted.
|
||||
// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bookworm-slim unshare' exits with operation not permitted.
|
||||
func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
|
||||
jsonData := `{
|
||||
@@ -904,7 +916,7 @@ func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) {
|
||||
}
|
||||
icmd.RunCommand(dockerBinary, "run", "--security-opt", "apparmor=unconfined",
|
||||
"--security-opt", "seccomp="+tmpFile.Name(),
|
||||
"debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
|
||||
"debian:bookworm-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Operation not permitted",
|
||||
})
|
||||
@@ -944,7 +956,7 @@ func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyChmod(c *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bullseye-slim unshare --map-root-user --user sh -c whoami' with a specific profile to
|
||||
// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bookworm-slim unshare --map-root-user --user sh -c whoami' with a specific profile to
|
||||
// deny unshare of a userns exits with operation not permitted.
|
||||
func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
|
||||
@@ -976,7 +988,7 @@ func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T)
|
||||
}
|
||||
icmd.RunCommand(dockerBinary, "run",
|
||||
"--security-opt", "apparmor=unconfined", "--security-opt", "seccomp="+tmpFile.Name(),
|
||||
"debian:bullseye-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
|
||||
"debian:bookworm-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Operation not permitted",
|
||||
})
|
||||
@@ -1028,12 +1040,12 @@ func (s *DockerCLIRunSuite) TestRunSeccompProfileAllow32Bit(c *testing.T) {
|
||||
icmd.RunCommand(dockerBinary, "run", "syscall-test", "exit32-test").Assert(c, icmd.Success)
|
||||
}
|
||||
|
||||
// TestRunSeccompAllowSetrlimit checks that 'docker run debian:bullseye-slim ulimit -v 1048510' succeeds.
|
||||
// TestRunSeccompAllowSetrlimit checks that 'docker run debian:bookworm-slim ulimit -v 1048510' succeeds.
|
||||
func (s *DockerCLIRunSuite) TestRunSeccompAllowSetrlimit(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
|
||||
|
||||
// ulimit uses setrlimit, so we want to make sure we don't break it
|
||||
icmd.RunCommand(dockerBinary, "run", "debian:bullseye-slim", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
|
||||
icmd.RunCommand(dockerBinary, "run", "debian:bookworm-slim", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
|
||||
}
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunSeccompDefaultProfileAcct(c *testing.T) {
|
||||
@@ -1329,7 +1341,7 @@ func (s *DockerCLIRunSuite) TestRunApparmorProcDirectory(c *testing.T) {
|
||||
func (s *DockerCLIRunSuite) TestRunSeccompWithDefaultProfile(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
|
||||
|
||||
out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bullseye-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
|
||||
out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bookworm-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
|
||||
assert.ErrorContains(c, err, "", out)
|
||||
assert.Equal(c, strings.TrimSpace(out), "unshare: unshare failed: Operation not permitted")
|
||||
}
|
||||
@@ -1384,6 +1396,7 @@ func (s *DockerCLIRunSuite) TestRunDeviceSymlink(c *testing.T) {
|
||||
// TestRunPIDsLimit makes sure the pids cgroup is set with --pids-limit
|
||||
func (s *DockerCLIRunSuite) TestRunPIDsLimit(c *testing.T) {
|
||||
testRequires(c, testEnv.IsLocalDaemon, pidsLimit)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/pids/pids.max"
|
||||
out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "4", "busybox", "cat", file)
|
||||
@@ -1395,6 +1408,7 @@ func (s *DockerCLIRunSuite) TestRunPIDsLimit(c *testing.T) {
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunPrivilegedAllowedDevices(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux, NotUserNamespace)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file := "/sys/fs/cgroup/devices/devices.list"
|
||||
out, _ := dockerCmd(c, "run", "--privileged", "busybox", "cat", file)
|
||||
@@ -1545,6 +1559,7 @@ func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *testing.T)
|
||||
|
||||
func (s *DockerCLIRunSuite) TestRunWithNanoCPUs(c *testing.T) {
|
||||
testRequires(c, cpuCfsQuota, cpuCfsPeriod)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
|
||||
file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
|
||||
|
||||
@@ -75,7 +75,7 @@ func (s *DockerSwarmSuite) TestServiceLogsCompleteness(c *testing.T) {
|
||||
name := "TestServiceLogsCompleteness"
|
||||
|
||||
// make a service that prints 6 lines
|
||||
out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for line in $(seq 0 5); do echo log test $line; done; exec tail /dev/null")
|
||||
out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for line in $(seq 0 5); do echo log test $line; done; exec tail -f /dev/null")
|
||||
assert.NilError(c, err)
|
||||
assert.Assert(c, strings.TrimSpace(out) != "")
|
||||
|
||||
@@ -126,7 +126,7 @@ func (s *DockerSwarmSuite) TestServiceLogsSince(c *testing.T) {
|
||||
|
||||
name := "TestServiceLogsSince"
|
||||
|
||||
out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for i in $(seq 1 3); do usleep 100000; echo log$i; done; exec tail /dev/null")
|
||||
out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for i in $(seq 1 3); do usleep 100000; echo log$i; done; exec tail -f /dev/null")
|
||||
assert.NilError(c, err)
|
||||
assert.Assert(c, strings.TrimSpace(out) != "")
|
||||
poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/testutil/request"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
func (s *DockerCLIUpdateSuite) TearDownTest(c *testing.T) {
|
||||
@@ -30,6 +31,7 @@ func (s *DockerCLIUpdateSuite) OnTimeout(c *testing.T) {
|
||||
func (s *DockerCLIUpdateSuite) TestUpdateRunningContainer(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
|
||||
@@ -45,6 +47,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateRunningContainer(c *testing.T) {
|
||||
func (s *DockerCLIUpdateSuite) TestUpdateRunningContainerWithRestart(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
|
||||
@@ -61,6 +64,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateRunningContainerWithRestart(c *testing.
|
||||
func (s *DockerCLIUpdateSuite) TestUpdateStoppedContainer(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
|
||||
@@ -76,6 +80,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateStoppedContainer(c *testing.T) {
|
||||
func (s *DockerCLIUpdateSuite) TestUpdatePausedContainer(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, cpuShare)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
|
||||
@@ -94,6 +99,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateWithUntouchedFields(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
testRequires(c, cpuShare)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
|
||||
@@ -134,6 +140,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateSwapMemoryOnly(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
testRequires(c, swapMemorySupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
|
||||
@@ -150,6 +157,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, memoryLimitSupport)
|
||||
testRequires(c, swapMemorySupport)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
name := "test-update-container"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
|
||||
@@ -244,6 +252,7 @@ func (s *DockerCLIUpdateSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testin
|
||||
|
||||
func (s *DockerCLIUpdateSuite) TestUpdateWithNanoCPUs(c *testing.T) {
|
||||
testRequires(c, cpuCfsQuota, cpuCfsPeriod)
|
||||
skip.If(c, onlyCgroupsv2(), "FIXME: cgroupsV2 not supported yet")
|
||||
|
||||
file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
|
||||
file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
|
||||
|
||||
@@ -48,7 +48,7 @@ func ensureSyscallTest(c *testing.T) {
|
||||
|
||||
dockerFile := filepath.Join(tmp, "Dockerfile")
|
||||
content := []byte(`
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
COPY . /usr/bin/
|
||||
`)
|
||||
err = os.WriteFile(dockerFile, content, 0600)
|
||||
@@ -64,7 +64,7 @@ func ensureSyscallTest(c *testing.T) {
|
||||
}
|
||||
|
||||
func ensureSyscallTestBuild(c *testing.T) {
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bullseye-slim")
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bookworm-slim")
|
||||
assert.NilError(c, err)
|
||||
|
||||
var buildArgs []string
|
||||
@@ -102,7 +102,7 @@ func ensureNNPTest(c *testing.T) {
|
||||
|
||||
dockerfile := filepath.Join(tmp, "Dockerfile")
|
||||
content := `
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
COPY . /usr/bin
|
||||
RUN chmod +s /usr/bin/nnp-test
|
||||
`
|
||||
@@ -119,7 +119,7 @@ func ensureNNPTest(c *testing.T) {
|
||||
}
|
||||
|
||||
func ensureNNPTestBuild(c *testing.T) {
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bullseye-slim")
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bookworm-slim")
|
||||
assert.NilError(c, err)
|
||||
|
||||
var buildArgs []string
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/cgroups"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
@@ -69,6 +70,11 @@ func bridgeNfIptables() bool {
|
||||
return !SysInfo.BridgeNFCallIPTablesDisabled
|
||||
}
|
||||
|
||||
func onlyCgroupsv2() bool {
|
||||
// Only check for unified, cgroup v1 tests can run under other modes
|
||||
return cgroups.Mode() == cgroups.Unified
|
||||
}
|
||||
|
||||
func unprivilegedUsernsClone() bool {
|
||||
content, err := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
|
||||
return err != nil || !strings.Contains(string(content), "0")
|
||||
|
||||
5
integration-cli/requirements_windows_test.go
Normal file
5
integration-cli/requirements_windows_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func onlyCgroupsv2() bool {
|
||||
return false
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
|
||||
clientUserRemap := dUserRemap.NewClientT(t)
|
||||
defer clientUserRemap.Close()
|
||||
|
||||
err = load.FrozenImagesLinux(clientUserRemap, "debian:bullseye-slim")
|
||||
err = load.FrozenImagesLinux(clientUserRemap, "debian:bookworm-slim")
|
||||
assert.NilError(t, err)
|
||||
|
||||
dUserRemapRunning := true
|
||||
@@ -54,7 +54,7 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
|
||||
}()
|
||||
|
||||
dockerfile := `
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y libcap2-bin --no-install-recommends
|
||||
RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep
|
||||
`
|
||||
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/testutil"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
@@ -133,7 +135,8 @@ func TestImportWithCustomPlatform(t *testing.T) {
|
||||
reference,
|
||||
types.ImageImportOptions{Platform: tc.platform})
|
||||
if tc.expectedErr != "" {
|
||||
assert.ErrorContains(t, err, tc.expectedErr)
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
@@ -71,3 +75,75 @@ func Run(ctx context.Context, t *testing.T, client client.APIClient, ops ...func
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
type RunResult struct {
|
||||
ContainerID string
|
||||
ExitCode int
|
||||
Stdout *bytes.Buffer
|
||||
Stderr *bytes.Buffer
|
||||
}
|
||||
|
||||
func RunAttach(ctx context.Context, t *testing.T, client client.APIClient, ops ...func(config *TestContainerConfig)) RunResult {
|
||||
t.Helper()
|
||||
|
||||
ops = append(ops, func(c *TestContainerConfig) {
|
||||
c.Config.AttachStdout = true
|
||||
c.Config.AttachStderr = true
|
||||
})
|
||||
id := Create(ctx, t, client, ops...)
|
||||
|
||||
aresp, err := client.ContainerAttach(ctx, id, types.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ContainerStart(ctx, id, types.ContainerStartOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
s, err := demultiplexStreams(ctx, aresp)
|
||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// Inspect to get the exit code. A new context is used here to make sure that if the context passed as argument as
|
||||
// reached timeout during the demultiplexStream call, we still return a RunResult.
|
||||
resp, err := client.ContainerInspect(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
|
||||
return RunResult{ContainerID: id, ExitCode: resp.State.ExitCode, Stdout: &s.stdout, Stderr: &s.stderr}
|
||||
}
|
||||
|
||||
type streams struct {
|
||||
stdout, stderr bytes.Buffer
|
||||
}
|
||||
|
||||
// demultiplexStreams starts a goroutine to demultiplex stdout and stderr from the types.HijackedResponse resp and
|
||||
// waits until either multiplexed stream reaches EOF or the context expires. It unconditionally closes resp and waits
|
||||
// until the demultiplexing goroutine has finished its work before returning.
|
||||
func demultiplexStreams(ctx context.Context, resp types.HijackedResponse) (streams, error) {
|
||||
var s streams
|
||||
outputDone := make(chan error, 1)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
_, err := stdcopy.StdCopy(&s.stdout, &s.stderr, resp.Reader)
|
||||
outputDone <- err
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
var err error
|
||||
select {
|
||||
case copyErr := <-outputDone:
|
||||
err = copyErr
|
||||
break
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
}
|
||||
|
||||
resp.Close()
|
||||
wg.Wait()
|
||||
return s, err
|
||||
}
|
||||
|
||||
@@ -33,3 +33,10 @@ func CreateNoError(ctx context.Context, t *testing.T, client client.APIClient, n
|
||||
assert.NilError(t, err)
|
||||
return name
|
||||
}
|
||||
|
||||
func RemoveNoError(ctx context.Context, t *testing.T, apiClient client.APIClient, name string) {
|
||||
t.Helper()
|
||||
|
||||
err := apiClient.NetworkRemove(ctx, name)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
34
integration/networking/main_test.go
Normal file
34
integration/networking/main_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/testutil/environment"
|
||||
)
|
||||
|
||||
var testEnv *environment.Execution
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = environment.EnsureFrozenImagesLinux(testEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testEnv.Print()
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) context.Context {
|
||||
environment.ProtectAll(t, testEnv)
|
||||
t.Cleanup(func() { testEnv.Clean(t) })
|
||||
return context.Background()
|
||||
}
|
||||
142
integration/networking/resolvconf_test.go
Normal file
142
integration/networking/resolvconf_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
"github.com/miekg/dns"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// writeTempResolvConf writes a resolv.conf that only contains a single
|
||||
// nameserver line, with address addr.
|
||||
// It returns the name of the temp file.
|
||||
func writeTempResolvConf(t *testing.T, addr string) string {
|
||||
t.Helper()
|
||||
// Not using t.TempDir() here because in rootless mode, while the temporary
|
||||
// directory gets mode 0777, it's a subdir of an 0700 directory owned by root.
|
||||
// So, it's not accessible by the daemon.
|
||||
f, err := os.CreateTemp("", "resolv.conf")
|
||||
assert.NilError(t, err)
|
||||
t.Cleanup(func() { os.Remove(f.Name()) })
|
||||
err = f.Chmod(0644)
|
||||
assert.NilError(t, err)
|
||||
f.Write([]byte("nameserver " + addr + "\n"))
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
const dnsRespAddr = "10.11.12.13"
|
||||
|
||||
// startDaftDNS starts and returns a really, really daft DNS server that only
|
||||
// responds to type-A requests, and always with address dnsRespAddr.
|
||||
func startDaftDNS(t *testing.T, addr string) *dns.Server {
|
||||
serveDNS := func(w dns.ResponseWriter, query *dns.Msg) {
|
||||
if query.Question[0].Qtype == dns.TypeA {
|
||||
resp := &dns.Msg{}
|
||||
resp.SetReply(query)
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: query.Question[0].Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 600,
|
||||
},
|
||||
}
|
||||
answer.A = net.ParseIP(dnsRespAddr)
|
||||
resp.Answer = append(resp.Answer, answer)
|
||||
_ = w.WriteMsg(resp)
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", &net.UDPAddr{
|
||||
IP: net.ParseIP(addr),
|
||||
Port: 53,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
server := &dns.Server{Handler: dns.HandlerFunc(serveDNS), PacketConn: conn}
|
||||
go func() {
|
||||
_ = server.ActivateAndServe()
|
||||
}()
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// Check that when a container is connected to an internal network, DNS
|
||||
// requests sent to daemon's internal DNS resolver are not forwarded to
|
||||
// an upstream resolver listening on a localhost address.
|
||||
// (Assumes the host does not already have a DNS server on 127.0.0.1.)
|
||||
func TestInternalNetworkDNS(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No resolv.conf on Windows")
|
||||
skip.If(t, testEnv.IsRootless, "Can't use resolver on host in rootless mode")
|
||||
ctx := setupTest(t)
|
||||
|
||||
// Start a DNS server on the loopback interface.
|
||||
server := startDaftDNS(t, "127.0.0.1")
|
||||
defer server.Shutdown()
|
||||
|
||||
// Set up a temp resolv.conf pointing at that DNS server, and a daemon using it.
|
||||
tmpFileName := writeTempResolvConf(t, "127.0.0.1")
|
||||
d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName))
|
||||
d.StartWithBusybox(t, "--experimental", "--ip6tables")
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
intNetName := "intnet"
|
||||
network.CreateNoError(ctx, t, c, intNetName,
|
||||
network.WithDriver("bridge"),
|
||||
network.WithInternal(),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, intNetName)
|
||||
|
||||
extNetName := "extnet"
|
||||
network.CreateNoError(ctx, t, c, extNetName,
|
||||
network.WithDriver("bridge"),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, extNetName)
|
||||
|
||||
// Create a container, initially with external connectivity.
|
||||
// Expect the external DNS server to respond to a request from the container.
|
||||
ctrId := container.Run(ctx, t, c, container.WithNetworkMode(extNetName))
|
||||
defer c.ContainerRemove(ctx, ctrId, types.ContainerRemoveOptions{Force: true})
|
||||
res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.ExitCode, 0))
|
||||
assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr))
|
||||
|
||||
// Connect the container to the internal network as well.
|
||||
// External DNS should still be used.
|
||||
err = c.NetworkConnect(ctx, intNetName, ctrId, nil)
|
||||
assert.NilError(t, err)
|
||||
res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.ExitCode, 0))
|
||||
assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr))
|
||||
|
||||
// Disconnect from the external network.
|
||||
// Expect no access to the external DNS.
|
||||
err = c.NetworkDisconnect(ctx, extNetName, ctrId, true)
|
||||
assert.NilError(t, err)
|
||||
res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.ExitCode, 1))
|
||||
assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL"))
|
||||
|
||||
// Reconnect the external network.
|
||||
// Check that the external DNS server is used again.
|
||||
err = c.NetworkConnect(ctx, extNetName, ctrId, nil)
|
||||
assert.NilError(t, err)
|
||||
res, err = container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.ExitCode, 0))
|
||||
assert.Check(t, is.Contains(res.Stdout(), dnsRespAddr))
|
||||
}
|
||||
@@ -117,6 +117,49 @@ func TestPluginInstall(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
|
||||
t.Run("with digest", func(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
reg := registry.NewV2(t)
|
||||
defer reg.Close()
|
||||
|
||||
name := "test-" + strings.ToLower(t.Name())
|
||||
repo := path.Join(registry.DefaultURL, name+":latest")
|
||||
err := plugin.Create(ctx, client, repo)
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr, err := client.PluginPush(ctx, repo, "")
|
||||
assert.NilError(t, err)
|
||||
defer rdr.Close()
|
||||
|
||||
buf := &strings.Builder{}
|
||||
assert.NilError(t, err)
|
||||
var digest string
|
||||
assert.NilError(t, jsonmessage.DisplayJSONMessagesStream(rdr, buf, 0, false, func(j jsonmessage.JSONMessage) {
|
||||
if j.Aux != nil {
|
||||
var r types.PushResult
|
||||
assert.NilError(t, json.Unmarshal(*j.Aux, &r))
|
||||
digest = r.Digest
|
||||
}
|
||||
}), buf)
|
||||
|
||||
err = client.PluginRemove(ctx, repo, types.PluginRemoveOptions{Force: true})
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr, err = client.PluginInstall(ctx, repo, types.PluginInstallOptions{
|
||||
Disabled: true,
|
||||
RemoteRef: repo + "@" + digest,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
defer rdr.Close()
|
||||
|
||||
_, err = io.Copy(io.Discard, rdr)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, _, err = client.PluginInspectWithRaw(ctx, repo)
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
|
||||
t.Run("with htpasswd", func(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
|
||||
@@ -186,3 +186,27 @@ func (sb *sandbox) getGatewayEndpoint() *endpoint {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasExternalConnectivity returns true if the sandbox is connected to any
|
||||
// endpoint which provides external connectivity.
|
||||
//
|
||||
// This function is only necessary on branches without
|
||||
// https://github.com/moby/moby/pull/46603. With that PR applied, this function
|
||||
// would be equivalent to sb.getGatewayEndpoint() != nil.
|
||||
func (sb *sandbox) hasExternalConnectivity() bool {
|
||||
for _, ep := range sb.getConnectedEndpoints() {
|
||||
n := ep.getNetwork()
|
||||
switch n.Type() {
|
||||
case "null", "host":
|
||||
continue
|
||||
case "bridge":
|
||||
if n.Internal() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(ep.Gateway()) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ func setINC(version iptables.IPVersion, iface string, enable bool) error {
|
||||
logrus.Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf(msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
logrus.Warn(msg)
|
||||
}
|
||||
|
||||
@@ -544,8 +544,12 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) {
|
||||
return sb.setupDefaultGW()
|
||||
}
|
||||
|
||||
moveExtConn := sb.getGatewayEndpoint() != extEp
|
||||
// Enable upstream forwarding if the sandbox gained external connectivity.
|
||||
if sb.resolver != nil {
|
||||
sb.resolver.SetForwardingPolicy(sb.hasExternalConnectivity())
|
||||
}
|
||||
|
||||
moveExtConn := sb.getGatewayEndpoint() != extEp
|
||||
if moveExtConn {
|
||||
if extEp != nil {
|
||||
logrus.Debugf("Revoking external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID())
|
||||
@@ -777,6 +781,11 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
|
||||
return sb.setupDefaultGW()
|
||||
}
|
||||
|
||||
// Disable upstream forwarding if the sandbox lost external connectivity.
|
||||
if sb.resolver != nil {
|
||||
sb.resolver.SetForwardingPolicy(sb.hasExternalConnectivity())
|
||||
}
|
||||
|
||||
// New endpoint providing external connectivity for the sandbox
|
||||
extEp = sb.getGatewayEndpoint()
|
||||
if moveExtConn && extEp != nil {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/libnetwork"
|
||||
@@ -31,10 +30,6 @@ import (
|
||||
var controller libnetwork.NetworkController
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if runtime.GOOS == "windows" {
|
||||
logrus.Info("Test suite does not currently support windows")
|
||||
os.Exit(0)
|
||||
}
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ func TestNetworkDBJoinLeaveNetworks(t *testing.T) {
|
||||
closeNetworkDBInstances(t, dbs)
|
||||
}
|
||||
|
||||
func TestNetworkDBCRUDTableEntry(t *testing.T) {
|
||||
func TestFlakyNetworkDBCRUDTableEntry(t *testing.T) {
|
||||
dbs := createNetworkDBInstances(t, 3, "node", DefaultConfig())
|
||||
|
||||
err := dbs[0].JoinNetwork("network1")
|
||||
@@ -271,7 +271,7 @@ func TestNetworkDBCRUDTableEntry(t *testing.T) {
|
||||
closeNetworkDBInstances(t, dbs)
|
||||
}
|
||||
|
||||
func TestNetworkDBCRUDTableEntries(t *testing.T) {
|
||||
func TestFlakyNetworkDBCRUDTableEntries(t *testing.T) {
|
||||
dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig())
|
||||
|
||||
err := dbs[0].JoinNetwork("network1")
|
||||
@@ -341,7 +341,7 @@ func TestNetworkDBCRUDTableEntries(t *testing.T) {
|
||||
closeNetworkDBInstances(t, dbs)
|
||||
}
|
||||
|
||||
func TestNetworkDBNodeLeave(t *testing.T) {
|
||||
func TestFlakyNetworkDBNodeLeave(t *testing.T) {
|
||||
dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig())
|
||||
|
||||
err := dbs[0].JoinNetwork("network1")
|
||||
@@ -835,7 +835,7 @@ func TestParallelDelete(t *testing.T) {
|
||||
closeNetworkDBInstances(t, dbs)
|
||||
}
|
||||
|
||||
func TestNetworkDBIslands(t *testing.T) {
|
||||
func TestFlakyNetworkDBIslands(t *testing.T) {
|
||||
pollTimeout := func() time.Duration {
|
||||
const defaultTimeout = 120 * time.Second
|
||||
dl, ok := t.Deadline()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
@@ -30,6 +31,9 @@ type Resolver interface {
|
||||
// SetExtServers configures the external nameservers the resolver
|
||||
// should use to forward queries
|
||||
SetExtServers([]extDNSEntry)
|
||||
// SetForwardingPolicy re-configures the embedded DNS resolver to either
|
||||
// enable or disable forwarding DNS queries to external servers.
|
||||
SetForwardingPolicy(policy bool)
|
||||
// ResolverOptions returns resolv.conf options that should be set
|
||||
ResolverOptions() []string
|
||||
}
|
||||
@@ -89,21 +93,23 @@ type resolver struct {
|
||||
tStamp time.Time
|
||||
queryLock sync.Mutex
|
||||
listenAddress string
|
||||
proxyDNS bool
|
||||
proxyDNS atomic.Bool
|
||||
resolverKey string
|
||||
startCh chan struct{}
|
||||
}
|
||||
|
||||
// NewResolver creates a new instance of the Resolver
|
||||
func NewResolver(address string, proxyDNS bool, resolverKey string, backend DNSBackend) Resolver {
|
||||
return &resolver{
|
||||
r := &resolver{
|
||||
backend: backend,
|
||||
proxyDNS: proxyDNS,
|
||||
listenAddress: address,
|
||||
resolverKey: resolverKey,
|
||||
err: fmt.Errorf("setup not done yet"),
|
||||
startCh: make(chan struct{}, 1),
|
||||
}
|
||||
r.proxyDNS.Store(proxyDNS)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *resolver) SetupFunc(port int) func() {
|
||||
@@ -196,6 +202,10 @@ func (r *resolver) SetExtServers(extDNS []extDNSEntry) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) SetForwardingPolicy(policy bool) {
|
||||
r.proxyDNS.Store(policy)
|
||||
}
|
||||
|
||||
func (r *resolver) NameServer() string {
|
||||
return r.listenAddress
|
||||
}
|
||||
@@ -407,7 +417,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
||||
if resp == nil {
|
||||
// If the backend doesn't support proxying dns request
|
||||
// fail the response
|
||||
if !r.proxyDNS {
|
||||
if !r.proxyDNS.Load() {
|
||||
resp = new(dns.Msg)
|
||||
resp.SetRcode(query, dns.RcodeServerFailure)
|
||||
if err := w.WriteMsg(resp); err != nil {
|
||||
|
||||
@@ -27,7 +27,11 @@ const (
|
||||
func (sb *sandbox) startResolver(restore bool) {
|
||||
sb.resolverOnce.Do(func() {
|
||||
var err error
|
||||
sb.resolver = NewResolver(resolverIPSandbox, true, sb.Key(), sb)
|
||||
// The resolver is started with proxyDNS=false if the sandbox does not currently
|
||||
// have a gateway. So, if the Sandbox is only connected to an 'internal' network,
|
||||
// it will not forward DNS requests to external resolvers. The resolver's
|
||||
// proxyDNS setting is then updated as network Endpoints are added/removed.
|
||||
sb.resolver = NewResolver(resolverIPSandbox, sb.hasExternalConnectivity(), sb.Key(), sb)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
sb.resolver = nil
|
||||
|
||||
@@ -5,6 +5,7 @@ package libnetwork
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -107,7 +108,7 @@ func processReturn(r io.Reader) error {
|
||||
return fmt.Errorf("failed to read buf in processReturn : %v", err)
|
||||
}
|
||||
if string(buf[0:n]) != success {
|
||||
return fmt.Errorf(string(buf[0:n]))
|
||||
return errors.New(string(buf[0:n]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
||||
wo = &tar.Header{
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: hdr.Mode & int64(os.ModePerm),
|
||||
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir),
|
||||
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
|
||||
Size: 0,
|
||||
Uid: hdr.Uid,
|
||||
Uname: hdr.Uname,
|
||||
@@ -59,7 +59,7 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
||||
Gname: hdr.Gname,
|
||||
AccessTime: hdr.AccessTime,
|
||||
ChangeTime: hdr.ChangeTime,
|
||||
} //#nosec G305 -- An archive is being created, not extracted.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
@@ -52,10 +54,23 @@ type Ctx struct {
|
||||
authReq *Request
|
||||
}
|
||||
|
||||
func isChunked(r *http.Request) bool {
|
||||
// RFC 7230 specifies that content length is to be ignored if Transfer-Encoding is chunked
|
||||
if strings.EqualFold(r.Header.Get("Transfer-Encoding"), "chunked") {
|
||||
return true
|
||||
}
|
||||
for _, v := range r.TransferEncoding {
|
||||
if strings.EqualFold(v, "chunked") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AuthZRequest authorized the request to the docker daemon using authZ plugins
|
||||
func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
|
||||
var body []byte
|
||||
if sendBody(ctx.requestURI, r.Header) && r.ContentLength > 0 && r.ContentLength < maxBodySize {
|
||||
if sendBody(ctx.requestURI, r.Header) && (r.ContentLength > 0 || isChunked(r)) && r.ContentLength < maxBodySize {
|
||||
var err error
|
||||
body, r.Body, err = drainBody(r.Body)
|
||||
if err != nil {
|
||||
@@ -108,7 +123,6 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
||||
if sendBody(ctx.requestURI, rm.Header()) {
|
||||
ctx.authReq.ResponseBody = rm.RawBody()
|
||||
}
|
||||
|
||||
for _, plugin := range ctx.plugins {
|
||||
logrus.Debugf("AuthZ response using plugin %s", plugin.Name())
|
||||
|
||||
@@ -146,10 +160,26 @@ func drainBody(body io.ReadCloser) ([]byte, io.ReadCloser, error) {
|
||||
return nil, newBody, err
|
||||
}
|
||||
|
||||
func isAuthEndpoint(urlPath string) (bool, error) {
|
||||
// eg www.test.com/v1.24/auth/optional?optional1=something&optional2=something (version optional)
|
||||
matched, err := regexp.MatchString(`^[^\/]*\/(v\d[\d\.]*\/)?auth.*`, urlPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
// sendBody returns true when request/response body should be sent to AuthZPlugin
|
||||
func sendBody(url string, header http.Header) bool {
|
||||
func sendBody(inURL string, header http.Header) bool {
|
||||
u, err := url.Parse(inURL)
|
||||
// Assume no if the URL cannot be parsed - an empty request will still be forwarded to the plugin and should be rejected
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip body for auth endpoint
|
||||
if strings.HasSuffix(url, "/auth") {
|
||||
isAuth, err := isAuthEndpoint(u.Path)
|
||||
if isAuth || err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +175,8 @@ func TestDrainBody(t *testing.T) {
|
||||
|
||||
func TestSendBody(t *testing.T) {
|
||||
var (
|
||||
url = "nothing.com"
|
||||
testcases = []struct {
|
||||
url string
|
||||
contentType string
|
||||
expected bool
|
||||
}{
|
||||
@@ -220,15 +220,93 @@ func TestSendBody(t *testing.T) {
|
||||
contentType: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth",
|
||||
contentType: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth?p1=test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/v1/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "https://www.nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "http://nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "http://www.nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "https://www.nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, testcase := range testcases {
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", testcase.contentType)
|
||||
if testcase.url == "" {
|
||||
testcase.url = "nothing.com"
|
||||
}
|
||||
|
||||
if b := sendBody(url, header); b != testcase.expected {
|
||||
t.Fatalf("Unexpected Content-Type; Expected: %t, Actual: %t", testcase.expected, b)
|
||||
if b := sendBody(testcase.url, header); b != testcase.expected {
|
||||
t.Fatalf("sendBody failed: url: %s, content-type: %s; Expected: %t, Actual: %t", testcase.url, testcase.contentType, testcase.expected, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ func dmTaskGetDepsFct(task *cdmTask) *Deps {
|
||||
|
||||
// golang issue: https://github.com/golang/go/issues/11925
|
||||
var devices []C.uint64_t
|
||||
devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices))
|
||||
devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices)) //nolint:staticcheck // ignore SA1019
|
||||
devicesHdr.Data = uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps)))
|
||||
devicesHdr.Len = int(Cdeps.count)
|
||||
devicesHdr.Cap = int(Cdeps.count)
|
||||
|
||||
@@ -3,11 +3,15 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"sync/atomic"
|
||||
|
||||
// make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
|
||||
// TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged.
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
|
||||
@@ -16,10 +20,15 @@ import (
|
||||
type ReadCloserWrapper struct {
|
||||
io.Reader
|
||||
closer func() error
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
// Close calls back the passed closer function
|
||||
func (r *ReadCloserWrapper) Close() error {
|
||||
if !r.closed.CompareAndSwap(false, true) {
|
||||
subsequentCloseWarn("ReadCloserWrapper")
|
||||
return nil
|
||||
}
|
||||
return r.closer()
|
||||
}
|
||||
|
||||
@@ -87,6 +96,7 @@ type cancelReadCloser struct {
|
||||
cancel func()
|
||||
pR *io.PipeReader // Stream to read from
|
||||
pW *io.PipeWriter
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
// NewCancelReadCloser creates a wrapper that closes the ReadCloser when the
|
||||
@@ -146,6 +156,17 @@ func (p *cancelReadCloser) closeWithError(err error) {
|
||||
// Close closes the wrapper its underlying reader. It will cause
|
||||
// future calls to Read to return io.EOF.
|
||||
func (p *cancelReadCloser) Close() error {
|
||||
if !p.closed.CompareAndSwap(false, true) {
|
||||
subsequentCloseWarn("cancelReadCloser")
|
||||
return nil
|
||||
}
|
||||
p.closeWithError(io.EOF)
|
||||
return nil
|
||||
}
|
||||
|
||||
func subsequentCloseWarn(name string) {
|
||||
logrus.Error("subsequent attempt to close " + name)
|
||||
if logrus.GetLevel() >= logrus.DebugLevel {
|
||||
logrus.Errorf("stack trace: %s", string(debug.Stack()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package ioutils // import "github.com/docker/docker/pkg/ioutils"
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// NopWriter represents a type which write operation is nop.
|
||||
type NopWriter struct{}
|
||||
@@ -29,9 +32,14 @@ func (f *NopFlusher) Flush() {}
|
||||
type writeCloserWrapper struct {
|
||||
io.Writer
|
||||
closer func() error
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
func (r *writeCloserWrapper) Close() error {
|
||||
if !r.closed.CompareAndSwap(false, true) {
|
||||
subsequentCloseWarn("WriteCloserWrapper")
|
||||
return nil
|
||||
}
|
||||
return r.closer()
|
||||
}
|
||||
|
||||
|
||||
@@ -200,8 +200,13 @@ func withFetchProgress(cs content.Store, out progress.Output, ref reference.Name
|
||||
switch desc.MediaType {
|
||||
case specs.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest:
|
||||
tn := reference.TagNameOnly(ref)
|
||||
tagged := tn.(reference.Tagged)
|
||||
progress.Messagef(out, tagged.Tag(), "Pulling from %s", reference.FamiliarName(ref))
|
||||
var tagOrDigest string
|
||||
if tagged, ok := tn.(reference.Tagged); ok {
|
||||
tagOrDigest = tagged.Tag()
|
||||
} else {
|
||||
tagOrDigest = tn.String()
|
||||
}
|
||||
progress.Messagef(out, tagOrDigest, "Pulling from %s", reference.FamiliarName(ref))
|
||||
progress.Messagef(out, "", "Digest: %s", desc.Digest.String())
|
||||
return nil, nil
|
||||
case
|
||||
|
||||
@@ -27,6 +27,10 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
|
||||
{{if ge .Version 208096}}
|
||||
# Host (privileged) processes may send signals to container processes.
|
||||
signal (receive) peer=unconfined,
|
||||
# runc may send signals to container processes (for "docker stop").
|
||||
signal (receive) peer=runc,
|
||||
# crun may send signals to container processes (for "docker stop" when used with crun OCI runtime).
|
||||
signal (receive) peer=crun,
|
||||
# dockerd may send signals to container processes (for "docker kill").
|
||||
signal (receive) peer={{.DaemonProfile}},
|
||||
# Container processes may send signals amongst themselves.
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"alarm",
|
||||
"bind",
|
||||
"brk",
|
||||
"cachestat",
|
||||
"capget",
|
||||
"capset",
|
||||
"chdir",
|
||||
@@ -109,6 +110,7 @@
|
||||
"fchdir",
|
||||
"fchmod",
|
||||
"fchmodat",
|
||||
"fchmodat2",
|
||||
"fchown",
|
||||
"fchown32",
|
||||
"fchownat",
|
||||
@@ -130,8 +132,11 @@
|
||||
"ftruncate",
|
||||
"ftruncate64",
|
||||
"futex",
|
||||
"futex_requeue",
|
||||
"futex_time64",
|
||||
"futex_wait",
|
||||
"futex_waitv",
|
||||
"futex_wake",
|
||||
"futimesat",
|
||||
"getcpu",
|
||||
"getcwd",
|
||||
@@ -206,6 +211,7 @@
|
||||
"lstat",
|
||||
"lstat64",
|
||||
"madvise",
|
||||
"map_shadow_stack",
|
||||
"membarrier",
|
||||
"memfd_create",
|
||||
"memfd_secret",
|
||||
@@ -783,7 +789,8 @@
|
||||
"names": [
|
||||
"get_mempolicy",
|
||||
"mbind",
|
||||
"set_mempolicy"
|
||||
"set_mempolicy",
|
||||
"set_mempolicy_home_node"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
|
||||
@@ -56,6 +56,7 @@ func DefaultProfile() *Seccomp {
|
||||
"alarm",
|
||||
"bind",
|
||||
"brk",
|
||||
"cachestat", // kernel v6.5, libseccomp v2.5.5
|
||||
"capget",
|
||||
"capset",
|
||||
"chdir",
|
||||
@@ -101,6 +102,7 @@ func DefaultProfile() *Seccomp {
|
||||
"fchdir",
|
||||
"fchmod",
|
||||
"fchmodat",
|
||||
"fchmodat2", // kernel v6.6, libseccomp v2.5.5
|
||||
"fchown",
|
||||
"fchown32",
|
||||
"fchownat",
|
||||
@@ -122,8 +124,11 @@ func DefaultProfile() *Seccomp {
|
||||
"ftruncate",
|
||||
"ftruncate64",
|
||||
"futex",
|
||||
"futex_requeue", // kernel v6.7, libseccomp v2.5.5
|
||||
"futex_time64",
|
||||
"futex_wait", // kernel v6.7, libseccomp v2.5.5
|
||||
"futex_waitv",
|
||||
"futex_wake", // kernel v6.7, libseccomp v2.5.5
|
||||
"futimesat",
|
||||
"getcpu",
|
||||
"getcwd",
|
||||
@@ -198,6 +203,7 @@ func DefaultProfile() *Seccomp {
|
||||
"lstat",
|
||||
"lstat64",
|
||||
"madvise",
|
||||
"map_shadow_stack", // kernel v6.6, libseccomp v2.5.5
|
||||
"membarrier",
|
||||
"memfd_create",
|
||||
"memfd_secret",
|
||||
@@ -771,6 +777,7 @@ func DefaultProfile() *Seccomp {
|
||||
"get_mempolicy",
|
||||
"mbind",
|
||||
"set_mempolicy",
|
||||
"set_mempolicy_home_node", // kernel v5.17, libseccomp v2.5.4
|
||||
},
|
||||
Action: specs.ActAllow,
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ var nativeToSeccomp = map[string]specs.Arch{
|
||||
"ppc": specs.ArchPPC,
|
||||
"ppc64": specs.ArchPPC64,
|
||||
"ppc64le": specs.ArchPPC64LE,
|
||||
"riscv64": specs.ArchRISCV64,
|
||||
"s390": specs.ArchS390,
|
||||
"s390x": specs.ArchS390X,
|
||||
}
|
||||
@@ -57,6 +58,7 @@ var goToNative = map[string]string{
|
||||
"ppc": "ppc",
|
||||
"ppc64": "ppc64",
|
||||
"ppc64le": "ppc64le",
|
||||
"riscv64": "riscv64",
|
||||
"s390": "s390",
|
||||
"s390x": "s390x",
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const imageSize = 64 * 1024 * 1024
|
||||
|
||||
// CanTestQuota - checks if xfs prjquota can be tested
|
||||
// returns a reason if not
|
||||
func CanTestQuota() (string, bool) {
|
||||
@@ -29,6 +27,12 @@ func CanTestQuota() (string, bool) {
|
||||
// PrepareQuotaTestImage - prepares an xfs prjquota test image
|
||||
// returns the path the the image on success
|
||||
func PrepareQuotaTestImage(t *testing.T) (string, error) {
|
||||
// imageSize is the size of the test-image. The minimum size allowed
|
||||
// is 300MB.
|
||||
//
|
||||
// See https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/commit/?id=6e0ed3d19c54603f0f7d628ea04b550151d8a262
|
||||
const imageSize = 300 * 1024 * 1024
|
||||
|
||||
mkfs, err := exec.LookPath("mkfs.xfs")
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user