mirror of
https://github.com/moby/moby.git
synced 2026-01-12 19:21:41 +00:00
Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5749085e9 | ||
|
|
6236f7b8a4 | ||
|
|
9512677feb | ||
|
|
5bb36e25ba | ||
|
|
45654ed012 | ||
|
|
334099505f | ||
|
|
52a3c39506 | ||
|
|
4fc9786f78 | ||
|
|
46dfcd83bf | ||
|
|
fb51c760c4 | ||
|
|
66bfae52bc | ||
|
|
6ca0546f25 | ||
|
|
2822d49c10 | ||
|
|
64b0c76151 | ||
|
|
5591f0b1ee | ||
|
|
4594e70063 | ||
|
|
7236817725 | ||
|
|
78746ca9e8 | ||
|
|
5853cd510c | ||
|
|
6ee7d86a12 | ||
|
|
ae6284a623 | ||
|
|
8d624c31dd | ||
|
|
1222a7081a | ||
|
|
6f1145e740 | ||
|
|
ef87a664ef | ||
|
|
3dc9802a83 | ||
|
|
fd1fe0b702 | ||
|
|
fdaf08a57b | ||
|
|
4d0b8cc2d7 | ||
|
|
7c63f178e7 | ||
|
|
b811212ccd | ||
|
|
fa8ac94616 | ||
|
|
2199ada691 | ||
|
|
fd7611ff1f | ||
|
|
dbfc648a94 | ||
|
|
8e67dfab97 | ||
|
|
b38d454861 | ||
|
|
4b8336f7cf | ||
|
|
2697d2b687 | ||
|
|
f58f842143 | ||
|
|
6679a5faeb | ||
|
|
90c72824c3 | ||
|
|
ad08dc12e0 | ||
|
|
7b54720ccb | ||
|
|
0922d32bce | ||
|
|
148d9f0e58 | ||
|
|
5070e418b8 | ||
|
|
054c3c2931 | ||
|
|
9406f3622d | ||
|
|
9816bfcaf5 | ||
|
|
52d6ad2a68 | ||
|
|
58e5151270 | ||
|
|
6e5ed2ccce | ||
|
|
54bd14a3fe | ||
|
|
c9ddc6effc | ||
|
|
16836e60bc | ||
|
|
e44436c31f | ||
|
|
34b3cf4b0c | ||
|
|
51618f7a83 | ||
|
|
b499acc0e8 | ||
|
|
67541d5841 | ||
|
|
989fab3c71 | ||
|
|
6bf8dfc4d8 | ||
|
|
e090646d47 | ||
|
|
b3bb2aabb8 | ||
|
|
e69efe2ef5 | ||
|
|
ccab609365 | ||
|
|
0a6866b839 | ||
|
|
cce1763d57 | ||
|
|
3d67dd0465 | ||
|
|
73e2f72a7c | ||
|
|
2926a45be6 | ||
|
|
b73fd4d936 | ||
|
|
bb2adc4496 | ||
|
|
b501aa82d5 | ||
|
|
46a703bb3b | ||
|
|
ff9340ca2c | ||
|
|
90a90ae2e1 | ||
|
|
66ed41aec8 | ||
|
|
ea2e2c5427 | ||
|
|
a5d731edec | ||
|
|
fc576226b2 | ||
|
|
c24fd7a2c3 | ||
|
|
5fb0a7ced7 | ||
|
|
2c26eac566 | ||
|
|
5badfb40eb | ||
|
|
f43fc6650c | ||
|
|
85361af1f7 | ||
|
|
ee40a9ebcd | ||
|
|
e8620110fc | ||
|
|
e988001872 | ||
|
|
6531bac59b | ||
|
|
2a82480df9 | ||
|
|
84a5b528ae | ||
|
|
511741735e | ||
|
|
2b8bc86679 | ||
|
|
4e2dbfa1af | ||
|
|
3a3bfcbf47 | ||
|
|
7be43586af | ||
|
|
d7085abec2 | ||
|
|
fc1d808c44 | ||
|
|
7485ef7e46 | ||
|
|
d2ecc7bad1 | ||
|
|
f121eccf29 | ||
|
|
00a9cf39ed | ||
|
|
c2d0053207 | ||
|
|
4c35d81147 | ||
|
|
28150fc70c | ||
|
|
d2c3163642 | ||
|
|
3153708f13 | ||
|
|
2f94f10342 | ||
|
|
b8a4fe5f8f | ||
|
|
648704522b | ||
|
|
4032b6778d | ||
|
|
5fa80da2d3 | ||
|
|
be371291bc | ||
|
|
1d531ff64f |
17
.DEREK.yml
Normal file
17
.DEREK.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
curators:
|
||||
- aboch
|
||||
- alexellis
|
||||
- andrewhsu
|
||||
- anonymuse
|
||||
- chanwit
|
||||
- ehazlett
|
||||
- fntlnz
|
||||
- gianarb
|
||||
- mgoelzer
|
||||
- programmerq
|
||||
- rheinwein
|
||||
- ripcurld0
|
||||
- thajeztah
|
||||
|
||||
features:
|
||||
- comments
|
||||
@@ -1,4 +1,7 @@
|
||||
bundles
|
||||
.gopath
|
||||
vendor/pkg
|
||||
.go-pkg-cache
|
||||
.git
|
||||
bundles/
|
||||
cli/winresources/**/winres.json
|
||||
cli/winresources/**/*.syso
|
||||
hack/integration-cli-on-swarm/integration-cli-on-swarm
|
||||
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,3 +0,0 @@
|
||||
Dockerfile* linguist-language=Dockerfile
|
||||
vendor.mod linguist-language=Go-Module
|
||||
vendor.sum linguist-language=Go-Checksums
|
||||
7
.github/CODEOWNERS
vendored
7
.github/CODEOWNERS
vendored
@@ -4,12 +4,17 @@
|
||||
# KEEP THIS FILE SORTED. Order is important. Last match takes precedence.
|
||||
|
||||
builder/** @tonistiigi
|
||||
client/** @dnephin
|
||||
contrib/mkimage/** @tianon
|
||||
daemon/graphdriver/devmapper/** @rhvgoyal
|
||||
daemon/graphdriver/lcow/** @johnstep @jhowardmsft
|
||||
daemon/graphdriver/overlay/** @dmcgowan
|
||||
daemon/graphdriver/overlay2/** @dmcgowan
|
||||
daemon/graphdriver/windows/** @johnstep
|
||||
daemon/graphdriver/windows/** @johnstep @jhowardmsft
|
||||
daemon/logger/awslogs/** @samuelkarp
|
||||
hack/** @tianon
|
||||
hack/integration-cli-on-swarm/** @AkihiroSuda
|
||||
integration-cli/** @vdemeester
|
||||
integration/** @vdemeester
|
||||
plugin/** @cpuguy83
|
||||
project/** @thaJeztah
|
||||
|
||||
27
.github/actions/setup-runner/action.yml
vendored
27
.github/actions/setup-runner/action.yml
vendored
@@ -1,27 +0,0 @@
|
||||
name: 'Setup Runner'
|
||||
description: 'Composite action to set up the GitHub Runner for jobs in the test.yml workflow'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- run: |
|
||||
sudo modprobe ip_vs
|
||||
sudo modprobe ipv6
|
||||
sudo modprobe ip6table_filter
|
||||
sudo modprobe -r overlay
|
||||
sudo modprobe overlay redirect_dir=off
|
||||
shell: bash
|
||||
- run: |
|
||||
if [ ! -e /etc/docker/daemon.json ]; then
|
||||
echo '{}' | sudo tee /etc/docker/daemon.json >/dev/null
|
||||
fi
|
||||
DOCKERD_CONFIG=$(jq '.+{"experimental":true,"live-restore":true,"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' /etc/docker/daemon.json)
|
||||
sudo tee /etc/docker/daemon.json <<<"$DOCKERD_CONFIG" >/dev/null
|
||||
sudo service docker restart
|
||||
shell: bash
|
||||
- run: |
|
||||
./contrib/check-config.sh || true
|
||||
shell: bash
|
||||
- run: |
|
||||
docker info
|
||||
shell: bash
|
||||
58
.github/workflows/.dco.yml
vendored
58
.github/workflows/.dco.yml
vendored
@@ -1,58 +0,0 @@
|
||||
# reusable workflow
|
||||
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:
|
||||
|
||||
env:
|
||||
ALPINE_VERSION: 3.16
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Dump context
|
||||
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@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
if (/^refs\/pull\//.test(context.ref) && context.payload?.pull_request?.base?.ref != undefined) {
|
||||
return context.payload.pull_request.base.ref;
|
||||
}
|
||||
return context.ref.replace(/^refs\/heads\//g, '');
|
||||
-
|
||||
name: Validate
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v "$(pwd):/workspace" \
|
||||
-e VALIDATE_REPO \
|
||||
-e VALIDATE_BRANCH \
|
||||
alpine:${{ env.ALPINE_VERSION }} sh -c 'apk add --no-cache -q bash git openssh-client && git config --system --add safe.directory /workspace && cd /workspace && hack/validate/dco'
|
||||
env:
|
||||
VALIDATE_REPO: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
VALIDATE_BRANCH: ${{ steps.base-ref.outputs.result }}
|
||||
517
.github/workflows/.windows.yml
vendored
517
.github/workflows/.windows.yml
vendored
@@ -1,517 +0,0 @@
|
||||
# reusable workflow
|
||||
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:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
send_coverage:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23.9"
|
||||
GOTESTLIST_VERSION: v0.3.1
|
||||
TESTSTAT_VERSION: v0.1.3
|
||||
WINDOWS_BASE_IMAGE: mcr.microsoft.com/windows/servercore
|
||||
WINDOWS_BASE_TAG_2019: ltsc2019
|
||||
WINDOWS_BASE_TAG_2022: ltsc2022
|
||||
TEST_IMAGE_NAME: moby:test
|
||||
TEST_CTN_NAME: moby
|
||||
DOCKER_BUILDKIT: 0
|
||||
ITG_CLI_MATRIX_SIZE: 6
|
||||
|
||||
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
|
||||
BIN_OUT: ${{ github.workspace }}\out
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
name: Env
|
||||
run: |
|
||||
Get-ChildItem Env: | Out-String
|
||||
-
|
||||
name: Init
|
||||
run: |
|
||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go-build"
|
||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go\pkg\mod"
|
||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
-
|
||||
name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
${{ github.workspace }}\go-build
|
||||
${{ env.GOPATH }}\pkg\mod
|
||||
key: ${{ inputs.os }}-${{ github.job }}-${{ hashFiles('**/vendor.sum') }}
|
||||
restore-keys: |
|
||||
${{ inputs.os }}-${{ github.job }}-
|
||||
-
|
||||
name: Docker info
|
||||
run: |
|
||||
docker info
|
||||
-
|
||||
name: Build base image
|
||||
run: |
|
||||
docker pull ${{ env.WINDOWS_BASE_IMAGE }}:${{ env.WINDOWS_BASE_IMAGE_TAG }}
|
||||
docker tag ${{ env.WINDOWS_BASE_IMAGE }}:${{ env.WINDOWS_BASE_IMAGE_TAG }} microsoft/windowsservercore
|
||||
docker build --build-arg GO_VERSION -t ${{ env.TEST_IMAGE_NAME }} -f Dockerfile.windows .
|
||||
-
|
||||
name: Build binaries
|
||||
run: |
|
||||
& docker run --name ${{ env.TEST_CTN_NAME }} -e "DOCKER_GITCOMMIT=${{ github.sha }}" `
|
||||
-v "${{ github.workspace }}\go-build:C:\Users\ContainerAdministrator\AppData\Local\go-build" `
|
||||
-v "${{ github.workspace }}\go\pkg\mod:C:\gopath\pkg\mod" `
|
||||
${{ env.TEST_IMAGE_NAME }} hack\make.ps1 -Daemon -Client
|
||||
-
|
||||
name: Copy artifacts
|
||||
run: |
|
||||
New-Item -ItemType "directory" -Path "${{ env.BIN_OUT }}"
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\src\github.com\docker\docker\bundles\docker.exe" ${{ env.BIN_OUT }}\
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\src\github.com\docker\docker\bundles\dockerd.exe" ${{ env.BIN_OUT }}\
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\bin\gotestsum.exe" ${{ env.BIN_OUT }}\
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\containerd\bin\containerd.exe" ${{ env.BIN_OUT }}\
|
||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\containerd\bin\containerd-shim-runhcs-v1.exe" ${{ env.BIN_OUT }}\
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{ inputs.os }}
|
||||
path: ${{ env.BIN_OUT }}/*
|
||||
if-no-files-found: error
|
||||
retention-days: 2
|
||||
|
||||
unit-test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}\go
|
||||
GOBIN: ${{ github.workspace }}\go\bin
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
name: Env
|
||||
run: |
|
||||
Get-ChildItem Env: | Out-String
|
||||
-
|
||||
name: Init
|
||||
run: |
|
||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go-build"
|
||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go\pkg\mod"
|
||||
New-Item -ItemType "directory" -Path "bundles"
|
||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
-
|
||||
name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
${{ github.workspace }}\go-build
|
||||
${{ env.GOPATH }}\pkg\mod
|
||||
key: ${{ inputs.os }}-${{ github.job }}-${{ hashFiles('**/vendor.sum') }}
|
||||
restore-keys: |
|
||||
${{ inputs.os }}-${{ github.job }}-
|
||||
-
|
||||
name: Docker info
|
||||
run: |
|
||||
docker info
|
||||
-
|
||||
name: Build base image
|
||||
run: |
|
||||
docker pull ${{ env.WINDOWS_BASE_IMAGE }}:${{ env.WINDOWS_BASE_IMAGE_TAG }}
|
||||
docker tag ${{ env.WINDOWS_BASE_IMAGE }}:${{ env.WINDOWS_BASE_IMAGE_TAG }} microsoft/windowsservercore
|
||||
docker build --build-arg GO_VERSION -t ${{ env.TEST_IMAGE_NAME }} -f Dockerfile.windows .
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
& docker run --name ${{ env.TEST_CTN_NAME }} -e "DOCKER_GITCOMMIT=${{ github.sha }}" `
|
||||
-v "${{ github.workspace }}\go-build:C:\Users\ContainerAdministrator\AppData\Local\go-build" `
|
||||
-v "${{ github.workspace }}\go\pkg\mod:C:\gopath\pkg\mod" `
|
||||
-v "${{ env.GOPATH }}\src\github.com\docker\docker\bundles:C:\gopath\src\github.com\docker\docker\bundles" `
|
||||
${{ env.TEST_IMAGE_NAME }} hack\make.ps1 -TestUnit
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: inputs.send_coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
||||
directory: bundles
|
||||
env_vars: RUNNER_OS
|
||||
flags: unit
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
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@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.os }}-unit-reports
|
||||
path: /tmp/artifacts
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/artifacts -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
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@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Install gotestlist
|
||||
run:
|
||||
go install github.com/crazy-max/gotestlist/cmd/gotestlist@${{ env.GOTESTLIST_VERSION }}
|
||||
-
|
||||
name: Create matrix
|
||||
id: tests
|
||||
working-directory: ./integration-cli
|
||||
run: |
|
||||
# This step creates a matrix for integration-cli tests. Tests suites
|
||||
# are distributed in integration-test job through a matrix. There is
|
||||
# also an override being added to the matrix like "./..." to run
|
||||
# "Test integration" step exclusively.
|
||||
matrix="$(gotestlist -d ${{ env.ITG_CLI_MATRIX_SIZE }} -o "./..." ./...)"
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.tests.outputs.matrix }}
|
||||
|
||||
integration-test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- build
|
||||
- integration-test-prepare
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runtime:
|
||||
- builtin
|
||||
- containerd
|
||||
test: ${{ fromJson(needs.integration-test-prepare.outputs.matrix) }}
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}\go
|
||||
GOBIN: ${{ github.workspace }}\go\bin
|
||||
BIN_OUT: ${{ github.workspace }}\out
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
||||
-
|
||||
name: Env
|
||||
run: |
|
||||
Get-ChildItem Env: | Out-String
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-${{ inputs.os }}
|
||||
path: ${{ env.BIN_OUT }}
|
||||
-
|
||||
name: Init
|
||||
run: |
|
||||
New-Item -ItemType "directory" -Path "bundles"
|
||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
||||
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.
|
||||
# more info: https://github.com/actions/virtual-environments/blob/d3a5bad25f3b4326c5666bab0011ac7f1beec95e/images/win/scripts/Installers/Install-Docker.ps1#L11
|
||||
name: Removing current daemon
|
||||
run: |
|
||||
if (Get-Service docker -ErrorAction SilentlyContinue) {
|
||||
$dockerVersion = (docker version -f "{{.Server.Version}}")
|
||||
Write-Host "Current installed Docker version: $dockerVersion"
|
||||
# remove service
|
||||
Stop-Service -Force -Name docker
|
||||
Remove-Service -Name docker
|
||||
# removes event log entry. we could use "Remove-EventLog -LogName -Source docker"
|
||||
# but this cmd is not available atm
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
& reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\docker" /f 2>&1 | Out-Null
|
||||
$ErrorActionPreference = "Stop"
|
||||
Write-Host "Service removed"
|
||||
}
|
||||
-
|
||||
name: Starting containerd
|
||||
if: matrix.runtime == 'containerd'
|
||||
run: |
|
||||
Write-Host "Generating config"
|
||||
& "${{ env.BIN_OUT }}\containerd.exe" config default | Out-File "$env:TEMP\ctn.toml" -Encoding ascii
|
||||
Write-Host "Creating service"
|
||||
New-Item -ItemType Directory "$env:TEMP\ctn-root" -ErrorAction SilentlyContinue | Out-Null
|
||||
New-Item -ItemType Directory "$env:TEMP\ctn-state" -ErrorAction SilentlyContinue | Out-Null
|
||||
Start-Process -Wait "${{ env.BIN_OUT }}\containerd.exe" `
|
||||
-ArgumentList "--log-level=debug", `
|
||||
"--config=$env:TEMP\ctn.toml", `
|
||||
"--address=\\.\pipe\containerd-containerd", `
|
||||
"--root=$env:TEMP\ctn-root", `
|
||||
"--state=$env:TEMP\ctn-state", `
|
||||
"--log-file=$env:TEMP\ctn.log", `
|
||||
"--register-service"
|
||||
Write-Host "Starting service"
|
||||
Start-Service -Name containerd
|
||||
Start-Sleep -Seconds 5
|
||||
Write-Host "Service started successfully!"
|
||||
-
|
||||
name: Starting test daemon
|
||||
run: |
|
||||
Write-Host "Creating service"
|
||||
If ("${{ matrix.runtime }}" -eq "containerd") {
|
||||
$runtimeArg="--containerd=\\.\pipe\containerd-containerd"
|
||||
echo "DOCKER_WINDOWS_CONTAINERD_RUNTIME=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
New-Item -ItemType Directory "$env:TEMP\moby-root" -ErrorAction SilentlyContinue | Out-Null
|
||||
New-Item -ItemType Directory "$env:TEMP\moby-exec" -ErrorAction SilentlyContinue | Out-Null
|
||||
Start-Process -Wait -NoNewWindow "${{ env.BIN_OUT }}\dockerd" `
|
||||
-ArgumentList $runtimeArg, "--debug", `
|
||||
"--host=npipe:////./pipe/docker_engine", `
|
||||
"--data-root=$env:TEMP\moby-root", `
|
||||
"--exec-root=$env:TEMP\moby-exec", `
|
||||
"--pidfile=$env:TEMP\docker.pid", `
|
||||
"--register-service"
|
||||
Write-Host "Starting service"
|
||||
Start-Service -Name docker
|
||||
Write-Host "Service started successfully!"
|
||||
-
|
||||
name: Waiting for test daemon to start
|
||||
run: |
|
||||
$tries=20
|
||||
Write-Host "Waiting for the test daemon to start..."
|
||||
While ($true) {
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
& "${{ env.BIN_OUT }}\docker" version
|
||||
$ErrorActionPreference = "Stop"
|
||||
If ($LastExitCode -eq 0) {
|
||||
break
|
||||
}
|
||||
$tries--
|
||||
If ($tries -le 0) {
|
||||
Throw "Failed to get a response from the daemon"
|
||||
}
|
||||
Write-Host -NoNewline "."
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
Write-Host "Test daemon started and replied!"
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: Docker info
|
||||
run: |
|
||||
& "${{ env.BIN_OUT }}\docker" info
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: Building contrib/busybox
|
||||
run: |
|
||||
& "${{ env.BIN_OUT }}\docker" build -t busybox `
|
||||
--build-arg WINDOWS_BASE_IMAGE `
|
||||
--build-arg WINDOWS_BASE_IMAGE_TAG `
|
||||
.\contrib\busybox\
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: List images
|
||||
run: |
|
||||
& "${{ env.BIN_OUT }}\docker" images
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Test integration
|
||||
if: matrix.test == './...'
|
||||
run: |
|
||||
.\hack\make.ps1 -TestIntegration
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
GO111MODULE: "off"
|
||||
TEST_CLIENT_BINARY: ${{ env.BIN_OUT }}\docker
|
||||
-
|
||||
name: Test integration-cli
|
||||
if: matrix.test != './...'
|
||||
run: |
|
||||
.\hack\make.ps1 -TestIntegrationCli
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
GO111MODULE: "off"
|
||||
TEST_CLIENT_BINARY: ${{ env.BIN_OUT }}\docker
|
||||
INTEGRATION_TESTRUN: ${{ matrix.test }}
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: inputs.send_coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
||||
directory: bundles
|
||||
env_vars: RUNNER_OS
|
||||
flags: integration,${{ matrix.runtime }}
|
||||
-
|
||||
name: Docker info
|
||||
run: |
|
||||
& "${{ env.BIN_OUT }}\docker" info
|
||||
env:
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
-
|
||||
name: Stop containerd
|
||||
if: always() && matrix.runtime == 'containerd'
|
||||
run: |
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
Stop-Service -Force -Name containerd
|
||||
$ErrorActionPreference = "Stop"
|
||||
-
|
||||
name: Containerd logs
|
||||
if: always() && matrix.runtime == 'containerd'
|
||||
run: |
|
||||
Copy-Item "$env:TEMP\ctn.log" -Destination ".\bundles\containerd.log"
|
||||
Get-Content "$env:TEMP\ctn.log" | Out-Host
|
||||
-
|
||||
name: Stop daemon
|
||||
if: always()
|
||||
run: |
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
Stop-Service -Force -Name docker
|
||||
$ErrorActionPreference = "Stop"
|
||||
-
|
||||
# as the daemon is registered as a service we have to check the event
|
||||
# logs against the docker provider.
|
||||
name: Daemon event logs
|
||||
if: always()
|
||||
run: |
|
||||
Get-WinEvent -ea SilentlyContinue `
|
||||
-FilterHashtable @{ProviderName= "docker"; LogName = "application"} |
|
||||
Select-Object -Property TimeCreated, @{N='Detailed Message'; E={$_.Message}} |
|
||||
Sort-Object @{Expression="TimeCreated";Descending=$false} |
|
||||
Select-Object -ExpandProperty 'Detailed Message' | Tee-Object -file ".\bundles\daemon.log"
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runtime:
|
||||
- builtin
|
||||
- containerd
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/reports
|
||||
pattern: ${{ inputs.os }}-${{ inputs.storage }}-integration-reports-${{ matrix.runtime }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
126
.github/workflows/buildkit.yml
vendored
126
.github/workflows/buildkit.yml
vendored
@@ -1,126 +0,0 @@
|
||||
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
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]+'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
DESTDIR: ./build
|
||||
|
||||
jobs:
|
||||
validate-dco:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: binary
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary
|
||||
path: ${{ env.DESTDIR }}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pkg:
|
||||
- client
|
||||
- cmd/buildctl
|
||||
- solver
|
||||
- frontend
|
||||
- frontend/dockerfile
|
||||
typ:
|
||||
- integration
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
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: |
|
||||
echo "$(./hack/buildkit-ref)" >> $GITHUB_ENV
|
||||
working-directory: moby
|
||||
-
|
||||
name: Checkout BuildKit ${{ env.BUILDKIT_REF }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "moby/buildkit"
|
||||
ref: ${{ env.BUILDKIT_REF }}
|
||||
path: buildkit
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Download binary artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binary
|
||||
path: ./buildkit/build/moby/
|
||||
-
|
||||
name: Update daemon.json
|
||||
run: |
|
||||
sudo rm -f /etc/docker/daemon.json
|
||||
sudo service docker restart
|
||||
docker version
|
||||
docker info
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
./hack/test ${{ matrix.typ }}
|
||||
env:
|
||||
CONTEXT: "."
|
||||
TEST_DOCKERD: "1"
|
||||
TEST_DOCKERD_BINARY: "./build/moby/dockerd"
|
||||
TESTPKGS: "./${{ matrix.pkg }}"
|
||||
# Diff/MergeOp tests are skipped
|
||||
TESTFLAGS: "-v --parallel=1 --timeout=30m --run=/^Test([^DM]|.[^ie]|..[^fr]|...[^fg])/worker=dockerd$"
|
||||
working-directory: buildkit
|
||||
127
.github/workflows/ci.yml
vendored
127
.github/workflows/ci.yml
vendored
@@ -1,127 +0,0 @@
|
||||
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
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
DESTDIR: ./build
|
||||
|
||||
jobs:
|
||||
validate-dco:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- binary
|
||||
- dynbinary
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
-
|
||||
name: List artifacts
|
||||
run: |
|
||||
tree -nh ${{ env.DESTDIR }}
|
||||
-
|
||||
name: Check artifacts
|
||||
run: |
|
||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
||||
|
||||
prepare-cross:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
run: |
|
||||
matrix="$(docker buildx bake binary-cross --print | jq -cr '.target."binary-cross".platforms')"
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.platforms.outputs.matrix }}
|
||||
|
||||
cross:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
- prepare-cross
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{ fromJson(needs.prepare-cross.outputs.matrix) }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: all
|
||||
set: |
|
||||
*.platform=${{ matrix.platform }}
|
||||
-
|
||||
name: List artifacts
|
||||
run: |
|
||||
tree -nh ${{ env.DESTDIR }}
|
||||
-
|
||||
name: Check artifacts
|
||||
run: |
|
||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
||||
585
.github/workflows/test.yml
vendored
585
.github/workflows/test.yml
vendored
@@ -1,585 +0,0 @@
|
||||
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
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23.9"
|
||||
GOTESTLIST_VERSION: v0.3.1
|
||||
TESTSTAT_VERSION: v0.1.3
|
||||
ITG_CLI_MATRIX_SIZE: 6
|
||||
DOCKER_EXPERIMENTAL: 1
|
||||
DOCKER_GRAPHDRIVER: overlay2
|
||||
|
||||
jobs:
|
||||
validate-dco:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
build-dev:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
mode:
|
||||
- ""
|
||||
- systemd
|
||||
steps:
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
if [ "${{ matrix.mode }}" = "systemd" ]; then
|
||||
echo "SYSTEMD=true" >> $GITHUB_ENV
|
||||
fi
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
*.cache-from=type=gha,scope=dev${{ matrix.mode }}
|
||||
*.cache-to=type=gha,scope=dev${{ matrix.mode }},mode=max
|
||||
*.output=type=cacheonly
|
||||
|
||||
validate-prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
matrix: ${{ steps.scripts.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: scripts
|
||||
run: |
|
||||
scripts=$(jq -ncR '[inputs]' <<< "$(ls -I .validate -I all -I default -I dco -I golangci-lint.yml -I yamllint.yaml -A ./hack/validate/)")
|
||||
echo "matrix=$scripts" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.scripts.outputs.matrix }}
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-prepare
|
||||
- build-dev
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
script: ${{ fromJson(needs.validate-prepare.outputs.matrix) }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=dev
|
||||
-
|
||||
name: Validate
|
||||
run: |
|
||||
make -o build validate-${{ matrix.script }}
|
||||
|
||||
unit:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=dev
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make -o build test-unit
|
||||
-
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p bundles /tmp/reports
|
||||
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 /tmp/reports
|
||||
sudo chown -R $(id -u):$(id -g) /tmp/reports
|
||||
tree -nh /tmp/reports
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles
|
||||
env_vars: RUNNER_OS
|
||||
flags: unit
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-reports-unit-${{ inputs.storage }}
|
||||
path: /tmp/reports/*
|
||||
retention-days: 1
|
||||
|
||||
unit-report:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
- unit
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: test-reports-unit-${{ inputs.storage }}
|
||||
path: /tmp/reports
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
docker-py:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=dev
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make TEST_SKIP_INTEGRATION_CLI=1 -o build test-docker-py
|
||||
-
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p bundles /tmp/reports
|
||||
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 /tmp/reports
|
||||
sudo chown -R $(id -u):$(id -g) /tmp/reports
|
||||
tree -nh /tmp/reports
|
||||
-
|
||||
name: Test daemon logs
|
||||
if: always()
|
||||
run: |
|
||||
cat bundles/test-docker-py/docker.log
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-reports-docker-py-${{ inputs.storage }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-flaky:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=dev
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make -o build test-integration-flaky
|
||||
env:
|
||||
TEST_SKIP_INTEGRATION_CLI: 1
|
||||
|
||||
integration:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- 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@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
CACHE_DEV_SCOPE=dev
|
||||
if [[ "${{ matrix.mode }}" == *"rootless"* ]]; then
|
||||
echo "DOCKER_ROOTLESS=1" >> $GITHUB_ENV
|
||||
fi
|
||||
if [[ "${{ matrix.mode }}" == *"systemd"* ]]; then
|
||||
echo "SYSTEMD=true" >> $GITHUB_ENV
|
||||
CACHE_DEV_SCOPE="${CACHE_DEV_SCOPE}systemd"
|
||||
fi
|
||||
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=${{ env.CACHE_DEV_SCOPE }}
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make -o build test-integration
|
||||
env:
|
||||
TEST_SKIP_INTEGRATION_CLI: 1
|
||||
TESTCOVERAGE: 1
|
||||
-
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
reportsName=${{ matrix.os }}
|
||||
if [ -n "${{ matrix.mode }}" ]; then
|
||||
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
|
||||
sudo chown -R $(id -u):$(id -g) $reportsPath
|
||||
tree -nh $reportsPath
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles/test-integration
|
||||
env_vars: RUNNER_OS
|
||||
flags: integration,${{ matrix.mode }}
|
||||
-
|
||||
name: Test daemon logs
|
||||
if: always()
|
||||
run: |
|
||||
cat bundles/test-integration/docker.log
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-reports-integration-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-report:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
- integration
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/reports
|
||||
pattern: test-reports-integration-${{ inputs.storage }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
integration-cli-prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
matrix: ${{ steps.tests.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Install gotestlist
|
||||
run:
|
||||
go install github.com/crazy-max/gotestlist/cmd/gotestlist@${{ env.GOTESTLIST_VERSION }}
|
||||
-
|
||||
name: Create matrix
|
||||
id: tests
|
||||
working-directory: ./integration-cli
|
||||
run: |
|
||||
# This step creates a matrix for integration-cli tests. Tests suites
|
||||
# are distributed in integration-cli job through a matrix. There is
|
||||
# also overrides being added to the matrix like "./..." to run
|
||||
# "Test integration" step exclusively and specific tests suites that
|
||||
# take a long time to run.
|
||||
matrix="$(gotestlist -d ${{ env.ITG_CLI_MATRIX_SIZE }} -o "./..." -o "DockerSwarmSuite" -o "DockerNetworkSuite|DockerExternalVolumeSuite" ./...)"
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.tests.outputs.matrix }}
|
||||
|
||||
integration-cli:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- build-dev
|
||||
- integration-cli-prepare
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test: ${{ fromJson(needs.integration-cli-prepare.outputs.matrix) }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up runner
|
||||
uses: ./.github/actions/setup-runner
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build dev image
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: dev
|
||||
set: |
|
||||
dev.cache-from=type=gha,scope=dev
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
make -o build test-integration
|
||||
env:
|
||||
TEST_SKIP_INTEGRATION: 1
|
||||
TESTCOVERAGE: 1
|
||||
TESTFLAGS: "-test.run (${{ matrix.test }})/"
|
||||
-
|
||||
name: Prepare reports
|
||||
if: always()
|
||||
run: |
|
||||
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
|
||||
tar -xzf /tmp/reports.tar.gz -C $reportsPath
|
||||
sudo chown -R $(id -u):$(id -g) $reportsPath
|
||||
tree -nh $reportsPath
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
directory: ./bundles/test-integration
|
||||
env_vars: RUNNER_OS
|
||||
flags: integration-cli
|
||||
-
|
||||
name: Test daemon logs
|
||||
if: always()
|
||||
run: |
|
||||
cat bundles/test-integration/docker.log
|
||||
-
|
||||
name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-reports-integration-cli-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
||||
path: /tmp/reports/*
|
||||
|
||||
integration-cli-report:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: always()
|
||||
needs:
|
||||
- integration-cli
|
||||
steps:
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
-
|
||||
name: Download reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/reports
|
||||
pattern: test-reports-integration-cli-${{ inputs.storage }}-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Install teststat
|
||||
run: |
|
||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
||||
-
|
||||
name: Create summary
|
||||
run: |
|
||||
teststat -markdown $(find /tmp/reports -type f -name '*.json' -print0 | xargs -0) >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
prepare-smoke:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- validate-dco
|
||||
outputs:
|
||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
run: |
|
||||
matrix="$(docker buildx bake binary-smoketest --print | jq -cr '.target."binary-smoketest".platforms')"
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.platforms.outputs.matrix }}
|
||||
|
||||
smoke:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120 # guardrails timeout for the whole job
|
||||
needs:
|
||||
- prepare-smoke
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{ fromJson(needs.prepare-smoke.outputs.matrix) }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@v5
|
||||
with:
|
||||
targets: binary-smoketest
|
||||
set: |
|
||||
*.platform=${{ matrix.platform }}
|
||||
31
.github/workflows/windows-2019.yml
vendored
31
.github/workflows/windows-2019.yml
vendored
@@ -1,31 +0,0 @@
|
||||
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
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-dco:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
run:
|
||||
needs:
|
||||
- validate-dco
|
||||
uses: ./.github/workflows/.windows.yml
|
||||
with:
|
||||
os: windows-2019
|
||||
send_coverage: false
|
||||
34
.github/workflows/windows-2022.yml
vendored
34
.github/workflows/windows-2022.yml
vendored
@@ -1,34 +0,0 @@
|
||||
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
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]+'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validate-dco:
|
||||
uses: ./.github/workflows/.dco.yml
|
||||
|
||||
run:
|
||||
needs:
|
||||
- validate-dco
|
||||
uses: ./.github/workflows/.windows.yml
|
||||
with:
|
||||
os: windows-2022
|
||||
send_coverage: true
|
||||
36
.gitignore
vendored
36
.gitignore
vendored
@@ -1,28 +1,24 @@
|
||||
# If you want to ignore files created by your editor/tools, please consider a
|
||||
# [global .gitignore](https://help.github.com/articles/ignoring-files).
|
||||
|
||||
*~
|
||||
*.bak
|
||||
# Docker project generated files to ignore
|
||||
# if you want to ignore files created by your editor/tools,
|
||||
# please consider a global .gitignore https://help.github.com/articles/ignoring-files
|
||||
*.exe
|
||||
*.exe~
|
||||
*.orig
|
||||
test.main
|
||||
.*.swp
|
||||
.DS_Store
|
||||
thumbs.db
|
||||
|
||||
# local repository customization
|
||||
.envrc
|
||||
# a .bashrc may be added to customize the build environment
|
||||
.bashrc
|
||||
.editorconfig
|
||||
|
||||
# build artifacts
|
||||
.gopath/
|
||||
.go-pkg-cache/
|
||||
autogen/
|
||||
bundles/
|
||||
cli/winresources/*/*.syso
|
||||
cli/winresources/*/winres.json
|
||||
cmd/dockerd/dockerd
|
||||
contrib/builder/rpm/*/changelog
|
||||
|
||||
# ci artifacts
|
||||
*.exe
|
||||
*.gz
|
||||
go-test-report.json
|
||||
junit-report.xml
|
||||
dockerversion/version_autogen.go
|
||||
dockerversion/version_autogen_unix.go
|
||||
vendor/pkg/
|
||||
hack/integration-cli-on-swarm/integration-cli-on-swarm
|
||||
coverage.txt
|
||||
profile.out
|
||||
test.main
|
||||
|
||||
334
.mailmap
334
.mailmap
@@ -1,24 +1,15 @@
|
||||
# This file lists the canonical name and email of contributors, and is used to
|
||||
# generate AUTHORS (in hack/generate-authors.sh).
|
||||
#
|
||||
# To find new duplicates, regenerate AUTHORS and scan for name duplicates, or
|
||||
# run the following to find email duplicates:
|
||||
# git log --format='%aE - %aN' | sort -uf | awk -v IGNORECASE=1 '$1 in a {print a[$1]; print}; {a[$1]=$0}'
|
||||
#
|
||||
# For an explanation of this file format, consult gitmailmap(5).
|
||||
# Generate AUTHORS: hack/generate-authors.sh
|
||||
|
||||
# Tip for finding duplicates (besides scanning the output of AUTHORS for name
|
||||
# duplicates that aren't also email duplicates): scan the output of:
|
||||
# git log --format='%aE - %aN' | sort -uf
|
||||
#
|
||||
# For explanation on this file format: man git-shortlog
|
||||
|
||||
<21551195@zju.edu.cn> <hsinko@users.noreply.github.com>
|
||||
<mr.wrfly@gmail.com> <wrfly@users.noreply.github.com>
|
||||
Aaron L. Xu <liker.xu@foxmail.com>
|
||||
Aaron L. Xu <liker.xu@foxmail.com> <likexu@harmonycloud.cn>
|
||||
Aaron Lehmann <alehmann@netflix.com>
|
||||
Aaron Lehmann <alehmann@netflix.com> <aaron.lehmann@docker.com>
|
||||
Abhinandan Prativadi <aprativadi@gmail.com>
|
||||
Abhinandan Prativadi <aprativadi@gmail.com> <abhi@docker.com>
|
||||
Abhinandan Prativadi <aprativadi@gmail.com> abhi <user.email>
|
||||
Abhishek Chanda <abhishek.becs@gmail.com>
|
||||
Abhishek Chanda <abhishek.becs@gmail.com> <abhishek.chanda@emc.com>
|
||||
Ada Mancini <ada@docker.com>
|
||||
Adam Dobrawy <naczelnik@jawnosc.tk>
|
||||
Adam Dobrawy <naczelnik@jawnosc.tk> <ad-m@users.noreply.github.com>
|
||||
Abhinandan Prativadi <abhi@docker.com>
|
||||
Adrien Gallouët <adrien@gallouet.fr> <angt@users.noreply.github.com>
|
||||
Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||
Ahmet Alp Balkan <ahmetb@microsoft.com> <ahmetalpbalkan@gmail.com>
|
||||
@@ -26,49 +17,27 @@ AJ Bowen <aj@soulshake.net>
|
||||
AJ Bowen <aj@soulshake.net> <aj@gandi.net>
|
||||
AJ Bowen <aj@soulshake.net> <amy@gandi.net>
|
||||
Akihiro Matsushima <amatsusbit@gmail.com> <amatsus@users.noreply.github.com>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp> <suda.akihiro@lab.ntt.co.jp>
|
||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp> <suda.kyoto@gmail.com>
|
||||
Akshay Moghe <akshay.moghe@gmail.com>
|
||||
Albin Kerouanton <albinker@gmail.com>
|
||||
Albin Kerouanton <albinker@gmail.com> <albin@akerouanton.name>
|
||||
Akihiro Suda <suda.akihiro@lab.ntt.co.jp> <suda.kyoto@gmail.com>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksa Sarai <asarai@suse.de> <asarai@suse.com>
|
||||
Aleksa Sarai <asarai@suse.de> <cyphar@cyphar.com>
|
||||
Aleksandrs Fadins <aleks@s-ko.net>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com> <aboch@docker.com>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com> <aboch@socketplane.io>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com> <aboch@users.noreply.github.com>
|
||||
Alex Chan <alex@alexwlchan.net>
|
||||
Alex Chan <alex@alexwlchan.net> <alex.chan@metaswitch.com>
|
||||
Alex Chen <alexchenunix@gmail.com> <root@localhost.localdomain>
|
||||
Alex Ellis <alexellis2@gmail.com>
|
||||
Alex Goodman <wagoodman@gmail.com> <wagoodman@users.noreply.github.com>
|
||||
Alexander Larsson <alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||
Alexander Morozov <lk4d4math@gmail.com>
|
||||
Alexander Morozov <lk4d4math@gmail.com> <lk4d4@docker.com>
|
||||
Alexander Morozov <lk4d4@docker.com>
|
||||
Alexander Morozov <lk4d4@docker.com> <lk4d4math@gmail.com>
|
||||
Alexandre Beslic <alexandre.beslic@gmail.com> <abronan@docker.com>
|
||||
Alexandre González <agonzalezro@gmail.com>
|
||||
Alexis Ries <ries.alexis@gmail.com>
|
||||
Alexis Ries <ries.alexis@gmail.com> <alexis.ries.ext@orange.com>
|
||||
Alexis Thomas <fr.alexisthomas@gmail.com>
|
||||
Alicia Lauerman <alicia@eta.im> <allydevour@me.com>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com> <allen.sun@daocloud.io>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com> <shlallen1990@gmail.com>
|
||||
Anca Iordache <anca.iordache@docker.com>
|
||||
Andrea Denisse Gómez <crypto.andrea@protonmail.ch>
|
||||
Andrew Kim <taeyeonkim90@gmail.com>
|
||||
Andrew Kim <taeyeonkim90@gmail.com> <akim01@fortinet.com>
|
||||
Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@microsoft.com>
|
||||
Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@outlook.com>
|
||||
Andrey Kolomentsev <andrey.kolomentsev@docker.com>
|
||||
Andrey Kolomentsev <andrey.kolomentsev@docker.com> <andrey.kolomentsev@gmail.com>
|
||||
André Martins <aanm90@gmail.com> <martins@noironetworks.com>
|
||||
Andy Rothfusz <github@developersupport.net> <github@metaliveblog.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Andy Zhang <andy.zhangtao@hotmail.com>
|
||||
Andy Zhang <andy.zhangtao@hotmail.com> <ztao@tibco-support.com>
|
||||
Ankush Agarwal <ankushagarwal11@gmail.com> <ankushagarwal@users.noreply.github.com>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <amurdaca@redhat.com>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <me@runcom.ninja>
|
||||
@@ -78,45 +47,28 @@ Antonio Murdaca <antonio.murdaca@gmail.com> <runcom@users.noreply.github.com>
|
||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
|
||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com> <abahuguna@fiberlink.com>
|
||||
Anusha Ragunathan <anusha.ragunathan@docker.com> <anusha@docker.com>
|
||||
Anyu Wang <wanganyu@outlook.com>
|
||||
Arko Dasgupta <arko@tetrate.io>
|
||||
Arko Dasgupta <arko@tetrate.io> <arko.dasgupta@docker.com>
|
||||
Arko Dasgupta <arko@tetrate.io> <arkodg@users.noreply.github.com>
|
||||
Arnaud Porterie <icecrime@gmail.com>
|
||||
Arnaud Porterie <icecrime@gmail.com> <arnaud.porterie@docker.com>
|
||||
Arnaud Rebillout <arnaud.rebillout@collabora.com>
|
||||
Arnaud Rebillout <arnaud.rebillout@collabora.com> <elboulangero@gmail.com>
|
||||
Arnaud Porterie <arnaud.porterie@docker.com>
|
||||
Arnaud Porterie <arnaud.porterie@docker.com> <icecrime@gmail.com>
|
||||
Arthur Gautier <baloo@gandi.net> <superbaloo+registrations.github@superbaloo.net>
|
||||
Artur Meyster <arthurfbi@yahoo.com>
|
||||
Avi Miller <avi.miller@oracle.com> <avi.miller@gmail.com>
|
||||
Ben Bonnefoy <frenchben@docker.com>
|
||||
Ben Golub <ben.golub@dotcloud.com>
|
||||
Ben Toews <mastahyeti@gmail.com> <mastahyeti@users.noreply.github.com>
|
||||
Benny Ng <benny.tpng@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bevisy Zhang <binbin36520@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Bhumika Bayani <bhumikabayani@gmail.com>
|
||||
Bilal Amarni <bilal.amarni@gmail.com> <bamarni@users.noreply.github.com>
|
||||
Bill Wang <ozbillwang@gmail.com> <SydOps@users.noreply.github.com>
|
||||
Bily Zhang <xcoder@tenxcloud.com>
|
||||
Bin Liu <liubin0329@gmail.com>
|
||||
Bin Liu <liubin0329@gmail.com> <liubin0329@users.noreply.github.com>
|
||||
Bingshen Wang <bingshen.wbs@alibaba-inc.com>
|
||||
Boaz Shuster <ripcurld.github@gmail.com>
|
||||
Bojun Zhu <bojun.zhu@foxmail.com>
|
||||
Boqin Qin <bobbqqin@gmail.com>
|
||||
Boshi Lian <farmer1992@gmail.com>
|
||||
Brandon Philips <brandon.philips@coreos.com> <brandon@ifup.co>
|
||||
Brandon Philips <brandon.philips@coreos.com> <brandon@ifup.org>
|
||||
Brent Salisbury <brent.salisbury@docker.com> <brent@docker.com>
|
||||
Brian Goff <cpuguy83@gmail.com>
|
||||
Brian Goff <cpuguy83@gmail.com> <bgoff@cpuguy83-mbp.home>
|
||||
Brian Goff <cpuguy83@gmail.com> <bgoff@cpuguy83-mbp.local>
|
||||
Brian Goff <cpuguy83@gmail.com> <brian.goff@microsoft.com>
|
||||
Brian Goff <cpuguy83@gmail.com> <cpuguy@hey.com>
|
||||
Cameron Sparr <gh@sparr.email>
|
||||
Carlos de Paula <me@carlosedp.com>
|
||||
Chander Govindarajan <chandergovind@gmail.com>
|
||||
Chao Wang <wangchao.fnst@cn.fujitsu.com> <chaowang@localhost.localdomain>
|
||||
Charles Hooper <charles.hooper@dotcloud.com> <chooper@plumata.com>
|
||||
@@ -125,19 +77,12 @@ Chen Chuanliang <chen.chuanliang@zte.com.cn>
|
||||
Chen Mingjie <chenmingjie0828@163.com>
|
||||
Chen Qiu <cheney-90@hotmail.com>
|
||||
Chen Qiu <cheney-90@hotmail.com> <21321229@zju.edu.cn>
|
||||
Chengfei Shang <cfshang@alauda.io>
|
||||
Chris Dias <cdias@microsoft.com>
|
||||
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Chris Price <cprice@mirantis.com>
|
||||
Chris Price <cprice@mirantis.com> <chris.price@docker.com>
|
||||
Chris Telfer <ctelfer@docker.com>
|
||||
Chris Telfer <ctelfer@docker.com> <ctelfer@users.noreply.github.com>
|
||||
Christopher Biscardi <biscarch@sketcht.com>
|
||||
Christopher Latham <sudosurootdev@gmail.com>
|
||||
Christy Norman <christy@linux.vnet.ibm.com>
|
||||
Chun Chen <ramichen@tencent.com> <chenchun.feed@gmail.com>
|
||||
Corbin Coleman <corbin.coleman@docker.com>
|
||||
Cristian Ariza <dev@cristianrz.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com> <unclejack@users.noreply.github.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com> <unclejacksons@gmail.com>
|
||||
@@ -152,7 +97,6 @@ Daniel Garcia <daniel@danielgarcia.info>
|
||||
Daniel Gasienica <daniel@gasienica.ch> <dgasienica@zynga.com>
|
||||
Daniel Goosen <daniel.goosen@surveysampling.com> <djgoosen@users.noreply.github.com>
|
||||
Daniel Grunwell <mwgrunny@gmail.com>
|
||||
Daniel Hiltgen <daniel.hiltgen@docker.com> <dhiltgen@users.noreply.github.com>
|
||||
Daniel J Walsh <dwalsh@redhat.com>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
|
||||
@@ -160,7 +104,6 @@ Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <root@vagrant-ubuntu-12.10.vagr
|
||||
Daniel Nephin <dnephin@docker.com> <dnephin@gmail.com>
|
||||
Daniel Norberg <dano@spotify.com> <daniel.norberg@gmail.com>
|
||||
Daniel Watkins <daniel@daniel-watkins.co.uk>
|
||||
Daniel Zhang <jmzwcn@gmail.com>
|
||||
Danny Yates <danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
|
||||
Darren Shepherd <darren.s.shepherd@gmail.com> <darren@rancher.com>
|
||||
Dattatraya Kumbhar <dattatraya.kumbhar@gslab.com>
|
||||
@@ -171,29 +114,16 @@ David M. Karr <davidmichaelkarr@gmail.com>
|
||||
David Sheets <dsheets@docker.com> <sheets@alum.mit.edu>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
David Williamson <david.williamson@docker.com> <davidwilliamson@users.noreply.github.com>
|
||||
Derek Ch <denc716@gmail.com>
|
||||
Derek McGowan <derek@mcg.dev>
|
||||
Derek McGowan <derek@mcg.dev> <derek@mcgstyle.net>
|
||||
Deshi Xiao <dxiao@redhat.com> <dsxiao@dataman-inc.com>
|
||||
Deshi Xiao <dxiao@redhat.com> <xiaods@gmail.com>
|
||||
Dhilip Kumars <dhilip.kumar.s@huawei.com>
|
||||
Diego Siqueira <dieg0@live.com>
|
||||
Diogo Monica <diogo@docker.com> <diogo.monica@gmail.com>
|
||||
Dmitry Sharshakov <d3dx12.xx@gmail.com>
|
||||
Dmitry Sharshakov <d3dx12.xx@gmail.com> <sh7dm@outlook.com>
|
||||
Dmytro Iakovliev <dmytro.iakovliev@zodiacsystems.com>
|
||||
Dominic Yin <yindongchao@inspur.com>
|
||||
Dominik Honnef <dominik@honnef.co> <dominikh@fork-bomb.org>
|
||||
Doug Davis <dug@us.ibm.com> <duglin@users.noreply.github.com>
|
||||
Doug Tangren <d.tangren@gmail.com>
|
||||
Drew Erny <derny@mirantis.com>
|
||||
Drew Erny <derny@mirantis.com> <drew.erny@docker.com>
|
||||
Elan Ruusamäe <glen@pld-linux.org>
|
||||
Elan Ruusamäe <glen@pld-linux.org> <glen@delfi.ee>
|
||||
Elango Sivanandam <elango.siva@docker.com>
|
||||
Elango Sivanandam <elango.siva@docker.com> <elango@docker.com>
|
||||
Eli Uriegas <seemethere101@gmail.com>
|
||||
Eli Uriegas <seemethere101@gmail.com> <eli.uriegas@docker.com>
|
||||
Eric G. Noriega <enoriega@vizuri.com> <egnoriega@users.noreply.github.com>
|
||||
Eric Hanchrow <ehanchrow@ine.com> <eric.hanchrow@gmail.com>
|
||||
Eric Rosenberg <ehaydenr@gmail.com> <ehaydenr@users.noreply.github.com>
|
||||
@@ -215,104 +145,64 @@ Feng Yan <fy2462@gmail.com>
|
||||
Fengtu Wang <wangfengtu@huawei.com> <wangfengtu@huawei.com>
|
||||
Francisco Carriedo <fcarriedo@gmail.com>
|
||||
Frank Rosquin <frank.rosquin+github@gmail.com> <frank.rosquin@gmail.com>
|
||||
Frank Yang <yyb196@gmail.com>
|
||||
Frederick F. Kautz IV <fkautz@redhat.com> <fkautz@alumni.cmu.edu>
|
||||
Fu JinLin <withlin@yeah.net>
|
||||
Gabriel Goller <gabrielgoller123@gmail.com>
|
||||
Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com>
|
||||
Gaetan de Villele <gdevillele@gmail.com>
|
||||
Gang Qiao <qiaohai8866@gmail.com> <1373319223@qq.com>
|
||||
Geon Kim <geon0250@gmail.com>
|
||||
George Kontridze <george@bugsnag.com>
|
||||
Gerwim Feiken <g.feiken@tfe.nl> <gerwim@gmail.com>
|
||||
Giampaolo Mancini <giampaolo@trampolineup.com>
|
||||
Giovan Isa Musthofa <giovanism@outlook.co.id>
|
||||
Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
|
||||
Gou Rao <gou@portworx.com> <gourao@users.noreply.github.com>
|
||||
Grant Millar <rid@cylo.io>
|
||||
Grant Millar <rid@cylo.io> <grant@cylo.io>
|
||||
Grant Millar <rid@cylo.io> <grant@seednet.eu>
|
||||
Greg Stephens <greg@udon.org>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <charmes.guillaume@gmail.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <guillaume.charmes@dotcloud.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <guillaume@charmes.net>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <guillaume@docker.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <guillaume@dotcloud.com>
|
||||
Gunadhya S. <6939749+gunadhya@users.noreply.github.com>
|
||||
Guoqiang QI <guoqiang.qi1@gmail.com>
|
||||
Guri <odg0318@gmail.com>
|
||||
Gurjeet Singh <gurjeet@singh.im> <singh.gurjeet@gmail.com>
|
||||
Gustav Sinder <gustav.sinder@gmail.com>
|
||||
Günther Jungbluth <gunther@gameslabs.net>
|
||||
Hakan Özler <hakan.ozler@kodcu.com>
|
||||
Hao Shu Wei <haoshuwei24@gmail.com>
|
||||
Hao Shu Wei <haoshuwei24@gmail.com> <haoshuwei1989@163.com>
|
||||
Hao Shu Wei <haoshuwei24@gmail.com> <haosw@cn.ibm.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com> <haoshuwei1989@163.com>
|
||||
Harald Albers <github@albersweb.de> <albers@users.noreply.github.com>
|
||||
Harald Niesche <harald@niesche.de>
|
||||
Harold Cooper <hrldcpr@gmail.com>
|
||||
Harry Zhang <harryz@hyper.sh>
|
||||
Harry Zhang <harryz@hyper.sh> <harryzhang@zju.edu.cn>
|
||||
Harry Zhang <harryz@hyper.sh> <resouer@163.com>
|
||||
Harry Zhang <harryz@hyper.sh> <resouer@gmail.com>
|
||||
Harry Zhang <resouer@163.com>
|
||||
Harshal Patil <harshal.patil@in.ibm.com> <harche@users.noreply.github.com>
|
||||
He Simei <hesimei@zju.edu.cn>
|
||||
Helen Xie <chenjg@harmonycloud.cn>
|
||||
Hiroyuki Sasagawa <hs19870702@gmail.com>
|
||||
Hollie Teal <hollie@docker.com>
|
||||
Hollie Teal <hollie@docker.com> <hollie.teal@docker.com>
|
||||
Hollie Teal <hollie@docker.com> <hollietealok@users.noreply.github.com>
|
||||
hsinko <21551195@zju.edu.cn> <hsinko@users.noreply.github.com>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hui Kang <hkang.sunysb@gmail.com>
|
||||
Hui Kang <hkang.sunysb@gmail.com> <kangh@us.ibm.com>
|
||||
Huu Nguyen <huu@prismskylabs.com> <whoshuu@gmail.com>
|
||||
Hyeongkyu Lee <hyeongkyu.lee@navercorp.com>
|
||||
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com>
|
||||
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com> <1187766782@qq.com>
|
||||
Ian Campbell <ian.campbell@docker.com>
|
||||
Ian Campbell <ian.campbell@docker.com> <ijc@docker.com>
|
||||
Ilya Khlopotov <ilya.khlopotov@gmail.com>
|
||||
Iskander Sharipov <quasilyte@gmail.com>
|
||||
Ivan Babrou <ibobrik@gmail.com>
|
||||
Ivan Markin <sw@nogoegst.net> <twim@riseup.net>
|
||||
Jack Laxson <jackjrabbit@gmail.com>
|
||||
Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com>
|
||||
Jacob Tomlinson <jacob@tom.linson.uk> <jacobtomlinson@users.noreply.github.com>
|
||||
Jaivish Kothari <janonymous.codevulture@gmail.com>
|
||||
Jake Moshenko <jake@devtable.com>
|
||||
Jakub Drahos <jdrahos@pulsepoint.com>
|
||||
Jakub Drahos <jdrahos@pulsepoint.com> <jack.drahos@gmail.com>
|
||||
James Nesbitt <jnesbitt@mirantis.com>
|
||||
James Nesbitt <jnesbitt@mirantis.com> <james.nesbitt@wunderkraut.com>
|
||||
Jamie Hannaford <jamie@limetree.org> <jamie.hannaford@rackspace.com>
|
||||
Jan Götte <jaseg@jaseg.net>
|
||||
Jana Radhakrishnan <mrjana@docker.com>
|
||||
Jana Radhakrishnan <mrjana@docker.com> <mrjana@socketplane.io>
|
||||
Javier Bassi <javierbassi@gmail.com>
|
||||
Javier Bassi <javierbassi@gmail.com> <CrimsonGlory@users.noreply.github.com>
|
||||
Jay Lim <jay@imjching.com>
|
||||
Jay Lim <jay@imjching.com> <imjching@hotmail.com>
|
||||
Jean Rouge <rougej+github@gmail.com> <jer329@cornell.edu>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||
Jean-Tiare Le Bigot <jt@yadutaf.fr> <admin@jtlebi.fr>
|
||||
Jeff Anderson <jeff@docker.com> <jefferya@programmerq.net>
|
||||
Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com>
|
||||
Jeroen Franse <jeroenfranse@gmail.com>
|
||||
Jessica Frazelle <jess@oxide.computer>
|
||||
Jessica Frazelle <jess@oxide.computer> <acidburn@docker.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <acidburn@google.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <acidburn@microsoft.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <jess@docker.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <jess@mesosphere.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <jessfraz@google.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <jfrazelle@users.noreply.github.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <me@jessfraz.com>
|
||||
Jessica Frazelle <jess@oxide.computer> <princess@docker.com>
|
||||
Jian Liao <jliao@alauda.io>
|
||||
Jiang Jinyang <jjyruby@gmail.com>
|
||||
Jiang Jinyang <jjyruby@gmail.com> <jiangjinyang@outlook.com>
|
||||
Jessica Frazelle <jessfraz@google.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <acidburn@docker.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <acidburn@google.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <jess@docker.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <jess@mesosphere.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <jfrazelle@users.noreply.github.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <me@jessfraz.com>
|
||||
Jessica Frazelle <jessfraz@google.com> <princess@docker.com>
|
||||
Jim Galasyn <jim.galasyn@docker.com>
|
||||
Jiuyue Ma <majiuyue@huawei.com>
|
||||
Joey Geiger <jgeiger@gmail.com>
|
||||
@@ -321,23 +211,19 @@ Joffrey F <joffrey@docker.com> <f.joffrey@gmail.com>
|
||||
Joffrey F <joffrey@docker.com> <joffrey@dotcloud.com>
|
||||
Johan Euphrosine <proppy@google.com> <proppy@aminche.com>
|
||||
John Harris <john@johnharris.io>
|
||||
John Howard <github@lowenna.com>
|
||||
John Howard <github@lowenna.com> <10522484+lowenna@users.noreply.github.com>
|
||||
John Howard <github@lowenna.com> <jhoward@microsoft.com>
|
||||
John Howard <github@lowenna.com> <jhoward@ntdev.microsoft.com>
|
||||
John Howard <github@lowenna.com> <jhowardmsft@users.noreply.github.com>
|
||||
John Howard <github@lowenna.com> <john.howard@microsoft.com>
|
||||
John Howard <github@lowenna.com> <john@lowenna.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhoward@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhoward@ntdev.microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhowardmsft@users.noreply.github.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <john.howard@microsoft.com>
|
||||
John Stephens <johnstep@docker.com> <johnstep@users.noreply.github.com>
|
||||
Jon Surrell <jon.surrell@gmail.com> <jon.surrell@automattic.com>
|
||||
Jonathan Choy <jonathan.j.choy@gmail.com>
|
||||
Jonathan Choy <jonathan.j.choy@gmail.com> <oni@tetsujinlabs.com>
|
||||
Jon Surrell <jon.surrell@gmail.com> <jon.surrell@automattic.com>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Jordan Jennings <jjn2009@gmail.com> <jjn2009@users.noreply.github.com>
|
||||
Jorit Kleine-Möllhoff <joppich@bricknet.de> <joppich@users.noreply.github.com>
|
||||
Jose Diaz-Gonzalez <email@josediazgonzalez.com>
|
||||
Jose Diaz-Gonzalez <email@josediazgonzalez.com> <jose@seatgeek.com>
|
||||
Jose Diaz-Gonzalez <email@josediazgonzalez.com> <josegonzalez@users.noreply.github.com>
|
||||
Jose Diaz-Gonzalez <jose@seatgeek.com> <josegonzalez@users.noreply.github.com>
|
||||
Josh Bonczkowski <josh.bonczkowski@gmail.com>
|
||||
Josh Eveleth <joshe@opendns.com> <jeveleth@users.noreply.github.com>
|
||||
Josh Hawn <josh.hawn@docker.com> <jlhawn@berkeley.edu>
|
||||
@@ -347,14 +233,10 @@ Josh Wilson <josh.wilson@fivestars.com> <jcwilson@users.noreply.github.com>
|
||||
Joyce Jang <mail@joycejang.com>
|
||||
Julien Bordellier <julienbordellier@gmail.com> <git@julienbordellier.com>
|
||||
Julien Bordellier <julienbordellier@gmail.com> <me@julienbordellier.com>
|
||||
Jun Du <dujun5@huawei.com>
|
||||
Justin Cormack <justin.cormack@docker.com>
|
||||
Justin Cormack <justin.cormack@docker.com> <justin.cormack@unikernel.com>
|
||||
Justin Cormack <justin.cormack@docker.com> <justin@specialbusservice.com>
|
||||
Justin Keller <85903732+jk-vb@users.noreply.github.com>
|
||||
Justin Keller <85903732+jk-vb@users.noreply.github.com> <jkeller@vb-jkeller-mbp.local>
|
||||
Justin Simonelis <justin.p.simonelis@gmail.com> <justin.simonelis@PTS-JSIMON2.toronto.exclamation.com>
|
||||
Justin Terry <juterry@microsoft.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@dotcloud.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@gmail.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jp@enix.org>
|
||||
@@ -363,13 +245,9 @@ Kai Qiang Wu (Kennan) <wkq5325@gmail.com>
|
||||
Kai Qiang Wu (Kennan) <wkq5325@gmail.com> <wkqwu@cn.ibm.com>
|
||||
Kamil Domański <kamil@domanski.co>
|
||||
Kamjar Gerami <kami.gerami@gmail.com>
|
||||
Karthik Nayak <karthik.188@gmail.com>
|
||||
Karthik Nayak <karthik.188@gmail.com> <Karthik.188@gmail.com>
|
||||
Ken Cochrane <kencochrane@gmail.com> <KenCochrane@gmail.com>
|
||||
Ken Herner <kherner@progress.com> <chosenken@gmail.com>
|
||||
Ken Reese <krrgithub@gmail.com>
|
||||
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
|
||||
Kevin Alvarez <crazy-max@users.noreply.github.com>
|
||||
Kevin Feyrer <kevin.feyrer@btinternet.com> <kevinfeyrer@users.noreply.github.com>
|
||||
Kevin Kern <kaiwentan@harmonycloud.cn>
|
||||
Kevin Meredith <kevin.m.meredith@gmail.com>
|
||||
@@ -380,25 +258,17 @@ Konrad Kleine <konrad.wilhelm.kleine@gmail.com> <kwk@users.noreply.github.com>
|
||||
Konstantin Gribov <grossws@gmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
Kotaro Yoshimatsu <kotaro.yoshimatsu@gmail.com>
|
||||
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
|
||||
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> <btkushuwahak@KUNAL-PC.swh.swh.nttdata.co.jp>
|
||||
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> <kunal.kushwaha@gmail.com>
|
||||
Kyle Squizzato <ksquizz@gmail.com>
|
||||
Kyle Squizzato <ksquizz@gmail.com> <kyle.squizzato@docker.com>
|
||||
Lajos Papp <lajos.papp@sequenceiq.com> <lalyos@yahoo.com>
|
||||
Lei Gong <lgong@alauda.io>
|
||||
Lei Jitang <leijitang@huawei.com>
|
||||
Lei Jitang <leijitang@huawei.com> <leijitang@gmail.com>
|
||||
Lei Jitang <leijitang@huawei.com> <leijitang@outlook.com>
|
||||
Leiiwang <u2takey@gmail.com>
|
||||
Liang Mingqiang <mqliang.zju@gmail.com>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Liao Qingwei <liaoqingwei@huawei.com>
|
||||
Linus Heckemann <lheckemann@twig-world.com>
|
||||
Linus Heckemann <lheckemann@twig-world.com> <anonymouse2048@gmail.com>
|
||||
Lokesh Mandvekar <lsm5@fedoraproject.org> <lsm5@redhat.com>
|
||||
Lorenzo Fontana <fontanalorenz@gmail.com> <fontanalorenzo@me.com>
|
||||
Lorenzo Fontana <fontanalorenz@gmail.com> <lo@linux.com>
|
||||
Lorenzo Fontana <lo@linux.com> <fontanalorenzo@me.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Louis Opter <kalessin@kalessin.fr> <louis@dotcloud.com>
|
||||
Luca Favatella <luca.favatella@erlang-solutions.com> <lucafavatella@users.noreply.github.com>
|
||||
@@ -407,11 +277,8 @@ Lyn <energylyn@zju.edu.cn>
|
||||
Lynda O'Leary <lyndaoleary29@gmail.com>
|
||||
Lynda O'Leary <lyndaoleary29@gmail.com> <lyndaoleary@hotmail.com>
|
||||
Ma Müller <mueller-ma@users.noreply.github.com>
|
||||
Madhan Raj Mookkandy <MadhanRaj.Mookkandy@microsoft.com>
|
||||
Madhan Raj Mookkandy <MadhanRaj.Mookkandy@microsoft.com> <madhanm@corp.microsoft.com>
|
||||
Madhan Raj Mookkandy <MadhanRaj.Mookkandy@microsoft.com> <madhanm@microsoft.com>
|
||||
Madhu Venugopal <mavenugo@gmail.com> <madhu@docker.com>
|
||||
Madhu Venugopal <mavenugo@gmail.com> <madhu@socketplane.io>
|
||||
Madhu Venugopal <madhu@socketplane.io> <madhu@docker.com>
|
||||
Mageee <fangpuyi@foxmail.com> <21521230.zju.edu.cn>
|
||||
Mansi Nahar <mmn4185@rit.edu> <mansi.nahar@macbookpro-mansinahar.local>
|
||||
Mansi Nahar <mmn4185@rit.edu> <mansinahar@users.noreply.github.com>
|
||||
@@ -424,12 +291,10 @@ Markan Patel <mpatel678@gmail.com>
|
||||
Markus Kortlang <hyp3rdino@googlemail.com> <markus.kortlang@lhsystems.com>
|
||||
Martin Redmond <redmond.martin@gmail.com> <martin@tinychat.com>
|
||||
Martin Redmond <redmond.martin@gmail.com> <xgithub@redmond5.com>
|
||||
Maru Newby <mnewby@thesprawl.net>
|
||||
Mary Anthony <mary.anthony@docker.com> <mary@docker.com>
|
||||
Mary Anthony <mary.anthony@docker.com> <moxieandmore@gmail.com>
|
||||
Mary Anthony <mary.anthony@docker.com> moxiegirl <mary@docker.com>
|
||||
Masato Ohba <over.rye@gmail.com>
|
||||
Mathieu Paturel <mathieu.paturel@gmail.com>
|
||||
Matt Bentley <matt.bentley@docker.com> <mbentley@mbentley.net>
|
||||
Matt Schurenko <matt.schurenko@gmail.com>
|
||||
Matt Williams <mattyw@me.com>
|
||||
@@ -439,47 +304,29 @@ Matthew Mosesohn <raytrac3r@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Matthias Kühnle <git.nivoc@neverbox.com> <kuehnle@online.de>
|
||||
Mauricio Garavaglia <mauricio@medallia.com> <mauriciogaravaglia@gmail.com>
|
||||
Maxwell <csuhp007@gmail.com>
|
||||
Maxwell <csuhp007@gmail.com> <csuhqg@foxmail.com>
|
||||
Menghui Chen <menghui.chen@alibaba-inc.com>
|
||||
Michael Beskin <mrbeskin@gmail.com>
|
||||
Michael Crosby <crosbymichael@gmail.com>
|
||||
Michael Crosby <crosbymichael@gmail.com> <crosby.michael@gmail.com>
|
||||
Michael Crosby <crosbymichael@gmail.com> <michael@crosbymichael.com>
|
||||
Michael Crosby <crosbymichael@gmail.com> <michael@docker.com>
|
||||
Michael Crosby <crosbymichael@gmail.com> <michael@thepasture.io>
|
||||
Michael Crosby <michael@docker.com> <crosby.michael@gmail.com>
|
||||
Michael Crosby <michael@docker.com> <crosbymichael@gmail.com>
|
||||
Michael Crosby <michael@docker.com> <michael@crosbymichael.com>
|
||||
Michał Gryko <github@odkurzacz.org>
|
||||
Michael Hudson-Doyle <michael.hudson@canonical.com> <michael.hudson@linaro.org>
|
||||
Michael Huettermann <michael@huettermann.net>
|
||||
Michael Käufl <docker@c.michael-kaeufl.de> <michael-k@users.noreply.github.com>
|
||||
Michael Nussbaum <michael.nussbaum@getbraintree.com>
|
||||
Michael Nussbaum <michael.nussbaum@getbraintree.com> <code@getbraintree.com>
|
||||
Michael Spetsiotis <michael_spets@hotmail.com>
|
||||
Michael Stapelberg <michael+gh@stapelberg.de>
|
||||
Michael Stapelberg <michael+gh@stapelberg.de> <stapelberg@google.com>
|
||||
Michal Kostrzewa <michal.kostrzewa@codilime.com>
|
||||
Michal Kostrzewa <michal.kostrzewa@codilime.com> <kostrzewa.michal@o2.pl>
|
||||
Michal Minář <miminar@redhat.com>
|
||||
Michał Gryko <github@odkurzacz.org>
|
||||
Michiel de Jong <michiel@unhosted.org>
|
||||
Mickaël Fortunato <morsi.morsicus@gmail.com>
|
||||
Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com> <30386061+doncicuto@users.noreply.github.com>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Mihai Borobocea <MihaiBorob@gmail.com> <MihaiBorobocea@gmail.com>
|
||||
Mikael Davranche <mikael.davranche@corp.ovh.com>
|
||||
Mikael Davranche <mikael.davranche@corp.ovh.com> <mikael.davranche@corp.ovh.net>
|
||||
Mike Casas <mkcsas0@gmail.com> <mikecasas@users.noreply.github.com>
|
||||
Mike Goelzer <mike.goelzer@docker.com> <mgoelzer@docker.com>
|
||||
Milind Chawre <milindchawre@gmail.com>
|
||||
Misty Stanley-Jones <misty@docker.com> <misty@apache.org>
|
||||
Mohammad Banikazemi <MBanikazemi@gmail.com>
|
||||
Mohammad Banikazemi <MBanikazemi@gmail.com> <mb@us.ibm.com>
|
||||
Mohit Soni <mosoni@ebay.com> <mohitsoni1989@gmail.com>
|
||||
Moorthy RS <rsmoorthy@gmail.com> <rsmoorthy@users.noreply.github.com>
|
||||
Moysés Borges <moysesb@gmail.com>
|
||||
Moysés Borges <moysesb@gmail.com> <moyses.furtado@wplex.com.br>
|
||||
mrfly <mr.wrfly@gmail.com> <wrfly@users.noreply.github.com>
|
||||
Nace Oroz <orkica@gmail.com>
|
||||
Natasha Jarus <linuxmercedes@gmail.com>
|
||||
Nathan LeClaire <nathan.leclaire@docker.com> <nathan.leclaire@gmail.com>
|
||||
Nathan LeClaire <nathan.leclaire@docker.com> <nathanleclaire@gmail.com>
|
||||
Neil Horman <nhorman@tuxdriver.com> <nhorman@hmswarspite.think-freely.org>
|
||||
@@ -491,11 +338,6 @@ Nolan Darilek <nolan@thewordnerd.info>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
O.S. Tezer <ostezer@gmail.com> <ostezer@users.noreply.github.com>
|
||||
Oh Jinkyun <tintypemolly@gmail.com> <tintypemolly@Ohui-MacBook-Pro.local>
|
||||
Oliver Reason <oli@overrateddev.co>
|
||||
Olli Janatuinen <olli.janatuinen@gmail.com>
|
||||
Olli Janatuinen <olli.janatuinen@gmail.com> <olljanat@users.noreply.github.com>
|
||||
Onur Filiz <onur.filiz@microsoft.com>
|
||||
Onur Filiz <onur.filiz@microsoft.com> <ofiliz@users.noreply.github.com>
|
||||
Ouyang Liduo <oyld0210@163.com>
|
||||
Patrick Stapleton <github@gdi2290.com>
|
||||
Paul Liljenberg <liljenberg.paul@gmail.com> <letters@paulnotcom.se>
|
||||
@@ -506,61 +348,38 @@ Peter Dave Hello <hsu@peterdavehello.org> <PeterDaveHello@users.noreply.github.c
|
||||
Peter Jaffe <pjaffe@nevo.com>
|
||||
Peter Nagy <xificurC@gmail.com> <pnagy@gratex.com>
|
||||
Peter Waller <p@pwaller.net> <peter@scraperwiki.com>
|
||||
Phil Estes <estesp@gmail.com>
|
||||
Phil Estes <estesp@gmail.com> <estesp@amazon.com>
|
||||
Phil Estes <estesp@gmail.com> <estesp@linux.vnet.ibm.com>
|
||||
Phil Estes <estesp@linux.vnet.ibm.com> <estesp@gmail.com>
|
||||
Philip Alexander Etling <paetling@gmail.com>
|
||||
Philipp Gillé <philipp.gille@gmail.com> <philippgille@users.noreply.github.com>
|
||||
Prasanna Gautam <prasannagautam@gmail.com>
|
||||
Puneet Pruthi <puneet.pruthi@oracle.com>
|
||||
Puneet Pruthi <puneet.pruthi@oracle.com> <puneetpruthi@gmail.com>
|
||||
Qiang Huang <h.huangqiang@huawei.com>
|
||||
Qiang Huang <h.huangqiang@huawei.com> <qhuang@10.0.2.15>
|
||||
Qin TianHuan <tianhuan@bingotree.cn>
|
||||
Ray Tsang <rayt@google.com> <saturnism@users.noreply.github.com>
|
||||
Renaud Gaubert <rgaubert@nvidia.com> <renaud.gaubert@gmail.com>
|
||||
Richard Scothern <richard.scothern@gmail.com>
|
||||
Robert Terhaar <rterhaar@atlanticdynamic.com> <robbyt@users.noreply.github.com>
|
||||
Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com>
|
||||
Roberto Muñoz Fernández <robertomf@gmail.com> <roberto.munoz.fernandez.contractor@bbva.com>
|
||||
Robin Thoni <robin@rthoni.com>
|
||||
Roman Dudin <katrmr@gmail.com> <decadent@users.noreply.github.com>
|
||||
Rong Zhang <rongzhang@alauda.io>
|
||||
Rongxiang Song <tinysong1226@gmail.com>
|
||||
Rony Weng <ronyweng@synology.com>
|
||||
Ross Boucher <rboucher@gmail.com>
|
||||
Rui Cao <ruicao@alauda.io>
|
||||
Runshen Zhu <runshen.zhu@gmail.com>
|
||||
Ryan Stelly <ryan.stelly@live.com>
|
||||
Ryoga Saito <contact@proelbtn.com>
|
||||
Ryoga Saito <contact@proelbtn.com> <proelbtn@users.noreply.github.com>
|
||||
Sainath Grandhi <sainath.grandhi@intel.com>
|
||||
Sainath Grandhi <sainath.grandhi@intel.com> <saiallforums@gmail.com>
|
||||
Sakeven Jiang <jc5930@sina.cn>
|
||||
Samuel Karp <me@samuelkarp.com> <skarp@amazon.com>
|
||||
Sandeep Bansal <sabansal@microsoft.com>
|
||||
Sandeep Bansal <sabansal@microsoft.com> <msabansal@microsoft.com>
|
||||
Santhosh Manohar <santhosh@docker.com>
|
||||
Sargun Dhillon <sargun@netflix.com> <sargun@sargun.me>
|
||||
Satoshi Tagomori <tagomoris@gmail.com>
|
||||
Sean Lee <seanlee@tw.ibm.com> <scaleoutsean@users.noreply.github.com>
|
||||
Sebastiaan van Stijn <github@gone.nl>
|
||||
Sebastiaan van Stijn <github@gone.nl> <moby@example.com>
|
||||
Sebastiaan van Stijn <github@gone.nl> <sebastiaan@ws-key-sebas3.dpi1.dpi>
|
||||
Sebastiaan van Stijn <github@gone.nl> <thaJeztah@users.noreply.github.com>
|
||||
Seongyeol Lim <seongyeol37@gmail.com>
|
||||
Shaun Kaasten <shaunk@gmail.com>
|
||||
Shawn Landden <shawn@churchofgit.com> <shawnlandden@gmail.com>
|
||||
Shengbo Song <thomassong@tencent.com>
|
||||
Shengbo Song <thomassong@tencent.com> <mymneo@163.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Shishir Mahajan <shishir.mahajan@redhat.com> <smahajan@redhat.com>
|
||||
Shu-Wai Chow <shu-wai.chow@seattlechildrens.org>
|
||||
Shukui Yang <yangshukui@huawei.com>
|
||||
Shuwei Hao <haosw@cn.ibm.com>
|
||||
Shuwei Hao <haosw@cn.ibm.com> <haoshuwei24@gmail.com>
|
||||
Sidhartha Mani <sidharthamn@gmail.com>
|
||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl> <sjoerd@byte.nl>
|
||||
Smark Meng <smark@freecoop.net>
|
||||
Smark Meng <smark@freecoop.net> <smarkm@users.noreply.github.com>
|
||||
Solomon Hykes <solomon@docker.com> <s@docker.com>
|
||||
Solomon Hykes <solomon@docker.com> <solomon.hykes@dotcloud.com>
|
||||
Solomon Hykes <solomon@docker.com> <solomon@dotcloud.com>
|
||||
@@ -574,12 +393,9 @@ Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
Stefan Berger <stefanb@linux.vnet.ibm.com> <stefanb@us.ibm.com>
|
||||
Stefan J. Wernli <swernli@microsoft.com> <swernli@ntdev.microsoft.com>
|
||||
Stefan S. <tronicum@user.github.com>
|
||||
Stefan Scherer <stefan.scherer@docker.com>
|
||||
Stefan Scherer <stefan.scherer@docker.com> <scherer_stefan@icloud.com>
|
||||
Stephan Spindler <shutefan@gmail.com> <shutefan@users.noreply.github.com>
|
||||
Stephen Day <stevvooe@gmail.com>
|
||||
Stephen Day <stevvooe@gmail.com> <stephen.day@docker.com>
|
||||
Stephen Day <stevvooe@gmail.com> <stevvooe@users.noreply.github.com>
|
||||
Stephen Day <stephen.day@docker.com>
|
||||
Stephen Day <stephen.day@docker.com> <stevvooe@users.noreply.github.com>
|
||||
Steve Desmond <steve@vtsv.ca> <stevedesmond-ca@users.noreply.github.com>
|
||||
Sun Gengze <690388648@qq.com>
|
||||
Sun Jianbo <wonderflow.sun@gmail.com>
|
||||
@@ -591,52 +407,31 @@ Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@fosiki.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@home.org.au>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@users.noreply.github.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <¨SvenDowideit@home.org.au¨>
|
||||
Sylvain Baubeau <lebauce@gmail.com>
|
||||
Sylvain Baubeau <lebauce@gmail.com> <sbaubeau@redhat.com>
|
||||
Sylvain Bellemare <sylvain@ascribe.io>
|
||||
Sylvain Bellemare <sylvain@ascribe.io> <sylvain.bellemare@ezeep.com>
|
||||
Takuto Sato <tockn.jp@gmail.com>
|
||||
Tangi Colin <tangicolin@gmail.com>
|
||||
Tejesh Mehta <tejesh.mehta@gmail.com> <tj@init.me>
|
||||
Terry Chu <zue.hterry@gmail.com>
|
||||
Terry Chu <zue.hterry@gmail.com> <jubosh.tw@gmail.com>
|
||||
Thatcher Peskens <thatcher@docker.com>
|
||||
Thatcher Peskens <thatcher@docker.com> <thatcher@dotcloud.com>
|
||||
Thatcher Peskens <thatcher@docker.com> <thatcher@gmx.net>
|
||||
Thiago Alves Silva <thiago.alves@aurea.com>
|
||||
Thiago Alves Silva <thiago.alves@aurea.com> <thiagoalves@users.noreply.github.com>
|
||||
Thomas Gazagnaire <thomas@gazagnaire.org> <thomas@gazagnaire.com>
|
||||
Thomas Ledos <thomas.ledos92@gmail.com>
|
||||
Thomas Léveil <thomasleveil@gmail.com>
|
||||
Thomas Léveil <thomasleveil@gmail.com> <thomasleveil@users.noreply.github.com>
|
||||
Tibor Vass <teabee89@gmail.com> <tibor@docker.com>
|
||||
Tibor Vass <teabee89@gmail.com> <tiborvass@users.noreply.github.com>
|
||||
Till Claassen <pixelistik@users.noreply.github.com>
|
||||
Tim Bart <tim@fewagainstmany.com>
|
||||
Tim Bosse <taim@bosboot.org> <maztaim@users.noreply.github.com>
|
||||
Tim Potter <tpot@hpe.com>
|
||||
Tim Potter <tpot@hpe.com> <tpot@Tims-MacBook-Pro.local>
|
||||
Tim Ruffles <oi@truffles.me.uk> <timruffles@googlemail.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
Tim Wagner <tim.wagner@freenet.ag>
|
||||
Tim Wagner <tim.wagner@freenet.ag> <33624860+herrwagner@users.noreply.github.com>
|
||||
Tim Zju <21651152@zju.edu.cn>
|
||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||
Toli Kuznets <toli@docker.com>
|
||||
Tom Barlow <tomwbarlow@gmail.com>
|
||||
Tom Denham <tom@tomdee.co.uk>
|
||||
Tom Denham <tom@tomdee.co.uk> <tom.denham@metaswitch.com>
|
||||
Tom Sweeney <tsweeney@redhat.com>
|
||||
Tom Wilkie <tom.wilkie@gmail.com>
|
||||
Tom Wilkie <tom.wilkie@gmail.com> <tom@weave.works>
|
||||
Tõnis Tiigi <tonistiigi@gmail.com>
|
||||
Trace Andreason <tandreason@gmail.com>
|
||||
Trapier Marshall <tmarshall@mirantis.com>
|
||||
Trapier Marshall <tmarshall@mirantis.com> <trapier.marshall@docker.com>
|
||||
Trishna Guha <trishnaguha17@gmail.com>
|
||||
Tristan Carel <tristan@cogniteev.com>
|
||||
Tristan Carel <tristan@cogniteev.com> <tristan.carel@gmail.com>
|
||||
Tyler Brown <tylers.pile@gmail.com>
|
||||
Umesh Yadav <umesh4257@gmail.com>
|
||||
Umesh Yadav <umesh4257@gmail.com> <dungeonmaster18@users.noreply.github.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
@@ -646,25 +441,15 @@ Victor Vieux <victor.vieux@docker.com> <victor@docker.com>
|
||||
Victor Vieux <victor.vieux@docker.com> <victor@dotcloud.com>
|
||||
Victor Vieux <victor.vieux@docker.com> <victorvieux@gmail.com>
|
||||
Victor Vieux <victor.vieux@docker.com> <vieux@docker.com>
|
||||
Vikas Choudhary <choudharyvikas16@gmail.com>
|
||||
Vikram bir Singh <vsingh@mirantis.com>
|
||||
Vikram bir Singh <vsingh@mirantis.com> <vikrambir.singh@docker.com>
|
||||
Viktor Vojnovski <viktor.vojnovski@amadeus.com> <vojnovski@gmail.com>
|
||||
Vincent Batts <vbatts@redhat.com> <vbatts@hashbangbash.com>
|
||||
Vincent Bernat <vincent@bernat.ch>
|
||||
Vincent Bernat <vincent@bernat.ch> <bernat@luffy.cx>
|
||||
Vincent Bernat <vincent@bernat.ch> <Vincent.Bernat@exoscale.ch>
|
||||
Vincent Bernat <vincent@bernat.ch> <vincent@bernat.im>
|
||||
Vincent Boulineau <vincent.boulineau@datadoghq.com>
|
||||
Vincent Bernat <Vincent.Bernat@exoscale.ch> <bernat@luffy.cx>
|
||||
Vincent Bernat <Vincent.Bernat@exoscale.ch> <vincent@bernat.im>
|
||||
Vincent Demeester <vincent.demeester@docker.com> <vincent+github@demeester.fr>
|
||||
Vincent Demeester <vincent.demeester@docker.com> <vincent@demeester.fr>
|
||||
Vincent Demeester <vincent.demeester@docker.com> <vincent@sbr.pm>
|
||||
Vishnu Kannan <vishnuk@google.com>
|
||||
Vitaly Ostrosablin <vostrosablin@virtuozzo.com>
|
||||
Vitaly Ostrosablin <vostrosablin@virtuozzo.com> <tmp6154@yandex.ru>
|
||||
Vladimir Rutsky <altsysrq@gmail.com> <iamironbob@gmail.com>
|
||||
Vladislav Kolesnikov <vkolesnikov@beget.ru>
|
||||
Vladislav Kolesnikov <vkolesnikov@beget.ru> <prime@vladqa.ru>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
Wang Chao <chao.wang@ucloud.cn>
|
||||
Wang Chao <chao.wang@ucloud.cn> <wcwxyz@gmail.com>
|
||||
@@ -676,24 +461,11 @@ Wang Yuexiao <wang.yuexiao@zte.com.cn>
|
||||
Wayne Chang <wayne@neverfear.org>
|
||||
Wayne Song <wsong@docker.com> <wsong@users.noreply.github.com>
|
||||
Wei Wu <wuwei4455@gmail.com> cizixs <cizixs@163.com>
|
||||
Wei-Ting Kuo <waitingkuo0527@gmail.com>
|
||||
Wen Cheng Ma <wenchma@cn.ibm.com>
|
||||
Wenjun Tang <tangwj2@lenovo.com> <dodia@163.com>
|
||||
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
Wing-Kam Wong <wingkwong.code@gmail.com>
|
||||
WuLonghui <wlh6666@qq.com>
|
||||
Xian Chaobo <xianchaobo@huawei.com>
|
||||
Xian Chaobo <xianchaobo@huawei.com> <jimmyxian2004@yahoo.com.cn>
|
||||
Xianglin Gao <xlgao@zju.edu.cn>
|
||||
Xianjie <guxianjie@gmail.com>
|
||||
Xianjie <guxianjie@gmail.com> <datastream@datastream-laptop.local>
|
||||
Xianlu Bird <xianlubird@gmail.com>
|
||||
Xiao YongBiao <xyb4638@gmail.com>
|
||||
Xiao Zhang <xiaozhang0210@hotmail.com>
|
||||
Xiaodong Liu <liuxiaodong@loongson.cn>
|
||||
Xiaodong Zhang <a4012017@sina.com>
|
||||
Xiaohua Ding <xiao_hua_ding@sina.cn>
|
||||
Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
|
||||
Xuecong Liao <satorulogic@gmail.com>
|
||||
Yamasaki Masahide <masahide.y@gmail.com>
|
||||
@@ -705,29 +477,15 @@ Yi EungJun <eungjun.yi@navercorp.com> <semtlenori@gmail.com>
|
||||
Ying Li <ying.li@docker.com>
|
||||
Ying Li <ying.li@docker.com> <cyli@twistedmatrix.com>
|
||||
Yong Tang <yong.tang.github@outlook.com> <yongtang@users.noreply.github.com>
|
||||
Yongxin Li <yxli@alauda.io>
|
||||
Yosef Fertel <yfertel@gmail.com> <frosforever@users.noreply.github.com>
|
||||
Yu Changchun <yuchangchun1@huawei.com>
|
||||
Yu Chengxia <yuchengxia@huawei.com>
|
||||
Yu Peng <yu.peng36@zte.com.cn>
|
||||
Yu Peng <yu.peng36@zte.com.cn> <yupeng36@zte.com.cn>
|
||||
Yuan Sun <sunyuan3@huawei.com>
|
||||
Yue Zhang <zy675793960@yeah.net>
|
||||
Yufei Xiong <yufei.xiong@qq.com>
|
||||
Zach Gershman <zachgersh@gmail.com>
|
||||
Zach Gershman <zachgersh@gmail.com> <zachgersh@users.noreply.github.com>
|
||||
Zachary Jaffee <zjaffee@us.ibm.com> <zij@case.edu>
|
||||
Zachary Jaffee <zjaffee@us.ibm.com> <zjaffee@apache.org>
|
||||
Zhang Kun <zkazure@gmail.com>
|
||||
Zhang Wentao <zhangwentao234@huawei.com>
|
||||
ZhangHang <stevezhang2014@gmail.com>
|
||||
Zhenkun Bi <bi.zhenkun@zte.com.cn>
|
||||
Zhou Hao <zhouhao@cn.fujitsu.com>
|
||||
Zhoulin Xie <zhoulin.xie@daocloud.io>
|
||||
Zhu Kunjia <zhu.kunjia@zte.com.cn>
|
||||
Ziheng Liu <lzhfromustc@gmail.com>
|
||||
Zou Yu <zouyu7@huawei.com>
|
||||
Zuhayr Elahi <zuhayr.elahi@docker.com>
|
||||
Zuhayr Elahi <zuhayr.elahi@docker.com> <elahi.zuhayr@gmail.com>
|
||||
정재영 <jjy600901@gmail.com>
|
||||
정재영 <jjy600901@gmail.com> <43400316+J-jaeyoung@users.noreply.github.com>
|
||||
|
||||
3609
CHANGELOG.md
Normal file
3609
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,10 +27,10 @@ issue, please bring it to their attention right away!
|
||||
Please **DO NOT** file a public issue, instead send your report privately to
|
||||
[security@docker.com](mailto:security@docker.com).
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it,
|
||||
although we keep your name confidential if you request it. We also like to send
|
||||
gifts—if you're into schwag, make sure to let us know. We currently do not
|
||||
offer a paid security bounty program, but are not ruling it out in the future.
|
||||
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||
We also like to send gifts—if you're into schwag, make sure to let
|
||||
us know. We currently do not offer a paid security bounty program, but are not
|
||||
ruling it out in the future.
|
||||
|
||||
|
||||
## Reporting other issues
|
||||
@@ -101,8 +101,9 @@ the contributors guide.
|
||||
<td>
|
||||
<p>
|
||||
Register for the Docker Community Slack at
|
||||
<a href="https://dockr.ly/slack" target="_blank">https://dockr.ly/slack</a>.
|
||||
<a href="https://community.docker.com/registrations/groups/4316" target="_blank">https://community.docker.com/registrations/groups/4316</a>.
|
||||
We use the #moby-project channel for general discussion, and there are separate channels for other Moby projects such as #containerd.
|
||||
Archives are available at <a href="https://dockercommunity.slackarchive.io/" target="_blank">https://dockercommunity.slackarchive.io/</a>.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
814
Dockerfile
814
Dockerfile
@@ -1,648 +1,242 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# This file describes the standard way to build Docker, using docker
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # Use make to build a development environment image and run it in a container.
|
||||
# # This is slow the first time.
|
||||
# make BIND_DIR=. shell
|
||||
#
|
||||
# The following commands are executed inside the running container.
|
||||
|
||||
ARG GO_VERSION=1.23.9
|
||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
||||
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
|
||||
ARG XX_VERSION=1.6.1
|
||||
# # Make a dockerd binary.
|
||||
# # hack/make.sh binary
|
||||
#
|
||||
# # Install dockerd to /usr/local/bin
|
||||
# # make install
|
||||
#
|
||||
# # Run unit tests
|
||||
# # hack/test/unit
|
||||
#
|
||||
# # Run tests e.g. integration, py
|
||||
# # hack/make.sh binary test-integration test-docker-py
|
||||
#
|
||||
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
ARG VPNKIT_VERSION=0.5.0
|
||||
ARG DOCKERCLI_VERSION=v17.06.2-ce
|
||||
|
||||
ARG SYSTEMD="false"
|
||||
ARG DOCKER_STATIC=1
|
||||
|
||||
# cross compilation helper
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
# dummy stage to make sure the image is built for deps that don't support some
|
||||
# architectures
|
||||
FROM --platform=$BUILDPLATFORM busybox AS build-dummy
|
||||
RUN mkdir -p /build
|
||||
FROM scratch AS binary-dummy
|
||||
COPY --from=build-dummy /build /build
|
||||
|
||||
# base
|
||||
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.d/debian.sources || true
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y file
|
||||
ENV GO111MODULE=off
|
||||
ENV GOTOOLCHAIN=local
|
||||
FROM golang:1.10.4 AS base
|
||||
# FIXME(vdemeester) this is kept for other script depending on it to not fail right away
|
||||
# Remove this once the other scripts uses something else to detect the version
|
||||
ENV GO_VERSION 1.10.4
|
||||
# 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
|
||||
|
||||
FROM base AS criu
|
||||
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 \
|
||||
echo 'deb https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_11/ /' > /etc/apt/sources.list.d/criu.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends criu \
|
||||
&& install -D /usr/sbin/criu /build/criu
|
||||
|
||||
# registry
|
||||
FROM base AS registry-src
|
||||
WORKDIR /usr/src/registry
|
||||
RUN git init . && git remote add origin "https://github.com/distribution/distribution.git"
|
||||
# Install CRIU for checkpoint/restore support
|
||||
ENV CRIU_VERSION 3.6
|
||||
# Install dependency packages specific to criu
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libnet-dev \
|
||||
libprotobuf-c0-dev \
|
||||
libprotobuf-dev \
|
||||
libnl-3-dev \
|
||||
libcap-dev \
|
||||
protobuf-compiler \
|
||||
protobuf-c-compiler \
|
||||
python-protobuf \
|
||||
&& mkdir -p /usr/src/criu \
|
||||
&& curl -sSL https://github.com/checkpoint-restore/criu/archive/v${CRIU_VERSION}.tar.gz | tar -C /usr/src/criu/ -xz --strip-components=1 \
|
||||
&& cd /usr/src/criu \
|
||||
&& make \
|
||||
&& make PREFIX=/build/ install-criu
|
||||
|
||||
FROM base AS registry
|
||||
WORKDIR /go/src/github.com/docker/distribution
|
||||
# REGISTRY_VERSION specifies the version of the registry to build and install
|
||||
# from the https://github.com/docker/distribution repository. This version of
|
||||
# the registry is used to test both schema 1 and schema 2 manifests. Generally,
|
||||
# the version specified here should match a current release.
|
||||
ARG REGISTRY_VERSION=v2.3.0
|
||||
# REGISTRY_VERSION_SCHEMA1 specifies the version of the registry to build and
|
||||
# install from the https://github.com/docker/distribution repository. This is
|
||||
# an older (pre v2.3.0) version of the registry that only supports schema1
|
||||
# manifests. This version of the registry is not working on arm64, so installation
|
||||
# is skipped on that architecture.
|
||||
ARG REGISTRY_VERSION_SCHEMA1=v2.1.0
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=from=registry-src,src=/usr/src/registry,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=registry-build-$TARGETPLATFORM \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src <<EOT
|
||||
set -ex
|
||||
git fetch -q --depth 1 origin "${REGISTRY_VERSION}" +refs/tags/*:refs/tags/*
|
||||
git checkout -q FETCH_HEAD
|
||||
export GOPATH="/go/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"
|
||||
CGO_ENABLED=0 xx-go build -o /build/registry-v2 -v ./cmd/registry
|
||||
xx-verify /build/registry-v2
|
||||
case $TARGETPLATFORM in
|
||||
linux/amd64|linux/arm/v7|linux/ppc64le|linux/s390x)
|
||||
git fetch -q --depth 1 origin "${REGISTRY_VERSION_SCHEMA1}" +refs/tags/*:refs/tags/*
|
||||
git checkout -q FETCH_HEAD
|
||||
CGO_ENABLED=0 xx-go build -o /build/registry-v2-schema1 -v ./cmd/registry
|
||||
xx-verify /build/registry-v2-schema1
|
||||
;;
|
||||
esac
|
||||
EOT
|
||||
# Install two versions of the registry. The first is an older version that
|
||||
# only supports schema1 manifests. The second is a newer version that supports
|
||||
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||
# and schema2 manifests.
|
||||
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||
&& case $(dpkg --print-architecture) in \
|
||||
amd64|ppc64*|s390x) \
|
||||
(cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \
|
||||
GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \
|
||||
go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \
|
||||
;; \
|
||||
esac \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
|
||||
|
||||
FROM base AS docker-py
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f
|
||||
RUN git clone https://github.com/docker/docker-py.git /build \
|
||||
&& cd /build \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT
|
||||
|
||||
|
||||
# go-swagger
|
||||
FROM base AS swagger-src
|
||||
WORKDIR /usr/src/swagger
|
||||
# Currently uses a fork from https://github.com/kolyshkin/go-swagger/tree/golang-1.13-fix
|
||||
# TODO: move to under moby/ or fix upstream go-swagger to work for us.
|
||||
RUN git init . && git remote add origin "https://github.com/kolyshkin/go-swagger.git"
|
||||
# GO_SWAGGER_COMMIT specifies the version of the go-swagger binary to build and
|
||||
# install. Go-swagger is used in CI for validating swagger.yaml in hack/validate/swagger-gen
|
||||
ARG GO_SWAGGER_COMMIT=c56166c036004ba7a3a321e5951ba472b9ae298c
|
||||
RUN git fetch -q --depth 1 origin "${GO_SWAGGER_COMMIT}" && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS swagger
|
||||
WORKDIR /go/src/github.com/go-swagger/go-swagger
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=from=swagger-src,src=/usr/src/swagger,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=swagger-build-$TARGETPLATFORM \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src/ <<EOT
|
||||
set -e
|
||||
xx-go build -o /build/swagger ./cmd/swagger
|
||||
xx-verify /build/swagger
|
||||
EOT
|
||||
# Install go-swagger for validating swagger.yaml
|
||||
ENV GO_SWAGGER_COMMIT c28258affb0b6251755d92489ef685af8d4ff3eb
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/go-swagger/go-swagger.git "$GOPATH/src/github.com/go-swagger/go-swagger" \
|
||||
&& (cd "$GOPATH/src/github.com/go-swagger/go-swagger" && git checkout -q "$GO_SWAGGER_COMMIT") \
|
||||
&& go build -o /build/swagger github.com/go-swagger/go-swagger/cmd/swagger \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# frozen-images
|
||||
# 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
|
||||
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 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
jq
|
||||
|
||||
FROM base AS frozen-images
|
||||
RUN apt-get update && apt-get install -y jq ca-certificates --no-install-recommends
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
RUN /download-frozen-image-v2.sh /build \
|
||||
busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
|
||||
busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \
|
||||
debian:bookworm-slim@sha256:2bc5c236e9b262645a323e9088dfa3bb1ecb16cc75811daf40a23a824d665be9 \
|
||||
hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \
|
||||
arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1
|
||||
buildpack-deps:jessie@sha256:dd86dced7c9cd2a724e779730f0a53f93b7ef42228d4344b25ce9a42a1486251 \
|
||||
busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0 \
|
||||
busybox:glibc@sha256:0b55a30394294ab23b9afd58fab94e61a923f5834fba7ddbae7f8e0c11ba85e6 \
|
||||
debian:jessie@sha256:287a20c5f73087ab406e6b364833e3fb7b3ae63ca0eb3486555dc27ed32c6e60 \
|
||||
hello-world:latest@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
|
||||
# See also ensureFrozenImagesLinux() in "integration-cli/fixtures_linux_daemon_test.go" (which needs to be updated when adding images to this list)
|
||||
|
||||
# delve
|
||||
FROM base AS delve-src
|
||||
WORKDIR /usr/src/delve
|
||||
RUN git init . && git remote add origin "https://github.com/go-delve/delve.git"
|
||||
# DELVE_VERSION specifies the version of the Delve debugger binary
|
||||
# from the https://github.com/go-delve/delve repository.
|
||||
# It can be used to run Docker with a possibility of
|
||||
# attaching debugger to it.
|
||||
ARG DELVE_VERSION=v1.20.1
|
||||
RUN git fetch -q --depth 1 origin "${DELVE_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
# Just a little hack so we don't have to install these deps twice, once for runc and once for dockerd
|
||||
FROM base AS runtime-dev
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libapparmor-dev \
|
||||
libseccomp-dev
|
||||
|
||||
FROM base AS delve-build
|
||||
WORKDIR /usr/src/delve
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=from=delve-src,src=/usr/src/delve,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=delve-build-$TARGETPLATFORM \
|
||||
--mount=type=cache,target=/go/pkg/mod <<EOT
|
||||
set -e
|
||||
GO111MODULE=on xx-go build -o /build/dlv ./cmd/dlv
|
||||
xx-verify /build/dlv
|
||||
EOT
|
||||
|
||||
# delve is currently only supported on linux/amd64 and linux/arm64;
|
||||
# https://github.com/go-delve/delve/blob/v1.8.1/pkg/proc/native/support_sentinel.go#L1-L6
|
||||
FROM binary-dummy AS delve-windows
|
||||
FROM binary-dummy AS delve-linux-arm
|
||||
FROM binary-dummy AS delve-linux-ppc64le
|
||||
FROM binary-dummy AS delve-linux-s390x
|
||||
FROM delve-build AS delve-linux-amd64
|
||||
FROM delve-build AS delve-linux-arm64
|
||||
FROM delve-linux-${TARGETARCH} AS delve-linux
|
||||
FROM delve-${TARGETOS} AS delve
|
||||
FROM base AS tomlv
|
||||
ENV INSTALL_BINARY_NAME=tomlv
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM base AS tomll
|
||||
# GOTOML_VERSION specifies the version of the tomll binary to build and install
|
||||
# from the https://github.com/pelletier/go-toml repository. This binary is used
|
||||
# in CI in the hack/validate/toml script.
|
||||
#
|
||||
# When updating this version, consider updating the github.com/pelletier/go-toml
|
||||
# dependency in vendor.mod accordingly.
|
||||
ARG GOTOML_VERSION=v1.8.1
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
GOBIN=/build/ GO111MODULE=on go install "github.com/pelletier/go-toml/cmd/tomll@${GOTOML_VERSION}" \
|
||||
&& /build/tomll --help
|
||||
FROM base AS vndr
|
||||
ENV INSTALL_BINARY_NAME=vndr
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM base AS gowinres
|
||||
# GOWINRES_VERSION defines go-winres tool version
|
||||
ARG GOWINRES_VERSION=v0.3.0
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
GOBIN=/build/ GO111MODULE=on go install "github.com/tc-hib/go-winres@${GOWINRES_VERSION}" \
|
||||
&& /build/go-winres --help
|
||||
FROM base AS containerd
|
||||
RUN apt-get update && apt-get install -y btrfs-tools
|
||||
ENV INSTALL_BINARY_NAME=containerd
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
# containerd
|
||||
FROM base AS containerd-src
|
||||
WORKDIR /usr/src/containerd
|
||||
RUN git init . && git remote add origin "https://github.com/containerd/containerd.git"
|
||||
# CONTAINERD_VERSION is used to build containerd binaries, and used for the
|
||||
# integration tests. The distributed docker .deb and .rpm packages depend on a
|
||||
# separate (containerd.io) package, which may be a different version as is
|
||||
# specified here. The containerd golang package is also pinned in vendor.mod.
|
||||
# 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.38
|
||||
RUN git fetch -q --depth 1 origin "${CONTAINERD_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
FROM base AS proxy
|
||||
ENV INSTALL_BINARY_NAME=proxy
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM base AS containerd-build
|
||||
WORKDIR /go/src/github.com/containerd/containerd
|
||||
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 \
|
||||
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
|
||||
set -e
|
||||
export CC=$(xx-info)-gcc
|
||||
export CGO_ENABLED=$([ "$DOCKER_STATIC" = "1" ] && echo "0" || echo "1")
|
||||
xx-go --wrap
|
||||
make $([ "$DOCKER_STATIC" = "1" ] && echo "STATIC=1") binaries
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/containerd
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/containerd-shim-runc-v2
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/ctr
|
||||
mkdir /build
|
||||
mv bin/containerd bin/containerd-shim-runc-v2 bin/ctr /build
|
||||
EOT
|
||||
|
||||
FROM containerd-build AS containerd-linux
|
||||
FROM binary-dummy AS containerd-windows
|
||||
FROM containerd-${TARGETOS} AS containerd
|
||||
|
||||
FROM base AS golangci_lint
|
||||
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}" \
|
||||
&& /build/golangci-lint --version
|
||||
|
||||
FROM base AS gotestsum
|
||||
ARG GOTESTSUM_VERSION=v1.8.2
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
GOBIN=/build/ GO111MODULE=on go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" \
|
||||
&& /build/gotestsum --version
|
||||
|
||||
FROM base AS shfmt
|
||||
ARG SHFMT_VERSION=v3.6.0
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
GOBIN=/build/ GO111MODULE=on go install "mvdan.cc/sh/v3/cmd/shfmt@${SHFMT_VERSION}" \
|
||||
&& /build/shfmt --version
|
||||
|
||||
# dockercli
|
||||
FROM base AS dockercli-src
|
||||
WORKDIR /tmp/dockercli
|
||||
RUN git init . && git remote add origin "https://github.com/docker/cli.git"
|
||||
ARG DOCKERCLI_VERSION
|
||||
RUN git fetch -q --depth 1 origin "${DOCKERCLI_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
RUN [ -d ./components/cli ] && mv ./components/cli /usr/src/dockercli || mv /tmp/dockercli /usr/src/dockercli
|
||||
WORKDIR /usr/src/dockercli
|
||||
FROM base AS gometalinter
|
||||
ENV INSTALL_BINARY_NAME=gometalinter
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM base AS dockercli
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
ARG DOCKERCLI_VERSION
|
||||
ARG DOCKERCLI_CHANNEL=stable
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apt-get install -y --no-install-recommends gcc libc6-dev
|
||||
RUN --mount=from=dockercli-src,src=/usr/src/dockercli,rw \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=dockercli-build-$TARGETPLATFORM <<EOT
|
||||
set -e
|
||||
DOWNLOAD_URL="https://download.docker.com/linux/static/${DOCKERCLI_CHANNEL}/$(xx-info march)/docker-${DOCKERCLI_VERSION#v}.tgz"
|
||||
if curl --head --silent --fail "${DOWNLOAD_URL}" 1>/dev/null 2>&1; then
|
||||
mkdir /build
|
||||
curl -Ls "${DOWNLOAD_URL}" | tar -xz docker/docker
|
||||
mv docker/docker /build/docker
|
||||
else
|
||||
CGO_ENABLED=0 xx-go build -o /build/docker ./cmd/docker
|
||||
fi
|
||||
xx-verify /build/docker
|
||||
EOT
|
||||
ENV INSTALL_BINARY_NAME=dockercli
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
# runc
|
||||
FROM base AS runc-src
|
||||
WORKDIR /usr/src/runc
|
||||
RUN git init . && git remote add origin "https://github.com/opencontainers/runc.git"
|
||||
# RUNC_VERSION should match the version that is used by the containerd version
|
||||
# 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.12
|
||||
RUN git fetch -q --depth 1 origin "${RUNC_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
FROM runtime-dev AS runc
|
||||
ENV INSTALL_BINARY_NAME=runc
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM base AS runc-build
|
||||
WORKDIR /go/src/github.com/opencontainers/runc
|
||||
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 \
|
||||
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
|
||||
set -e
|
||||
xx-go --wrap
|
||||
CGO_ENABLED=1 make "$([ "$DOCKER_STATIC" = "1" ] && echo "static" || echo "runc")"
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") runc
|
||||
mkdir /build
|
||||
mv runc /build/
|
||||
EOT
|
||||
FROM base AS tini
|
||||
RUN apt-get update && apt-get install -y cmake vim-common
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
ENV INSTALL_BINARY_NAME=tini
|
||||
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
|
||||
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
|
||||
|
||||
FROM runc-build AS runc-linux
|
||||
FROM binary-dummy AS runc-windows
|
||||
FROM runc-${TARGETOS} AS runc
|
||||
|
||||
# tini
|
||||
FROM base AS tini-src
|
||||
WORKDIR /usr/src/tini
|
||||
RUN git init . && git remote add origin "https://github.com/krallin/tini.git"
|
||||
# TINI_VERSION specifies the version of tini (docker-init) to build. This
|
||||
# binary is used when starting containers with the `--init` option.
|
||||
ARG TINI_VERSION=v0.19.0
|
||||
RUN git fetch -q --depth 1 origin "${TINI_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS tini-build
|
||||
WORKDIR /go/src/github.com/krallin/tini
|
||||
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
|
||||
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 \
|
||||
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
|
||||
CC=$(xx-info)-gcc cmake .
|
||||
make tini-static
|
||||
xx-verify --static tini-static
|
||||
mkdir /build
|
||||
mv tini-static /build/docker-init
|
||||
EOT
|
||||
|
||||
FROM tini-build AS tini-linux
|
||||
FROM binary-dummy AS tini-windows
|
||||
FROM tini-${TARGETOS} AS tini
|
||||
|
||||
# rootlesskit
|
||||
FROM base AS rootlesskit-src
|
||||
WORKDIR /usr/src/rootlesskit
|
||||
RUN git init . && git remote add origin "https://github.com/rootless-containers/rootlesskit.git"
|
||||
# When updating, also update rootlesskit commit in vendor.mod accordingly.
|
||||
ARG ROOTLESSKIT_VERSION=v1.1.0
|
||||
RUN git fetch -q --depth 1 origin "${ROOTLESSKIT_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS rootlesskit-build
|
||||
WORKDIR /go/src/github.com/rootless-containers/rootlesskit
|
||||
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 \
|
||||
pkg-config
|
||||
ENV GO111MODULE=on
|
||||
ARG DOCKER_STATIC
|
||||
RUN --mount=from=rootlesskit-src,src=/usr/src/rootlesskit,rw \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=rootlesskit-build-$TARGETPLATFORM <<EOT
|
||||
set -e
|
||||
export CGO_ENABLED=$([ "$DOCKER_STATIC" = "1" ] && echo "0" || echo "1")
|
||||
xx-go build -o /build/rootlesskit -ldflags="$([ "$DOCKER_STATIC" != "1" ] && echo "-linkmode=external")" ./cmd/rootlesskit
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /build/rootlesskit
|
||||
xx-go build -o /build/rootlesskit-docker-proxy -ldflags="$([ "$DOCKER_STATIC" != "1" ] && echo "-linkmode=external")" ./cmd/rootlesskit-docker-proxy
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /build/rootlesskit-docker-proxy
|
||||
EOT
|
||||
COPY ./contrib/dockerd-rootless.sh /build/
|
||||
COPY ./contrib/dockerd-rootless-setuptool.sh /build/
|
||||
|
||||
FROM rootlesskit-build AS rootlesskit-linux
|
||||
FROM binary-dummy AS rootlesskit-windows
|
||||
FROM rootlesskit-${TARGETOS} AS rootlesskit
|
||||
|
||||
FROM base AS crun
|
||||
ARG CRUN_VERSION=1.4.5
|
||||
RUN --mount=type=cache,sharing=locked,id=moby-crun-aptlib,target=/var/lib/apt \
|
||||
--mount=type=cache,sharing=locked,id=moby-crun-aptcache,target=/var/cache/apt \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
libcap-dev \
|
||||
libprotobuf-c-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
libyajl-dev \
|
||||
python3 \
|
||||
;
|
||||
RUN --mount=type=tmpfs,target=/tmp/crun-build \
|
||||
git clone https://github.com/containers/crun.git /tmp/crun-build && \
|
||||
cd /tmp/crun-build && \
|
||||
git checkout -q "${CRUN_VERSION}" && \
|
||||
./autogen.sh && \
|
||||
./configure --bindir=/build && \
|
||||
make -j install
|
||||
|
||||
# vpnkit
|
||||
# use dummy scratch stage to avoid build to fail for unsupported platforms
|
||||
FROM scratch AS vpnkit-windows
|
||||
FROM scratch AS vpnkit-linux-386
|
||||
FROM scratch AS vpnkit-linux-arm
|
||||
FROM scratch AS vpnkit-linux-ppc64le
|
||||
FROM scratch AS vpnkit-linux-riscv64
|
||||
FROM scratch AS vpnkit-linux-s390x
|
||||
FROM djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-linux-amd64
|
||||
FROM djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-linux-arm64
|
||||
FROM vpnkit-linux-${TARGETARCH} AS vpnkit-linux
|
||||
FROM vpnkit-${TARGETOS} AS vpnkit
|
||||
|
||||
# containerutility
|
||||
FROM base AS containerutil-src
|
||||
WORKDIR /usr/src/containerutil
|
||||
RUN git init . && git remote add origin "https://github.com/docker-archive/windows-container-utility.git"
|
||||
ARG CONTAINERUTILITY_VERSION=aa1ba87e99b68e0113bd27ec26c60b88f9d4ccd9
|
||||
RUN git fetch -q --depth 1 origin "${CONTAINERUTILITY_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS containerutil-build
|
||||
WORKDIR /usr/src/containerutil
|
||||
ARG TARGETPLATFORM
|
||||
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
|
||||
CC="$(xx-info)-gcc" CXX="$(xx-info)-g++" make
|
||||
xx-verify --static containerutility.exe
|
||||
mkdir /build
|
||||
mv containerutility.exe /build/
|
||||
EOT
|
||||
|
||||
FROM binary-dummy AS containerutil-linux
|
||||
FROM containerutil-build AS containerutil-windows-amd64
|
||||
FROM containerutil-windows-${TARGETARCH} AS containerutil-windows
|
||||
FROM containerutil-${TARGETOS} AS containerutil
|
||||
|
||||
FROM base AS dev-systemd-false
|
||||
COPY --from=dockercli /build/ /usr/local/cli
|
||||
COPY --from=frozen-images /build/ /docker-frozen-images
|
||||
COPY --from=swagger /build/ /usr/local/bin/
|
||||
COPY --from=delve /build/ /usr/local/bin/
|
||||
COPY --from=tomll /build/ /usr/local/bin/
|
||||
COPY --from=gowinres /build/ /usr/local/bin/
|
||||
COPY --from=tini /build/ /usr/local/bin/
|
||||
COPY --from=registry /build/ /usr/local/bin/
|
||||
|
||||
# Skip the CRIU stage for now, as the opensuse package repository is sometimes
|
||||
# unstable, and we're currently not using it in CI.
|
||||
#
|
||||
# FIXME(thaJeztah): re-enable this stage when https://github.com/moby/moby/issues/38963 is resolved (see https://github.com/moby/moby/pull/38984)
|
||||
# COPY --from=criu /build/ /usr/local/bin/
|
||||
COPY --from=gotestsum /build/ /usr/local/bin/
|
||||
COPY --from=golangci_lint /build/ /usr/local/bin/
|
||||
COPY --from=shfmt /build/ /usr/local/bin/
|
||||
COPY --from=runc /build/ /usr/local/bin/
|
||||
COPY --from=containerd /build/ /usr/local/bin/
|
||||
COPY --from=rootlesskit /build/ /usr/local/bin/
|
||||
COPY --from=vpnkit / /usr/local/bin/
|
||||
COPY --from=containerutil /build/ /usr/local/bin/
|
||||
COPY --from=crun /build/ /usr/local/bin/
|
||||
COPY hack/dockerfile/etc/docker/ /etc/docker/
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
VOLUME /var/lib/docker
|
||||
VOLUME /home/unprivilegeduser/.local/share/docker
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
FROM dev-systemd-false AS dev-systemd-true
|
||||
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 -y --no-install-recommends \
|
||||
dbus \
|
||||
dbus-user-session \
|
||||
systemd \
|
||||
systemd-sysv
|
||||
ENTRYPOINT ["hack/dind-systemd"]
|
||||
|
||||
FROM dev-systemd-${SYSTEMD} AS dev-base
|
||||
# TODO: Some of this is only really needed for testing, it would be nice to split this up
|
||||
FROM runtime-dev AS dev
|
||||
RUN groupadd -r docker
|
||||
RUN useradd --create-home --gid docker unprivilegeduser \
|
||||
&& mkdir -p /home/unprivilegeduser/.local/share/docker \
|
||||
&& chown -R unprivilegeduser /home/unprivilegeduser
|
||||
RUN useradd --create-home --gid docker unprivilegeduser
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc
|
||||
# Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH
|
||||
RUN echo "source /usr/share/bash-completion/bash_completion" >> /etc/bash.bashrc
|
||||
RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker
|
||||
RUN ldconfig
|
||||
# Set dev environment as safe git directory to prevent "dubious ownership" errors
|
||||
# when bind-mounting the source into the dev-container. See https://github.com/moby/moby/pull/44930
|
||||
RUN git config --global --add safe.directory $GOPATH/src/github.com/docker/docker
|
||||
# This should only install packages that are specifically needed for the dev environment and nothing else
|
||||
# Do you really need to add another package here? Can it be done in a different build stage?
|
||||
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 -y --no-install-recommends \
|
||||
apparmor \
|
||||
bash-completion \
|
||||
bzip2 \
|
||||
inetutils-ping \
|
||||
iproute2 \
|
||||
iptables \
|
||||
jq \
|
||||
libcap2-bin \
|
||||
libnet1 \
|
||||
libnl-3-200 \
|
||||
libprotobuf-c1 \
|
||||
libyajl2 \
|
||||
net-tools \
|
||||
patch \
|
||||
pigz \
|
||||
sudo \
|
||||
thin-provisioning-tools \
|
||||
uidmap \
|
||||
vim \
|
||||
vim-common \
|
||||
xfsprogs \
|
||||
xz-utils \
|
||||
zip \
|
||||
zstd
|
||||
# Switch to use iptables instead of nftables (to match the CI hosts)
|
||||
# TODO use some kind of runtime auto-detection instead if/when nftables is supported (https://github.com/moby/moby/issues/26824)
|
||||
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
|
||||
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 \
|
||||
gcc \
|
||||
pkg-config \
|
||||
dpkg-dev \
|
||||
libapparmor-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
libsecret-1-dev \
|
||||
libsystemd-dev \
|
||||
libudev-dev \
|
||||
yamllint
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apparmor \
|
||||
aufs-tools \
|
||||
bash-completion \
|
||||
btrfs-tools \
|
||||
iptables \
|
||||
jq \
|
||||
libdevmapper-dev \
|
||||
libudev-dev \
|
||||
libsystemd-dev \
|
||||
binutils-mingw-w64 \
|
||||
g++-mingw-w64-x86-64 \
|
||||
net-tools \
|
||||
pigz \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
python-wheel \
|
||||
thin-provisioning-tools \
|
||||
vim \
|
||||
vim-common \
|
||||
xfsprogs \
|
||||
zip \
|
||||
bzip2 \
|
||||
xz-utils \
|
||||
--no-install-recommends
|
||||
COPY --from=swagger /build/swagger* /usr/local/bin/
|
||||
COPY --from=frozen-images /build/ /docker-frozen-images
|
||||
COPY --from=gometalinter /build/ /usr/local/bin/
|
||||
COPY --from=tomlv /build/ /usr/local/bin/
|
||||
COPY --from=vndr /build/ /usr/local/bin/
|
||||
COPY --from=tini /build/ /usr/local/bin/
|
||||
COPY --from=runc /build/ /usr/local/bin/
|
||||
COPY --from=containerd /build/ /usr/local/bin/
|
||||
COPY --from=proxy /build/ /usr/local/bin/
|
||||
COPY --from=dockercli /build/ /usr/local/cli
|
||||
COPY --from=registry /build/registry* /usr/local/bin/
|
||||
COPY --from=criu /build/ /usr/local/
|
||||
COPY --from=docker-py /build/ /docker-py
|
||||
# TODO: This is for the docker-py tests, which shouldn't really be needed for
|
||||
# this image, but currently CI is expecting to run this image. This should be
|
||||
# split out into a separate image, including all the `python-*` deps installed
|
||||
# above.
|
||||
RUN cd /docker-py \
|
||||
&& pip install docker-pycreds==0.2.1 \
|
||||
&& pip install yamllint==1.5.0 \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
FROM base AS build
|
||||
COPY --from=gowinres /build/ /usr/local/bin/
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
|
||||
# Options for hack/validate/gometalinter
|
||||
ENV GOMETALINTER_OPTS="--deadline=2m"
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV GO111MODULE=off
|
||||
ENV CGO_ENABLED=1
|
||||
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 \
|
||||
clang \
|
||||
lld \
|
||||
llvm
|
||||
ARG TARGETPLATFORM
|
||||
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 \
|
||||
xx-apt-get install --no-install-recommends -y \
|
||||
dpkg-dev \
|
||||
gcc \
|
||||
libapparmor-dev \
|
||||
libc6-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
libsecret-1-dev \
|
||||
libsystemd-dev \
|
||||
libudev-dev \
|
||||
pkg-config
|
||||
ARG DOCKER_BUILDTAGS
|
||||
ARG DOCKER_DEBUG
|
||||
ARG DOCKER_GITCOMMIT=HEAD
|
||||
ARG DOCKER_LDFLAGS
|
||||
ARG DOCKER_STATIC
|
||||
ARG VERSION
|
||||
ARG PLATFORM
|
||||
ARG PRODUCT
|
||||
ARG DEFAULT_PRODUCT_LICENSE
|
||||
ARG PACKAGER_NAME
|
||||
# PREFIX overrides DEST dir in make.sh script otherwise it fails because of
|
||||
# read only mount in current work dir
|
||||
ENV PREFIX=/tmp
|
||||
RUN <<EOT
|
||||
# in bullseye arm64 target does not link with lld so configure it to use ld instead
|
||||
if [ "$(xx-info arch)" = "arm64" ]; then
|
||||
XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple
|
||||
fi
|
||||
EOT
|
||||
RUN --mount=type=bind,target=. \
|
||||
--mount=type=tmpfs,target=cli/winresources/dockerd \
|
||||
--mount=type=tmpfs,target=cli/winresources/docker-proxy \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=moby-build-$TARGETPLATFORM <<EOT
|
||||
set -e
|
||||
target=$([ "$DOCKER_STATIC" = "1" ] && echo "binary" || echo "dynbinary")
|
||||
xx-go --wrap
|
||||
PKG_CONFIG=$(xx-go env PKG_CONFIG) ./hack/make.sh $target
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/dockerd$([ "$(xx-info os)" = "windows" ] && echo ".exe")
|
||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/docker-proxy$([ "$(xx-info os)" = "windows" ] && echo ".exe")
|
||||
mkdir /build
|
||||
mv /tmp/bundles/${target}-daemon/* /build/
|
||||
EOT
|
||||
|
||||
# usage:
|
||||
# > docker buildx bake binary
|
||||
# > DOCKER_STATIC=0 docker buildx bake binary
|
||||
# or
|
||||
# > make binary
|
||||
# > make dynbinary
|
||||
FROM scratch AS binary
|
||||
COPY --from=build /build/ /
|
||||
|
||||
# usage:
|
||||
# > docker buildx bake all
|
||||
FROM scratch AS all
|
||||
COPY --from=tini /build/ /
|
||||
COPY --from=runc /build/ /
|
||||
COPY --from=containerd /build/ /
|
||||
COPY --from=rootlesskit /build/ /
|
||||
COPY --from=containerutil /build/ /
|
||||
COPY --from=vpnkit / /
|
||||
COPY --from=build /build /
|
||||
|
||||
# smoke tests
|
||||
# usage:
|
||||
# > docker buildx bake binary-smoketest
|
||||
FROM base AS smoketest
|
||||
WORKDIR /usr/local/bin
|
||||
COPY --from=build /build .
|
||||
RUN <<EOT
|
||||
set -ex
|
||||
file dockerd
|
||||
dockerd --version
|
||||
file docker-proxy
|
||||
docker-proxy --version
|
||||
EOT
|
||||
|
||||
# usage:
|
||||
# > make shell
|
||||
# > SYSTEMD=true make shell
|
||||
FROM dev-base AS dev
|
||||
COPY . .
|
||||
VOLUME /var/lib/docker
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
# Upload docker source
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
106
Dockerfile.e2e
106
Dockerfile.e2e
@@ -1,84 +1,74 @@
|
||||
ARG GO_VERSION=1.23.9
|
||||
## Step 1: Build tests
|
||||
FROM golang:1.10.4-alpine3.7 as builder
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS base
|
||||
ENV GO111MODULE=off
|
||||
RUN apk --no-cache add \
|
||||
RUN apk add --update \
|
||||
bash \
|
||||
btrfs-progs-dev \
|
||||
build-base \
|
||||
curl \
|
||||
lvm2-dev \
|
||||
jq
|
||||
jq \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN mkdir -p /build/
|
||||
RUN mkdir -p /go/src/github.com/docker/docker/
|
||||
WORKDIR /go/src/github.com/docker/docker/
|
||||
|
||||
FROM base AS frozen-images
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /
|
||||
RUN /download-frozen-image-v2.sh /build \
|
||||
busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
|
||||
busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
|
||||
debian:bullseye-slim@sha256:dacf278785a4daa9de07596ec739dbc07131e189942772210709c5c0777e8437 \
|
||||
hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \
|
||||
arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1
|
||||
# See also frozenImages in "testutil/environment/protect.go" (which needs to be updated when adding images to this list)
|
||||
# Generate frozen images
|
||||
COPY contrib/download-frozen-image-v2.sh contrib/download-frozen-image-v2.sh
|
||||
RUN contrib/download-frozen-image-v2.sh /output/docker-frozen-images \
|
||||
buildpack-deps:jessie@sha256:dd86dced7c9cd2a724e779730f0a53f93b7ef42228d4344b25ce9a42a1486251 \
|
||||
busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0 \
|
||||
busybox:glibc@sha256:0b55a30394294ab23b9afd58fab94e61a923f5834fba7ddbae7f8e0c11ba85e6 \
|
||||
debian:jessie@sha256:287a20c5f73087ab406e6b364833e3fb7b3ae63ca0eb3486555dc27ed32c6e60 \
|
||||
hello-world:latest@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
|
||||
|
||||
FROM base AS dockercli
|
||||
COPY hack/dockerfile/install/install.sh ./install.sh
|
||||
COPY hack/dockerfile/install/dockercli.installer ./
|
||||
RUN PREFIX=/build ./install.sh dockercli
|
||||
|
||||
# TestDockerCLIBuildSuite dependency
|
||||
FROM base AS contrib
|
||||
COPY contrib/syscall-test /build/syscall-test
|
||||
COPY contrib/httpserver/Dockerfile /build/httpserver/Dockerfile
|
||||
COPY contrib/httpserver contrib/httpserver
|
||||
RUN CGO_ENABLED=0 go build -buildmode=pie -o /build/httpserver/httpserver github.com/docker/docker/contrib/httpserver
|
||||
|
||||
# Build the integration tests and copy the resulting binaries to /build/tests
|
||||
FROM base AS builder
|
||||
# Install dockercli
|
||||
# Please edit hack/dockerfile/install/<name>.installer to update them.
|
||||
COPY hack/dockerfile/install hack/dockerfile/install
|
||||
RUN ./hack/dockerfile/install/install.sh dockercli
|
||||
|
||||
# Set tag and add sources
|
||||
COPY . .
|
||||
# Copy test sources tests that use assert can print errors
|
||||
RUN mkdir -p /build${PWD} && find integration integration-cli -name \*_test.go -exec cp --parents '{}' /build${PWD} \;
|
||||
# Build and install test binaries
|
||||
ARG DOCKER_GITCOMMIT=undefined
|
||||
ARG DOCKER_GITCOMMIT
|
||||
ENV DOCKER_GITCOMMIT=${DOCKER_GITCOMMIT:-undefined}
|
||||
ADD . .
|
||||
|
||||
# Build DockerSuite.TestBuild* dependency
|
||||
RUN CGO_ENABLED=0 go build -buildmode=pie -o /output/httpserver github.com/docker/docker/contrib/httpserver
|
||||
|
||||
# Build the integration tests and copy the resulting binaries to /output/tests
|
||||
RUN hack/make.sh build-integration-test-binary
|
||||
RUN mkdir -p /build/tests && find . -name test.main -exec cp --parents '{}' /build/tests \;
|
||||
RUN mkdir -p /output/tests && find . -name test.main -exec cp --parents '{}' /output/tests \;
|
||||
|
||||
## Generate testing image
|
||||
FROM alpine:3.10 as runner
|
||||
|
||||
ENV DOCKER_REMOTE_DAEMON=1
|
||||
ENV DOCKER_INTEGRATION_DAEMON_DEST=/
|
||||
ENTRYPOINT ["/scripts/run.sh"]
|
||||
|
||||
# Add an unprivileged user to be used for tests which need it
|
||||
RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash
|
||||
## Step 2: Generate testing image
|
||||
FROM alpine:3.7 as runner
|
||||
|
||||
# GNU tar is used for generating the emptyfs image
|
||||
RUN apk --no-cache add \
|
||||
RUN apk add --update \
|
||||
bash \
|
||||
ca-certificates \
|
||||
g++ \
|
||||
git \
|
||||
inetutils-ping \
|
||||
iptables \
|
||||
libcap2-bin \
|
||||
pigz \
|
||||
tar \
|
||||
xz
|
||||
xz \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
COPY hack/test/e2e-run.sh /scripts/run.sh
|
||||
COPY hack/make/.ensure-emptyfs /scripts/ensure-emptyfs.sh
|
||||
# Add an unprivileged user to be used for tests which need it
|
||||
RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash
|
||||
|
||||
COPY integration/testdata /tests/integration/testdata
|
||||
COPY integration/build/testdata /tests/integration/build/testdata
|
||||
COPY integration-cli/fixtures /tests/integration-cli/fixtures
|
||||
COPY contrib/httpserver/Dockerfile /tests/contrib/httpserver/Dockerfile
|
||||
COPY contrib/syscall-test /tests/contrib/syscall-test
|
||||
COPY integration-cli/fixtures /tests/integration-cli/fixtures
|
||||
|
||||
COPY --from=frozen-images /build/ /docker-frozen-images
|
||||
COPY --from=dockercli /build/ /usr/bin/
|
||||
COPY --from=contrib /build/ /tests/contrib/
|
||||
COPY --from=builder /build/ /
|
||||
COPY hack/test/e2e-run.sh /scripts/run.sh
|
||||
COPY hack/make/.ensure-emptyfs /scripts/ensure-emptyfs.sh
|
||||
|
||||
COPY --from=builder /output/docker-frozen-images /docker-frozen-images
|
||||
COPY --from=builder /output/httpserver /tests/contrib/httpserver/httpserver
|
||||
COPY --from=builder /output/tests /tests
|
||||
COPY --from=builder /usr/local/bin/docker /usr/bin/docker
|
||||
|
||||
ENV DOCKER_REMOTE_DAEMON=1 DOCKER_INTEGRATION_DAEMON_DEST=/
|
||||
|
||||
ENTRYPOINT ["/scripts/run.sh"]
|
||||
|
||||
@@ -5,26 +5,21 @@
|
||||
|
||||
# This represents the bare minimum required to build and test Docker.
|
||||
|
||||
ARG GO_VERSION=1.23.9
|
||||
FROM debian:stretch
|
||||
|
||||
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 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
|
||||
# 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
|
||||
|
||||
# Compile and runtime deps
|
||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies
|
||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
curl \
|
||||
cmake \
|
||||
gcc \
|
||||
git \
|
||||
libapparmor-dev \
|
||||
libdevmapper-dev \
|
||||
@@ -38,9 +33,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
xfsprogs \
|
||||
xz-utils \
|
||||
\
|
||||
aufs-tools \
|
||||
vim-common \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Go
|
||||
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.10.4
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go
|
||||
ENV CGO_LDFLAGS -L/lib
|
||||
|
||||
# Install runc, containerd, tini and docker-proxy
|
||||
# Please edit hack/dockerfile/install/<name>.installer to update them.
|
||||
COPY hack/dockerfile/install hack/dockerfile/install
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
#
|
||||
# 1. Clone the sources from github.com:
|
||||
#
|
||||
# >> git clone https://github.com/docker/docker.git C:\gopath\src\github.com\docker\docker
|
||||
# >> Cloning into 'C:\gopath\src\github.com\docker\docker'...
|
||||
# >> git clone https://github.com/docker/docker.git C:\go\src\github.com\docker\docker
|
||||
# >> Cloning into 'C:\go\src\github.com\docker\docker'...
|
||||
# >> remote: Counting objects: 186216, done.
|
||||
# >> remote: Compressing objects: 100% (21/21), done.
|
||||
# >> remote: Total 186216 (delta 5), reused 0 (delta 0), pack-reused 186195
|
||||
@@ -59,7 +59,7 @@
|
||||
#
|
||||
# 2. Change directory to the cloned docker sources:
|
||||
#
|
||||
# >> cd C:\gopath\src\github.com\docker\docker
|
||||
# >> cd C:\go\src\github.com\docker\docker
|
||||
#
|
||||
#
|
||||
# 3. Build a docker image with the components required to build the docker binaries from source
|
||||
@@ -79,8 +79,8 @@
|
||||
# 5. Copy the binaries out of the container, replacing HostPath with an appropriate destination
|
||||
# folder on the host system where you want the binaries to be located.
|
||||
#
|
||||
# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe
|
||||
# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe
|
||||
# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe
|
||||
# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe
|
||||
#
|
||||
#
|
||||
# 6. (Optional) Remove the interim container holding the built executable binaries:
|
||||
@@ -147,42 +147,24 @@
|
||||
# The docker integration tests do not currently run in a container on Windows, predominantly
|
||||
# due to Windows not supporting privileged mode, so anything using a volume would fail.
|
||||
# They (along with the rest of the docker CI suite) can be run using
|
||||
# https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1.
|
||||
# https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1.
|
||||
#
|
||||
# -----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# The number of build steps below are explicitly minimised to improve performance.
|
||||
|
||||
# Extremely important - do not change the following line to reference a "specific" image,
|
||||
# such as `mcr.microsoft.com/windows/servercore:ltsc2022`. If using this Dockerfile in process
|
||||
# isolated containers, the kernel of the host must match the container image, and hence
|
||||
# would fail between Windows Server 2016 (aka RS1) and Windows Server 2019 (aka RS5).
|
||||
# It is expected that the image `microsoft/windowsservercore:latest` is present, and matches
|
||||
# the hosts kernel version before doing a build.
|
||||
FROM microsoft/windowsservercore
|
||||
|
||||
# Use PowerShell as the default shell
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
ARG GO_VERSION=1.23.9
|
||||
ARG GOTESTSUM_VERSION=v1.8.2
|
||||
ARG GOWINRES_VERSION=v0.3.0
|
||||
ARG CONTAINERD_VERSION=v1.6.38
|
||||
|
||||
# Environment variable notes:
|
||||
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
|
||||
# - CONTAINERD_VERSION must be consistent with 'hack/dockerfile/install/containerd.installer' used by Linux.
|
||||
# - FROM_DOCKERFILE is used for detection of building within a container.
|
||||
ENV GO_VERSION=${GO_VERSION} `
|
||||
CONTAINERD_VERSION=${CONTAINERD_VERSION} `
|
||||
ENV GO_VERSION=1.10.4 `
|
||||
GIT_VERSION=2.11.1 `
|
||||
GOPATH=C:\gopath `
|
||||
GO111MODULE=off `
|
||||
GOTOOLCHAIN=local `
|
||||
FROM_DOCKERFILE=1 `
|
||||
GOTESTSUM_VERSION=${GOTESTSUM_VERSION} `
|
||||
GOWINRES_VERSION=${GOWINRES_VERSION}
|
||||
GOPATH=C:\go `
|
||||
FROM_DOCKERFILE=1
|
||||
|
||||
RUN `
|
||||
Function Test-Nano() { `
|
||||
@@ -211,30 +193,28 @@ RUN `
|
||||
Throw ("Failed to download " + $source) `
|
||||
}`
|
||||
} else { `
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; `
|
||||
$webClient = New-Object System.Net.WebClient; `
|
||||
$webClient.DownloadFile($source, $target); `
|
||||
} `
|
||||
} `
|
||||
`
|
||||
setx /M PATH $('C:\git\cmd;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin;C:\containerd\bin'); `
|
||||
setx /M PATH $('C:\git\cmd;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin'); `
|
||||
`
|
||||
Write-Host INFO: Downloading git...; `
|
||||
$location='https://www.nuget.org/api/v2/package/GitForWindows/'+$Env:GIT_VERSION; `
|
||||
Download-File $location C:\gitsetup.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading go...; `
|
||||
$dlGoVersion=$Env:GO_VERSION -replace '\.0$',''; `
|
||||
Download-File "https://go.dev/dl/go${dlGoVersion}.windows-amd64.zip" C:\go.zip; `
|
||||
Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') C:\go.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading compiler 1 of 3...; `
|
||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading compiler 2 of 3...; `
|
||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/runtime.zip C:\runtime.zip; `
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip C:\runtime.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading compiler 3 of 3...; `
|
||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/binutils.zip C:\binutils.zip; `
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip C:\binutils.zip; `
|
||||
`
|
||||
Write-Host INFO: Extracting git...; `
|
||||
Expand-Archive C:\gitsetup.zip C:\git-tmp; `
|
||||
@@ -258,58 +238,19 @@ RUN `
|
||||
Remove-Item C:\binutils.zip; `
|
||||
Remove-Item C:\gitsetup.zip; `
|
||||
`
|
||||
Write-Host INFO: Downloading containerd; `
|
||||
$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; `
|
||||
tar -xzf C:\containerd.tar.gz -C C:\containerd; `
|
||||
Remove-Item C:\containerd.tar.gz; `
|
||||
`
|
||||
# Ensure all directories exist that we will require below....
|
||||
$srcDir = """$Env:GOPATH`\src\github.com\docker\docker\bundles"""; `
|
||||
Write-Host INFO: Ensuring existence of directory $srcDir...; `
|
||||
New-Item -Force -ItemType Directory -Path $srcDir | Out-Null; `
|
||||
Write-Host INFO: Creating source directory...; `
|
||||
New-Item -ItemType Directory -Path C:\go\src\github.com\docker\docker | Out-Null; `
|
||||
`
|
||||
Write-Host INFO: Configuring git core.autocrlf...; `
|
||||
C:\git\cmd\git config --global core.autocrlf true;
|
||||
|
||||
RUN `
|
||||
Function Install-GoTestSum() { `
|
||||
$Env:GO111MODULE = 'on'; `
|
||||
$tmpGobin = "${Env:GOBIN_TMP}"; `
|
||||
$Env:GOBIN = """${Env:GOPATH}`\bin"""; `
|
||||
Write-Host "INFO: Installing gotestsum version $Env:GOTESTSUM_VERSION in $Env:GOBIN"; `
|
||||
&go install "gotest.tools/gotestsum@${Env:GOTESTSUM_VERSION}"; `
|
||||
$Env:GOBIN = "${tmpGobin}"; `
|
||||
$Env:GO111MODULE = 'off'; `
|
||||
if ($LASTEXITCODE -ne 0) { `
|
||||
Throw '"gotestsum install failed..."'; `
|
||||
} `
|
||||
} `
|
||||
C:\git\cmd\git config --global core.autocrlf true; `
|
||||
`
|
||||
Install-GoTestSum
|
||||
|
||||
RUN `
|
||||
Function Install-GoWinres() { `
|
||||
$Env:GO111MODULE = 'on'; `
|
||||
$tmpGobin = "${Env:GOBIN_TMP}"; `
|
||||
$Env:GOBIN = """${Env:GOPATH}`\bin"""; `
|
||||
Write-Host "INFO: Installing go-winres version $Env:GOWINRES_VERSION in $Env:GOBIN"; `
|
||||
&go install "github.com/tc-hib/go-winres@${Env:GOWINRES_VERSION}"; `
|
||||
$Env:GOBIN = "${tmpGobin}"; `
|
||||
$Env:GO111MODULE = 'off'; `
|
||||
if ($LASTEXITCODE -ne 0) { `
|
||||
Throw '"go-winres install failed..."'; `
|
||||
} `
|
||||
} `
|
||||
`
|
||||
Install-GoWinres
|
||||
Write-Host INFO: Completed
|
||||
|
||||
# Make PowerShell the default entrypoint
|
||||
ENTRYPOINT ["powershell.exe"]
|
||||
|
||||
# Set the working directory to the location of the sources
|
||||
WORKDIR ${GOPATH}\src\github.com\docker\docker
|
||||
WORKDIR C:\go\src\github.com\docker\docker
|
||||
|
||||
# Copy the sources into the container
|
||||
COPY . .
|
||||
|
||||
170
Jenkinsfile
vendored
170
Jenkinsfile
vendored
@@ -1,170 +0,0 @@
|
||||
#!groovy
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
options {
|
||||
buildDiscarder(logRotator(daysToKeepStr: '30'))
|
||||
timeout(time: 2, unit: 'HOURS')
|
||||
timestamps()
|
||||
}
|
||||
parameters {
|
||||
booleanParam(name: 'arm64', defaultValue: true, description: 'ARM (arm64) Build/Test')
|
||||
booleanParam(name: 'dco', defaultValue: true, description: 'Run the DCO check')
|
||||
}
|
||||
environment {
|
||||
DOCKER_BUILDKIT = '1'
|
||||
DOCKER_EXPERIMENTAL = '1'
|
||||
DOCKER_GRAPHDRIVER = 'overlay2'
|
||||
CHECK_CONFIG_COMMIT = '33a3680e08d1007e72c3b3f1454f823d8e9948ee'
|
||||
TESTDEBUG = '0'
|
||||
TIMEOUT = '120m'
|
||||
}
|
||||
stages {
|
||||
stage('pr-hack') {
|
||||
when { changeRequest() }
|
||||
steps {
|
||||
script {
|
||||
echo "Workaround for PR auto-cancel feature. Borrowed from https://issues.jenkins-ci.org/browse/JENKINS-43353"
|
||||
def buildNumber = env.BUILD_NUMBER as int
|
||||
if (buildNumber > 1) milestone(buildNumber - 1)
|
||||
milestone(buildNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('DCO-check') {
|
||||
when {
|
||||
beforeAgent true
|
||||
expression { params.dco }
|
||||
}
|
||||
agent { label 'arm64 && ubuntu-2004' }
|
||||
steps {
|
||||
sh '''
|
||||
docker run --rm \
|
||||
-v "$WORKSPACE:/workspace" \
|
||||
-e VALIDATE_REPO=${GIT_URL} \
|
||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
||||
alpine sh -c 'apk add --no-cache -q bash git openssh-client && git config --system --add safe.directory /workspace && cd /workspace && hack/validate/dco'
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
parallel {
|
||||
stage('arm64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
expression { params.arm64 }
|
||||
}
|
||||
agent { label 'arm64 && ubuntu-2004' }
|
||||
environment {
|
||||
TEST_SKIP_INTEGRATION_CLI = '1'
|
||||
}
|
||||
|
||||
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'
|
||||
sh 'docker info'
|
||||
sh '''
|
||||
echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}"
|
||||
curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \
|
||||
&& bash ${WORKSPACE}/check-config.sh || true
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage("Build dev image") {
|
||||
steps {
|
||||
sh 'docker build --force-rm -t docker:${GIT_COMMIT} .'
|
||||
}
|
||||
}
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
--name docker-pr$BUILD_NUMBER \
|
||||
-e DOCKER_EXPERIMENTAL \
|
||||
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e VALIDATE_REPO=${GIT_URL} \
|
||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
||||
docker:${GIT_COMMIT} \
|
||||
hack/test/unit
|
||||
'''
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Integration tests") {
|
||||
environment { TEST_SKIP_INTEGRATION_CLI = '1' }
|
||||
steps {
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
--name docker-pr$BUILD_NUMBER \
|
||||
-e DOCKER_EXPERIMENTAL \
|
||||
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e TESTDEBUG \
|
||||
-e TEST_SKIP_INTEGRATION_CLI \
|
||||
-e TIMEOUT \
|
||||
-e VALIDATE_REPO=${GIT_URL} \
|
||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
||||
docker:${GIT_COMMIT} \
|
||||
hack/make.sh \
|
||||
dynbinary \
|
||||
test-integration
|
||||
'''
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
sh '''
|
||||
echo "Ensuring container killed."
|
||||
docker rm -vf docker-pr$BUILD_NUMBER || true
|
||||
'''
|
||||
|
||||
sh '''
|
||||
echo "Chowning /workspace to jenkins user"
|
||||
docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
|
||||
'''
|
||||
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
|
||||
sh '''
|
||||
bundleName=arm64-integration
|
||||
echo "Creating ${bundleName}-bundles.tar.gz"
|
||||
# exclude overlay2 directories
|
||||
find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
|
||||
'''
|
||||
|
||||
archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
|
||||
}
|
||||
}
|
||||
cleanup {
|
||||
sh 'make clean'
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
LICENSE
2
LICENSE
@@ -176,7 +176,7 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2018 Docker, Inc.
|
||||
Copyright 2013-2017 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
147
MAINTAINERS
147
MAINTAINERS
@@ -24,17 +24,21 @@
|
||||
# subsystem maintainers accountable. If ownership is unclear, they are the de facto owners.
|
||||
|
||||
people = [
|
||||
"aaronlehmann",
|
||||
"akihirosuda",
|
||||
"anusha",
|
||||
"coolljt0725",
|
||||
"cpuguy83",
|
||||
"crosbymichael",
|
||||
"dnephin",
|
||||
"duglin",
|
||||
"estesp",
|
||||
"jhowardmsft",
|
||||
"johnstep",
|
||||
"justincormack",
|
||||
"kolyshkin",
|
||||
"mhbauer",
|
||||
"mlaventure",
|
||||
"runcom",
|
||||
"samuelkarp",
|
||||
"stevvooe",
|
||||
"thajeztah",
|
||||
"tianon",
|
||||
@@ -46,6 +50,15 @@
|
||||
"yongtang"
|
||||
]
|
||||
|
||||
[Org."Docs maintainers"]
|
||||
|
||||
# TODO Describe the docs maintainers role.
|
||||
|
||||
people = [
|
||||
"misty",
|
||||
"thajeztah"
|
||||
]
|
||||
|
||||
[Org.Curators]
|
||||
|
||||
# The curators help ensure that incoming issues and pull requests are properly triaged and
|
||||
@@ -61,16 +74,13 @@
|
||||
people = [
|
||||
"alexellis",
|
||||
"andrewhsu",
|
||||
"corhere",
|
||||
"anonymuse",
|
||||
"chanwit",
|
||||
"fntlnz",
|
||||
"gianarb",
|
||||
"ndeloof",
|
||||
"neersighted",
|
||||
"olljanat",
|
||||
"programmerq",
|
||||
"rheinwein",
|
||||
"ripcurld",
|
||||
"rumpl",
|
||||
"samwhited",
|
||||
"thajeztah"
|
||||
]
|
||||
|
||||
@@ -81,12 +91,6 @@
|
||||
# Thank you!
|
||||
|
||||
people = [
|
||||
# Aaron Lehmann was a maintainer for swarmkit, the registry, and the engine,
|
||||
# and contributed many improvements, features, and bugfixes in those areas,
|
||||
# among which "automated service rollbacks", templated secrets and configs,
|
||||
# and resumable image layer downloads.
|
||||
"aaronlehmann",
|
||||
|
||||
# Harald Albers is the mastermind behind the bash completion scripts for the
|
||||
# Docker CLI. The completion scripts moved to the Docker CLI repository, so
|
||||
# you can now find him perform his magic in the https://github.com/docker/cli repository.
|
||||
@@ -106,30 +110,6 @@
|
||||
# and tweets as @calavera.
|
||||
"calavera",
|
||||
|
||||
# Michael Crosby was "chief maintainer" of the Docker project.
|
||||
# During his time as a maintainer, Michael contributed to many
|
||||
# milestones of the project; he was release captain of Docker v1.0.0,
|
||||
# started the development of "libcontainer" (what later became runc)
|
||||
# and containerd, as well as demoing cool hacks such as live migrating
|
||||
# a game server container with checkpoint/restore.
|
||||
#
|
||||
# Michael is currently a maintainer of containerd, but you may see
|
||||
# him around in other projects on GitHub.
|
||||
"crosbymichael",
|
||||
|
||||
# Before becoming a maintainer, Daniel Nephin was a core contributor
|
||||
# to "Fig" (now known as Docker Compose). As a maintainer for both the
|
||||
# Engine and Docker CLI, Daniel contributed many features, among which
|
||||
# the `docker stack` commands, allowing users to deploy their Docker
|
||||
# Compose projects as a Swarm service.
|
||||
"dnephin",
|
||||
|
||||
# Doug Davis contributed many features and fixes for the classic builder,
|
||||
# such as "wildcard" copy, the dockerignore file, custom paths/names
|
||||
# for the Dockerfile, as well as enhancements to the API and documentation.
|
||||
# Follow Doug on Twitter, where he tweets as @duginabox.
|
||||
"duglin",
|
||||
|
||||
# As a maintainer, Erik was responsible for the "builder", and
|
||||
# started the first designs for the new networking model in
|
||||
# Docker. Erik is now working on all kinds of plugins for Docker
|
||||
@@ -138,7 +118,7 @@
|
||||
# still stumble into him in our issue tracker, or on IRC.
|
||||
"erikh",
|
||||
|
||||
# Evan Hazlett is the creator of the Shipyard and Interlock open source projects,
|
||||
# Evan Hazlett is the creator of of the Shipyard and Interlock open source projects,
|
||||
# and the author of "Orca", which became the foundation of Docker Universal Control
|
||||
# Plane (UCP). As a maintainer, Evan helped integrating SwarmKit (secrets, tasks)
|
||||
# into the Docker engine.
|
||||
@@ -180,16 +160,6 @@
|
||||
# check out her open source projects on GitHub https://github.com/jessfraz (a must-try).
|
||||
"jessfraz",
|
||||
|
||||
# As a maintainer, John Howard managed to make the impossible possible;
|
||||
# to run Docker on Windows. After facing many challenges, teaching
|
||||
# fellow-maintainers that 'Windows is not Linux', and many changes in
|
||||
# Windows Server to facilitate containers, native Windows containers
|
||||
# saw the light of day in 2015.
|
||||
#
|
||||
# John is now enjoying life without containers: playing piano, painting,
|
||||
# and walking his dogs, but you may occasionally see him drop by on GitHub.
|
||||
"lowenna",
|
||||
|
||||
# Alexander Morozov contributed many features to Docker, worked on the premise of
|
||||
# what later became containerd (and worked on that too), and made a "stupid" Go
|
||||
# vendor tool specifically for docker/docker needs: vndr (https://github.com/LK4D4/vndr).
|
||||
@@ -203,13 +173,6 @@
|
||||
# Swarm mode networking.
|
||||
"mavenugo",
|
||||
|
||||
# As a maintainer, Kenfe-Mickaël Laventure worked on the container runtime,
|
||||
# integrating containerd 1.0 with the daemon, and adding support for custom
|
||||
# OCI runtimes, as well as implementing the `docker prune` subcommands,
|
||||
# which was a welcome feature to be added. You can keep up with Mickaél on
|
||||
# Twitter (@kmlaventure).
|
||||
"mlaventure",
|
||||
|
||||
# As a docs maintainer, Mary Anthony contributed greatly to the Docker
|
||||
# docs. She wrote the Docker Contributor Guide and Getting Started
|
||||
# Guides. She helped create a doc build system independent of
|
||||
@@ -284,7 +247,7 @@
|
||||
|
||||
[people.akihirosuda]
|
||||
Name = "Akihiro Suda"
|
||||
Email = "akihiro.suda.cz@hco.ntt.co.jp"
|
||||
Email = "suda.akihiro@lab.ntt.co.jp"
|
||||
GitHub = "AkihiroSuda"
|
||||
|
||||
[people.aluzzardi]
|
||||
@@ -302,6 +265,11 @@
|
||||
Email = "andrewhsu@docker.com"
|
||||
GitHub = "andrewhsu"
|
||||
|
||||
[people.anonymuse]
|
||||
Name = "Jesse White"
|
||||
Email = "anonymuse@gmail.com"
|
||||
GitHub = "anonymuse"
|
||||
|
||||
[people.anusha]
|
||||
Name = "Anusha Ragunathan"
|
||||
Email = "anusha@docker.com"
|
||||
@@ -317,16 +285,16 @@
|
||||
Email = "leijitang@huawei.com"
|
||||
GitHub = "coolljt0725"
|
||||
|
||||
[people.corhere]
|
||||
Name = "Cory Snider"
|
||||
Email = "csnider@mirantis.com"
|
||||
GitHub = "corhere"
|
||||
|
||||
[people.cpuguy83]
|
||||
Name = "Brian Goff"
|
||||
Email = "cpuguy83@gmail.com"
|
||||
GitHub = "cpuguy83"
|
||||
|
||||
[people.chanwit]
|
||||
Name = "Chanwit Kaewkasi"
|
||||
Email = "chanwit@gmail.com"
|
||||
GitHub = "chanwit"
|
||||
|
||||
[people.crosbymichael]
|
||||
Name = "Michael Crosby"
|
||||
Email = "crosbymichael@gmail.com"
|
||||
@@ -377,6 +345,11 @@
|
||||
Email = "james@lovedthanlost.net"
|
||||
GitHub = "jamtur01"
|
||||
|
||||
[people.jhowardmsft]
|
||||
Name = "John Howard"
|
||||
Email = "jhoward@microsoft.com"
|
||||
GitHub = "jhowardmsft"
|
||||
|
||||
[people.jessfraz]
|
||||
Name = "Jessie Frazelle"
|
||||
Email = "jess@linux.com"
|
||||
@@ -392,21 +365,11 @@
|
||||
Email = "justin.cormack@docker.com"
|
||||
GitHub = "justincormack"
|
||||
|
||||
[people.kolyshkin]
|
||||
Name = "Kir Kolyshkin"
|
||||
Email = "kolyshkin@gmail.com"
|
||||
GitHub = "kolyshkin"
|
||||
|
||||
[people.lk4d4]
|
||||
Name = "Alexander Morozov"
|
||||
Email = "lk4d4@docker.com"
|
||||
GitHub = "lk4d4"
|
||||
|
||||
[people.lowenna]
|
||||
Name = "John Howard"
|
||||
Email = "github@lowenna.com"
|
||||
GitHub = "lowenna"
|
||||
|
||||
[people.mavenugo]
|
||||
Name = "Madhu Venugopal"
|
||||
Email = "madhu@docker.com"
|
||||
@@ -417,6 +380,11 @@
|
||||
Email = "mbauer@us.ibm.com"
|
||||
GitHub = "mhbauer"
|
||||
|
||||
[people.misty]
|
||||
Name = "Misty Stanley-Jones"
|
||||
Email = "misty@docker.com"
|
||||
GitHub = "mistyhacks"
|
||||
|
||||
[people.mlaventure]
|
||||
Name = "Kenfe-Mickaël Laventure"
|
||||
Email = "mickael.laventure@gmail.com"
|
||||
@@ -432,51 +400,26 @@
|
||||
Email = "mrjana@docker.com"
|
||||
GitHub = "mrjana"
|
||||
|
||||
[people.ndeloof]
|
||||
Name = "Nicolas De Loof"
|
||||
Email = "nicolas.deloof@gmail.com"
|
||||
GitHub = "ndeloof"
|
||||
|
||||
[people.neersighted]
|
||||
Name = "Bjorn Neergaard"
|
||||
Email = "bneergaard@mirantis.com"
|
||||
GitHub = "neersighted"
|
||||
|
||||
[people.olljanat]
|
||||
Name = "Olli Janatuinen"
|
||||
Email = "olli.janatuinen@gmail.com"
|
||||
GitHub = "olljanat"
|
||||
|
||||
[people.programmerq]
|
||||
Name = "Jeff Anderson"
|
||||
Email = "jeff@docker.com"
|
||||
GitHub = "programmerq"
|
||||
|
||||
[people.rheinwein]
|
||||
Name = "Laura Frank"
|
||||
Email = "laura@codeship.com"
|
||||
GitHub = "rheinwein"
|
||||
|
||||
[people.ripcurld]
|
||||
Name = "Boaz Shuster"
|
||||
Email = "ripcurld.github@gmail.com"
|
||||
GitHub = "ripcurld"
|
||||
|
||||
[people.rumpl]
|
||||
Name = "Djordje Lukic"
|
||||
Email = "djordje.lukic@docker.com"
|
||||
GitHub = "rumpl"
|
||||
|
||||
[people.runcom]
|
||||
Name = "Antonio Murdaca"
|
||||
Email = "runcom@redhat.com"
|
||||
GitHub = "runcom"
|
||||
|
||||
[people.samuelkarp]
|
||||
Name = "Samuel Karp"
|
||||
Email = "me@samuelkarp.com"
|
||||
GitHub = "samuelkarp"
|
||||
|
||||
[people.samwhited]
|
||||
Name = "Sam Whited"
|
||||
Email = "sam@samwhited.com"
|
||||
GitHub = "samwhited"
|
||||
|
||||
[people.shykes]
|
||||
Name = "Solomon Hykes"
|
||||
Email = "solomon@docker.com"
|
||||
|
||||
171
Makefile
171
Makefile
@@ -1,11 +1,10 @@
|
||||
.PHONY: all binary dynbinary build cross help install manpages run shell test test-docker-py test-integration test-unit validate validate-% win
|
||||
|
||||
DOCKER ?= docker
|
||||
BUILDX ?= $(DOCKER) buildx
|
||||
.PHONY: all binary dynbinary build cross help init-go-pkg-cache install manpages run shell test test-docker-py test-integration test-unit validate win
|
||||
|
||||
# set the graph driver as the current graphdriver if not set
|
||||
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
|
||||
export DOCKER_GRAPHDRIVER
|
||||
DOCKER_INCREMENTAL_BINARY := $(if $(DOCKER_INCREMENTAL_BINARY),$(DOCKER_INCREMENTAL_BINARY),1)
|
||||
export DOCKER_INCREMENTAL_BINARY
|
||||
|
||||
# get OS/Arch of docker engine
|
||||
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH}')
|
||||
@@ -14,12 +13,6 @@ DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $$
|
||||
DOCKER_GITCOMMIT := $(shell git rev-parse --short HEAD || echo unsupported)
|
||||
export DOCKER_GITCOMMIT
|
||||
|
||||
# allow overriding the repository and branch that validation scripts are running
|
||||
# against these are used in hack/validate/.validate to check what changed in the PR.
|
||||
export VALIDATE_REPO
|
||||
export VALIDATE_BRANCH
|
||||
export VALIDATE_ORIGIN_BRANCH
|
||||
|
||||
# env vars passed through directly to Docker's build scripts
|
||||
# to allow things like `make KEEPBUNDLE=1 binary` easily
|
||||
# `project/PACKAGERS.md` have some limited documentation of some of these
|
||||
@@ -31,11 +24,12 @@ export VALIDATE_ORIGIN_BRANCH
|
||||
# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary
|
||||
#
|
||||
DOCKER_ENVS := \
|
||||
-e DOCKER_CROSSPLATFORMS \
|
||||
-e BUILD_APT_MIRROR \
|
||||
-e BUILDFLAGS \
|
||||
-e KEEPBUNDLE \
|
||||
-e DOCKER_BUILD_ARGS \
|
||||
-e DOCKER_BUILD_GOGC \
|
||||
-e DOCKER_BUILD_OPTS \
|
||||
-e DOCKER_BUILD_PKGS \
|
||||
-e DOCKER_BUILDKIT \
|
||||
-e DOCKER_BASH_COMPLETION_PATH \
|
||||
@@ -44,71 +38,66 @@ DOCKER_ENVS := \
|
||||
-e DOCKER_EXPERIMENTAL \
|
||||
-e DOCKER_GITCOMMIT \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e DOCKER_INCREMENTAL_BINARY \
|
||||
-e DOCKER_LDFLAGS \
|
||||
-e DOCKER_PORT \
|
||||
-e DOCKER_REMAP_ROOT \
|
||||
-e DOCKER_ROOTLESS \
|
||||
-e DOCKER_STORAGE_OPTS \
|
||||
-e DOCKER_TEST_HOST \
|
||||
-e DOCKER_USERLANDPROXY \
|
||||
-e DOCKERD_ARGS \
|
||||
-e DELVE_PORT \
|
||||
-e GITHUB_ACTIONS \
|
||||
-e TEST_FORCE_VALIDATE \
|
||||
-e TEST_INTEGRATION_DIR \
|
||||
-e TEST_INTEGRATION_FAIL_FAST \
|
||||
-e TEST_SKIP_INTEGRATION \
|
||||
-e TEST_SKIP_INTEGRATION_CLI \
|
||||
-e TESTCOVERAGE \
|
||||
-e TESTDEBUG \
|
||||
-e TESTDIRS \
|
||||
-e TESTFLAGS \
|
||||
-e TESTFLAGS_INTEGRATION \
|
||||
-e TESTFLAGS_INTEGRATION_CLI \
|
||||
-e TEST_FILTER \
|
||||
-e TIMEOUT \
|
||||
-e VALIDATE_REPO \
|
||||
-e VALIDATE_BRANCH \
|
||||
-e VALIDATE_ORIGIN_BRANCH \
|
||||
-e HTTP_PROXY \
|
||||
-e HTTPS_PROXY \
|
||||
-e NO_PROXY \
|
||||
-e http_proxy \
|
||||
-e https_proxy \
|
||||
-e no_proxy \
|
||||
-e VERSION \
|
||||
-e PLATFORM \
|
||||
-e DEFAULT_PRODUCT_LICENSE \
|
||||
-e PRODUCT \
|
||||
-e PACKAGER_NAME
|
||||
-e PRODUCT
|
||||
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
|
||||
|
||||
# to allow `make BIND_DIR=. shell` or `make BIND_DIR= test`
|
||||
# (default to no bind mount if DOCKER_HOST is set)
|
||||
# note: BINDDIR is supported for backwards-compatibility here
|
||||
BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles))
|
||||
|
||||
# DOCKER_MOUNT can be overriden, but use at your own risk!
|
||||
ifndef DOCKER_MOUNT
|
||||
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)")
|
||||
DOCKER_MOUNT := $(if $(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT):$(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT))
|
||||
|
||||
# This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs.
|
||||
# The volume will be cleaned up when the container is removed due to `--rm`.
|
||||
# Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set.
|
||||
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles) -v "$(CURDIR)/.git:/go/src/github.com/docker/docker/.git"
|
||||
|
||||
DOCKER_MOUNT_CACHE := -v docker-dev-cache:/root/.cache -v docker-mod-cache:/go/pkg/mod/
|
||||
DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,)
|
||||
DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,)
|
||||
DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_CACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION)
|
||||
endif # ifndef DOCKER_MOUNT
|
||||
|
||||
# This allows to set the docker-dev container name
|
||||
DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
|
||||
|
||||
DOCKER_IMAGE := docker-dev
|
||||
DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
|
||||
DELVE_PORT_FORWARD := $(if $(DELVE_PORT),-p "$(DELVE_PORT)",)
|
||||
# enable package cache if DOCKER_INCREMENTAL_BINARY and DOCKER_MOUNT (i.e.DOCKER_HOST) are set
|
||||
PKGCACHE_MAP := gopath:/go/pkg goroot-linux_amd64:/usr/local/go/pkg/linux_amd64 goroot-linux_amd64_netgo:/usr/local/go/pkg/linux_amd64_netgo
|
||||
PKGCACHE_VOLROOT := dockerdev-go-pkg-cache
|
||||
PKGCACHE_VOL := $(if $(PKGCACHE_DIR),$(CURDIR)/$(PKGCACHE_DIR)/,$(PKGCACHE_VOLROOT)-)
|
||||
DOCKER_MOUNT_PKGCACHE := $(if $(DOCKER_INCREMENTAL_BINARY),$(shell echo $(PKGCACHE_MAP) | sed -E 's@([^ ]*)@-v "$(PKGCACHE_VOL)\1"@g'),)
|
||||
DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,)
|
||||
DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,)
|
||||
DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_PKGCACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION)
|
||||
|
||||
DOCKER_FLAGS := $(DOCKER) run --rm --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) $(DELVE_PORT_FORWARD)
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
|
||||
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
|
||||
DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
|
||||
|
||||
DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD)
|
||||
BUILD_APT_MIRROR := $(if $(DOCKER_BUILD_APT_MIRROR),--build-arg APT_MIRROR=$(DOCKER_BUILD_APT_MIRROR))
|
||||
export BUILD_APT_MIRROR
|
||||
|
||||
SWAGGER_DOCS_PORT ?= 9000
|
||||
|
||||
INTEGRATION_CLI_MASTER_IMAGE := $(if $(INTEGRATION_CLI_MASTER_IMAGE), $(INTEGRATION_CLI_MASTER_IMAGE), integration-cli-master)
|
||||
INTEGRATION_CLI_WORKER_IMAGE := $(if $(INTEGRATION_CLI_WORKER_IMAGE), $(INTEGRATION_CLI_WORKER_IMAGE), integration-cli-worker)
|
||||
|
||||
define \n
|
||||
|
||||
|
||||
@@ -122,88 +111,61 @@ ifeq ($(INTERACTIVE), 1)
|
||||
DOCKER_FLAGS += -t
|
||||
endif
|
||||
|
||||
# on GitHub Runners input device is not a TTY but we allocate a pseudo-one,
|
||||
# otherwise keep STDIN open even if not attached if not a GitHub Runner.
|
||||
ifeq ($(GITHUB_ACTIONS),true)
|
||||
DOCKER_FLAGS += -t
|
||||
else
|
||||
DOCKER_FLAGS += -i
|
||||
endif
|
||||
|
||||
DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
||||
|
||||
DOCKER_BUILD_ARGS += --build-arg=GO_VERSION
|
||||
ifdef DOCKER_SYSTEMD
|
||||
DOCKER_BUILD_ARGS += --build-arg=SYSTEMD=true
|
||||
endif
|
||||
|
||||
BUILD_OPTS := ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -f "$(DOCKERFILE)"
|
||||
BUILD_CMD := $(BUILDX) build
|
||||
BAKE_CMD := $(BUILDX) bake
|
||||
|
||||
default: binary
|
||||
|
||||
all: build ## validate all checks, build linux binaries, run all tests,\ncross build non-linux binaries, and generate archives
|
||||
all: build ## validate all checks, build linux binaries, run all tests\ncross build non-linux binaries and generate archives
|
||||
$(DOCKER_RUN_DOCKER) bash -c 'hack/validate/default && hack/make.sh'
|
||||
|
||||
binary: bundles ## build statically linked linux binaries
|
||||
$(BAKE_CMD) binary
|
||||
binary: build ## build the linux binaries
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||
|
||||
dynbinary: bundles ## build dynamically linked linux binaries
|
||||
$(BAKE_CMD) dynbinary
|
||||
dynbinary: build ## build the linux dynbinaries
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary
|
||||
|
||||
cross: bundles ## cross build the binaries
|
||||
$(BAKE_CMD) binary-cross
|
||||
build: bundles init-go-pkg-cache
|
||||
$(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n})
|
||||
docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-cache
|
||||
clean: clean-pkg-cache-vol ## clean up cached resources
|
||||
|
||||
.PHONY: clean-cache
|
||||
clean-cache: ## remove the docker volumes that are used for caching in the dev-container
|
||||
docker volume rm -f docker-dev-cache docker-mod-cache
|
||||
clean-pkg-cache-vol:
|
||||
@- $(foreach mapping,$(PKGCACHE_MAP), \
|
||||
$(shell docker volume rm $(PKGCACHE_VOLROOT)-$(shell echo $(mapping) | awk -F':/' '{ print $$1 }') > /dev/null 2>&1) \
|
||||
)
|
||||
|
||||
cross: build ## cross build the binaries for darwin, freebsd and\nwindows
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
|
||||
|
||||
help: ## this help
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
||||
init-go-pkg-cache:
|
||||
$(if $(PKGCACHE_DIR), mkdir -p $(shell echo $(PKGCACHE_MAP) | sed -E 's@([^: ]*):[^ ]*@$(PKGCACHE_DIR)/\1@g'))
|
||||
|
||||
install: ## install the linux binaries
|
||||
KEEPBUNDLE=1 hack/make.sh install-binary
|
||||
|
||||
run: build ## run the docker daemon in a container
|
||||
$(DOCKER_RUN_DOCKER) sh -c "KEEPBUNDLE=1 hack/make.sh install-binary run"
|
||||
|
||||
.PHONY: build
|
||||
ifeq ($(BIND_DIR), .)
|
||||
build: shell_target := --target=dev-base
|
||||
else
|
||||
build: shell_target := --target=dev
|
||||
endif
|
||||
build: bundles
|
||||
$(BUILD_CMD) $(BUILD_OPTS) $(shell_target) --load -t "$(DOCKER_IMAGE)" .
|
||||
|
||||
shell: build ## start a shell inside the build env
|
||||
shell: build ## start a shell inside the build env
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
test: build test-unit ## run the unit, integration and docker-py tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration test-docker-py
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-integration test-docker-py
|
||||
|
||||
test-docker-py: build ## run the docker-py tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
||||
|
||||
test-integration-cli: test-integration ## (DEPRECATED) use test-integration
|
||||
|
||||
ifneq ($(and $(TEST_SKIP_INTEGRATION),$(TEST_SKIP_INTEGRATION_CLI)),)
|
||||
test-integration:
|
||||
@echo Both integrations suites skipped per environment variables
|
||||
else
|
||||
test-integration: build ## run the integration tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration
|
||||
endif
|
||||
|
||||
test-integration-flaky: build ## run the stress test for all new integration tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration-flaky
|
||||
|
||||
test-unit: build ## run the unit tests
|
||||
$(DOCKER_RUN_DOCKER) hack/test/unit
|
||||
@@ -211,11 +173,8 @@ test-unit: build ## run the unit tests
|
||||
validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isolation, golint, tests, tomls, go vet and vendor
|
||||
$(DOCKER_RUN_DOCKER) hack/validate/all
|
||||
|
||||
validate-%: build ## validate specific check
|
||||
$(DOCKER_RUN_DOCKER) hack/validate/$*
|
||||
|
||||
win: bundles ## cross build the binary for windows
|
||||
$(BAKE_CMD) --set *.platform=windows/amd64 binary
|
||||
win: build ## cross build the binary for windows
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh win
|
||||
|
||||
.PHONY: swagger-gen
|
||||
swagger-gen:
|
||||
@@ -231,4 +190,20 @@ swagger-docs: ## preview the API documentation
|
||||
@docker run --rm -v $(PWD)/api/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
|
||||
-e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \
|
||||
-p $(SWAGGER_DOCS_PORT):80 \
|
||||
bfirsh/redoc:1.14.0
|
||||
bfirsh/redoc:1.6.2
|
||||
|
||||
build-integration-cli-on-swarm: build ## build images and binary for running integration-cli on Swarm in parallel
|
||||
@echo "Building hack/integration-cli-on-swarm (if build fails, please refer to hack/integration-cli-on-swarm/README.md)"
|
||||
go build -buildmode=pie -o ./hack/integration-cli-on-swarm/integration-cli-on-swarm ./hack/integration-cli-on-swarm/host
|
||||
@echo "Building $(INTEGRATION_CLI_MASTER_IMAGE)"
|
||||
docker build -t $(INTEGRATION_CLI_MASTER_IMAGE) hack/integration-cli-on-swarm/agent
|
||||
# For worker, we don't use `docker build` so as to enable DOCKER_INCREMENTAL_BINARY and so on
|
||||
@echo "Building $(INTEGRATION_CLI_WORKER_IMAGE) from $(DOCKER_IMAGE)"
|
||||
$(eval tmp := integration-cli-worker-tmp)
|
||||
# We mount pkgcache, but not bundle (bundle needs to be baked into the image)
|
||||
# For avoiding bakings DOCKER_GRAPHDRIVER and so on to image, we cannot use $(DOCKER_ENVS) here
|
||||
docker run -t -d --name $(tmp) -e DOCKER_GITCOMMIT -e BUILDFLAGS -e DOCKER_INCREMENTAL_BINARY --privileged $(DOCKER_MOUNT_PKGCACHE) $(DOCKER_IMAGE) top
|
||||
docker exec $(tmp) hack/make.sh build-integration-test-binary dynbinary
|
||||
docker exec $(tmp) go build -buildmode=pie -o /worker github.com/docker/docker/hack/integration-cli-on-swarm/agent/worker
|
||||
docker commit -c 'ENTRYPOINT ["/worker"]' $(tmp) $(INTEGRATION_CLI_WORKER_IMAGE)
|
||||
docker rm -f $(tmp)
|
||||
|
||||
2
NOTICE
2
NOTICE
@@ -3,7 +3,7 @@ Copyright 2012-2017 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||
|
||||
This product contains software (https://github.com/creack/pty) developed
|
||||
This product contains software (https://github.com/kr/pty) developed
|
||||
by Keith Rarick, licensed under the MIT License.
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
91
ROADMAP.md
91
ROADMAP.md
@@ -35,83 +35,34 @@ issue, in the Slack channel, or in person at the Moby Summits that happen every
|
||||
|
||||
## 1.1 Runtime improvements
|
||||
|
||||
Over time we have accumulated a lot of functionality in the container runtime
|
||||
aspect of Moby while also growing in other areas. Much of the container runtime
|
||||
pieces are now duplicated work available in other, lower level components such
|
||||
as [containerd](https://containerd.io).
|
||||
We introduced [`runC`](https://runc.io) as a standalone low-level tool for container
|
||||
execution in 2015, the first stage in spinning out parts of the Engine into standalone tools.
|
||||
|
||||
Moby currently only utilizes containerd for basic runtime state management, e.g. starting
|
||||
and stopping a container, which is what the pre-containerd 1.0 daemon provided.
|
||||
Now that containerd is a full-fledged container runtime which supports full
|
||||
container life-cycle management, we would like to start relying more on containerd
|
||||
and removing the bits in Moby which are now duplicated. This will necessitate
|
||||
a significant effort to refactor and even remove large parts of Moby's codebase.
|
||||
As runC continued evolving, and the OCI specification along with it, we created
|
||||
[`containerd`](https://github.com/containerd/containerd), a daemon to control and monitor `runC`.
|
||||
In late 2016 this was relaunched as the `containerd` 1.0 track, aiming to provide a common runtime
|
||||
for the whole spectrum of container systems, including Kubernetes, with wide community support.
|
||||
This change meant that there was an increased scope for `containerd`, including image management
|
||||
and storage drivers.
|
||||
|
||||
Tracking issues:
|
||||
Moby will rely on a long-running `containerd` companion daemon for all container execution
|
||||
related operations. This could open the door in the future for Engine restarts without interrupting
|
||||
running containers. The switch over to containerd 1.0 is an important goal for the project, and
|
||||
will result in a significant simplification of the functions implemented in this repository.
|
||||
|
||||
- [#38043](https://github.com/moby/moby/issues/38043) Proposal: containerd image integration
|
||||
|
||||
## 1.2 Image Builder
|
||||
|
||||
Work is ongoing to integrate [BuildKit](https://github.com/moby/buildkit) into
|
||||
Moby and replace the "v0" build implementation. Buildkit offers better cache
|
||||
management, parallelizable build steps, and better extensibility while also
|
||||
keeping builds portable, a chief tenent of Moby's builder.
|
||||
|
||||
Upon completion of this effort, users will have a builder that performs better
|
||||
while also being more extensible, enabling users to provide their own custom
|
||||
syntax which can be either Dockerfile-like or something completely different.
|
||||
|
||||
See [buildpacks on buildkit](https://github.com/tonistiigi/buildkit-pack) as an
|
||||
example of this extensibility.
|
||||
|
||||
New features for the builder and Dockerfile should be implemented first in the
|
||||
BuildKit backend using an external Dockerfile implementation from the container
|
||||
images. This allows everyone to test and evaluate the feature without upgrading
|
||||
their daemon. New features should go to the experimental channel first, and can be
|
||||
part of the `docker/dockerfile:experimental` image. From there they graduate to
|
||||
`docker/dockerfile:latest` and binary releases. The Dockerfile frontend source
|
||||
code is temporarily located at
|
||||
[https://github.com/moby/buildkit/tree/master/frontend/dockerfile](https://github.com/moby/buildkit/tree/master/frontend/dockerfile)
|
||||
with separate new features defined with go build tags.
|
||||
|
||||
Tracking issues:
|
||||
|
||||
- [#32925](https://github.com/moby/moby/issues/32925) discussion: builder future: buildkit
|
||||
|
||||
## 1.3 Rootless Mode
|
||||
|
||||
Running the daemon requires elevated privileges for many tasks. We would like to
|
||||
support running the daemon as a normal, unprivileged user without requiring `suid`
|
||||
binaries.
|
||||
|
||||
Tracking issues:
|
||||
|
||||
- [#37375](https://github.com/moby/moby/issues/37375) Proposal: allow running `dockerd` as an unprivileged user (aka rootless mode)
|
||||
|
||||
## 1.4 Testing
|
||||
|
||||
Moby has many tests, both unit and integration. Moby needs more tests which can
|
||||
cover the full spectrum functionality and edge cases out there.
|
||||
|
||||
Tests in the `integration-cli` folder should also be migrated into (both in
|
||||
location and style) the `integration` folder. These newer tests are simpler to
|
||||
run in isolation, simpler to read, simpler to write, and more fully exercise the
|
||||
API. Meanwhile tests of the docker CLI should generally live in docker/cli.
|
||||
|
||||
Tracking issues:
|
||||
|
||||
- [#32866](https://github.com/moby/moby/issues/32866) Replace integration-cli suite with API test suite
|
||||
|
||||
## 1.5 Internal decoupling
|
||||
## 1.2 Internal decoupling
|
||||
|
||||
A lot of work has been done in trying to decouple Moby internals. This process of creating
|
||||
standalone projects with a well defined function that attract a dedicated community should continue.
|
||||
As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit)
|
||||
as the next standalone component.
|
||||
|
||||
We see gRPC as the natural communication layer between decoupled components.
|
||||
|
||||
In addition to pushing out large components into other projects, much of the
|
||||
internal code structure, and in particular the
|
||||
["Daemon"](https://godoc.org/github.com/docker/docker/daemon#Daemon) object,
|
||||
should be split into smaller, more manageable, and more testable components.
|
||||
## 1.3 Custom assembly tooling
|
||||
|
||||
We have been prototyping the Moby [assembly tool](https://github.com/moby/tool) which was originally
|
||||
developed for LinuxKit and intend to turn it into a more generic packaging and assembly mechanism
|
||||
that can build not only the default version of Moby, as distribution packages or other useful forms,
|
||||
but can also build very different container systems, themselves built of cooperating daemons built in
|
||||
and running in containers. We intend to merge this functionality into this repo.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Reporting security issues
|
||||
|
||||
The Moby maintainers take security seriously. If you discover a security issue, please bring it to their attention right away!
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to security@docker.com.
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it. We also like to send gifts—if you're into schwag, make sure to let us know. We currently do not offer a paid security bounty program, but are not ruling it out in the future.
|
||||
45
TESTING.md
45
TESTING.md
@@ -28,7 +28,7 @@ Most code changes will fall into one of the following categories.
|
||||
### Writing tests for new features
|
||||
|
||||
New code should be covered by unit tests. If the code is difficult to test with
|
||||
unit tests, then that is a good sign that it should be refactored to make it
|
||||
a unit tests then that is a good sign that it should be refactored to make it
|
||||
easier to reuse and maintain. Consider accepting unexported interfaces instead
|
||||
of structs so that fakes can be provided for dependencies.
|
||||
|
||||
@@ -44,23 +44,16 @@ case. Error cases should be handled by unit tests.
|
||||
|
||||
Bugs fixes should include a unit test case which exercises the bug.
|
||||
|
||||
A bug fix may also include new assertions in existing integration tests for the
|
||||
A bug fix may also include new assertions in an existing integration tests for the
|
||||
API endpoint.
|
||||
|
||||
### Writing new integration tests
|
||||
|
||||
Note the `integration-cli` tests are deprecated; new tests will be rejected by
|
||||
the CI.
|
||||
|
||||
Instead, implement new tests under `integration/`.
|
||||
|
||||
### Integration tests environment considerations
|
||||
|
||||
When adding new tests or modifying existing tests under `integration/`, testing
|
||||
When adding new tests or modifying existing test under `integration/`, testing
|
||||
environment should be properly considered. `skip.If` from
|
||||
[gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the
|
||||
test run conditionally. Full testing environment conditions can be found at
|
||||
[environment.go](https://github.com/moby/moby/blob/6b6eeed03b963a27085ea670f40cd5ff8a61f32e/testutil/environment/environment.go)
|
||||
[environment.go](https://github.com/moby/moby/blob/cb37987ee11655ed6bbef663d245e55922354c68/internal/test/environment/environment.go)
|
||||
|
||||
Here is a quick example. If the test needs to interact with a docker daemon on
|
||||
the same host, the following condition should be checked within the test code
|
||||
@@ -74,8 +67,6 @@ If a remote daemon is detected, the test will be skipped.
|
||||
|
||||
## Running tests
|
||||
|
||||
### Unit Tests
|
||||
|
||||
To run the unit test suite:
|
||||
|
||||
```
|
||||
@@ -91,36 +82,8 @@ The following environment variables may be used to run a subset of tests:
|
||||
* `TESTFLAGS` - flags passed to `go test`, to run tests which match a pattern
|
||||
use `TESTFLAGS="-test.run TestNameOrPrefix"`
|
||||
|
||||
### Integration Tests
|
||||
|
||||
To run the integration test suite:
|
||||
|
||||
```
|
||||
make test-integration
|
||||
```
|
||||
|
||||
This make target runs both the "integration" suite and the "integration-cli"
|
||||
suite.
|
||||
|
||||
You can specify which integration test dirs to build and run by specifying
|
||||
the list of dirs in the TEST_INTEGRATION_DIR environment variable.
|
||||
|
||||
You can also explicitly skip either suite by setting (any value) in
|
||||
TEST_SKIP_INTEGRATION and/or TEST_SKIP_INTEGRATION_CLI environment variables.
|
||||
|
||||
Flags specific to each suite can be set in the TESTFLAGS_INTEGRATION and
|
||||
TESTFLAGS_INTEGRATION_CLI environment variables.
|
||||
|
||||
If all you want is to specify a test filter to run, you can set the
|
||||
`TEST_FILTER` environment variable. This ends up getting passed directly to `go
|
||||
test -run` (or `go test -check-f`, depending on the test suite). It will also
|
||||
automatically set the other above mentioned environment variables accordingly.
|
||||
|
||||
### Go Version
|
||||
|
||||
You can change a version of golang used for building stuff that is being tested
|
||||
by setting `GO_VERSION` variable, for example:
|
||||
|
||||
```
|
||||
make GO_VERSION=1.12.8 test
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion = "1.42"
|
||||
DefaultVersion = "1.39"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package api // import "github.com/docker/docker/api"
|
||||
|
||||
@@ -3,19 +3,17 @@ package build // import "github.com/docker/docker/api/server/backend/build"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/builder"
|
||||
buildkit "github.com/docker/docker/builder/builder-next"
|
||||
daemonevents "github.com/docker/docker/daemon/events"
|
||||
"github.com/docker/docker/builder/fscache"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// ImageComponent provides an interface for working with images
|
||||
@@ -32,21 +30,14 @@ type Builder interface {
|
||||
// Backend provides build functionality to the API router
|
||||
type Backend struct {
|
||||
builder Builder
|
||||
fsCache *fscache.FSCache
|
||||
imageComponent ImageComponent
|
||||
buildkit *buildkit.Builder
|
||||
eventsService *daemonevents.Events
|
||||
}
|
||||
|
||||
// NewBackend creates a new build backend from components
|
||||
func NewBackend(components ImageComponent, builder Builder, buildkit *buildkit.Builder, es *daemonevents.Events) (*Backend, error) {
|
||||
return &Backend{imageComponent: components, builder: builder, buildkit: buildkit, eventsService: es}, nil
|
||||
}
|
||||
|
||||
// RegisterGRPC registers buildkit controller to the grpc server.
|
||||
func (b *Backend) RegisterGRPC(s *grpc.Server) {
|
||||
if b.buildkit != nil {
|
||||
b.buildkit.RegisterGRPC(s)
|
||||
}
|
||||
func NewBackend(components ImageComponent, builder Builder, fsCache *fscache.FSCache, buildkit *buildkit.Builder) (*Backend, error) {
|
||||
return &Backend{imageComponent: components, builder: builder, fsCache: fsCache, buildkit: buildkit}, nil
|
||||
}
|
||||
|
||||
// Build builds an image from a Source
|
||||
@@ -91,8 +82,6 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string
|
||||
if !useBuildKit {
|
||||
stdout := config.ProgressWriter.StdoutFormatter
|
||||
fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID))
|
||||
}
|
||||
if imageID != "" && !useBuildKit {
|
||||
err = tagger.TagImages(image.ID(imageID))
|
||||
}
|
||||
return imageID, err
|
||||
@@ -100,16 +89,34 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string
|
||||
|
||||
// PruneCache removes all cached build sources
|
||||
func (b *Backend) PruneCache(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
|
||||
buildCacheSize, cacheIDs, err := b.buildkit.Prune(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to prune build cache")
|
||||
}
|
||||
b.eventsService.Log("prune", events.BuilderEventType, events.Actor{
|
||||
Attributes: map[string]string{
|
||||
"reclaimed": strconv.FormatInt(buildCacheSize, 10),
|
||||
},
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var fsCacheSize uint64
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
fsCacheSize, err = b.fsCache.Prune(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to prune fscache")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return &types.BuildCachePruneReport{SpaceReclaimed: uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil
|
||||
|
||||
var buildCacheSize int64
|
||||
var cacheIDs []string
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
buildCacheSize, cacheIDs, err = b.buildkit.Prune(ctx, opts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to prune build cache")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.BuildCachePruneReport{SpaceReclaimed: fsCacheSize + uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil
|
||||
}
|
||||
|
||||
// Cancel cancels the build by ID
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httpstatus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/gorilla/mux"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// makeErrorHandler makes an HTTP handler that decodes a Docker error and
|
||||
// returns it in the response.
|
||||
func makeErrorHandler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
statusCode := httpstatus.FromError(err)
|
||||
vars := mux.Vars(r)
|
||||
if apiVersionSupportsJSONErrors(vars["version"]) {
|
||||
response := &types.ErrorResponse{
|
||||
Message: err.Error(),
|
||||
}
|
||||
_ = httputils.WriteJSON(w, statusCode, response)
|
||||
} else {
|
||||
http.Error(w, status.Convert(err).Message(), statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiVersionSupportsJSONErrors(version string) bool {
|
||||
const firstAPIVersionWithJSONErrors = "1.23"
|
||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package httpstatus // import "github.com/docker/docker/api/server/httpstatus"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
containerderrors "github.com/containerd/containerd/errdefs"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// FromError retrieves status code from error message.
|
||||
func FromError(err error) int {
|
||||
if err == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
var statusCode int
|
||||
|
||||
// Stop right there
|
||||
// Are you sure you should be adding a new error class here? Do one of the existing ones work?
|
||||
|
||||
// Note that the below functions are already checking the error causal chain for matches.
|
||||
switch {
|
||||
case errdefs.IsNotFound(err):
|
||||
statusCode = http.StatusNotFound
|
||||
case errdefs.IsInvalidParameter(err):
|
||||
statusCode = http.StatusBadRequest
|
||||
case errdefs.IsConflict(err):
|
||||
statusCode = http.StatusConflict
|
||||
case errdefs.IsUnauthorized(err):
|
||||
statusCode = http.StatusUnauthorized
|
||||
case errdefs.IsUnavailable(err):
|
||||
statusCode = http.StatusServiceUnavailable
|
||||
case errdefs.IsForbidden(err):
|
||||
statusCode = http.StatusForbidden
|
||||
case errdefs.IsNotModified(err):
|
||||
statusCode = http.StatusNotModified
|
||||
case errdefs.IsNotImplemented(err):
|
||||
statusCode = http.StatusNotImplemented
|
||||
case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
|
||||
statusCode = http.StatusInternalServerError
|
||||
default:
|
||||
statusCode = statusCodeFromGRPCError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
statusCode = statusCodeFromContainerdError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
statusCode = statusCodeFromDistributionError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
if e, ok := err.(causer); ok {
|
||||
return FromError(e.Cause())
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"module": "api",
|
||||
"error_type": fmt.Sprintf("%T", err),
|
||||
}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return statusCode
|
||||
}
|
||||
|
||||
// statusCodeFromGRPCError returns status code according to gRPC error
|
||||
func statusCodeFromGRPCError(err error) int {
|
||||
switch status.Code(err) {
|
||||
case codes.InvalidArgument: // code 3
|
||||
return http.StatusBadRequest
|
||||
case codes.NotFound: // code 5
|
||||
return http.StatusNotFound
|
||||
case codes.AlreadyExists: // code 6
|
||||
return http.StatusConflict
|
||||
case codes.PermissionDenied: // code 7
|
||||
return http.StatusForbidden
|
||||
case codes.FailedPrecondition: // code 9
|
||||
return http.StatusBadRequest
|
||||
case codes.Unauthenticated: // code 16
|
||||
return http.StatusUnauthorized
|
||||
case codes.OutOfRange: // code 11
|
||||
return http.StatusBadRequest
|
||||
case codes.Unimplemented: // code 12
|
||||
return http.StatusNotImplemented
|
||||
case codes.Unavailable: // code 14
|
||||
return http.StatusServiceUnavailable
|
||||
default:
|
||||
// codes.Canceled(1)
|
||||
// codes.Unknown(2)
|
||||
// codes.DeadlineExceeded(4)
|
||||
// codes.ResourceExhausted(8)
|
||||
// codes.Aborted(10)
|
||||
// codes.Internal(13)
|
||||
// codes.DataLoss(15)
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// statusCodeFromDistributionError returns status code according to registry errcode
|
||||
// code is loosely based on errcode.ServeJSON() in docker/distribution
|
||||
func statusCodeFromDistributionError(err error) int {
|
||||
switch errs := err.(type) {
|
||||
case errcode.Errors:
|
||||
if len(errs) < 1 {
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
if _, ok := errs[0].(errcode.ErrorCoder); ok {
|
||||
return statusCodeFromDistributionError(errs[0])
|
||||
}
|
||||
case errcode.ErrorCoder:
|
||||
return errs.ErrorCode().Descriptor().HTTPStatusCode
|
||||
}
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
// statusCodeFromContainerdError returns status code for containerd errors when
|
||||
// consumed directly (not through gRPC)
|
||||
func statusCodeFromContainerdError(err error) int {
|
||||
switch {
|
||||
case containerderrors.IsInvalidArgument(err):
|
||||
return http.StatusBadRequest
|
||||
case containerderrors.IsNotFound(err):
|
||||
return http.StatusNotFound
|
||||
case containerderrors.IsAlreadyExists(err):
|
||||
return http.StatusConflict
|
||||
case containerderrors.IsFailedPrecondition(err):
|
||||
return http.StatusPreconditionFailed
|
||||
case containerderrors.IsUnavailable(err):
|
||||
return http.StatusServiceUnavailable
|
||||
case containerderrors.IsNotImplemented(err):
|
||||
return http.StatusNotImplemented
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
131
api/server/httputils/errors.go
Normal file
131
api/server/httputils/errors.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// GetHTTPErrorStatusCode retrieves status code from error message.
|
||||
func GetHTTPErrorStatusCode(err error) int {
|
||||
if err == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
var statusCode int
|
||||
|
||||
// Stop right there
|
||||
// Are you sure you should be adding a new error class here? Do one of the existing ones work?
|
||||
|
||||
// Note that the below functions are already checking the error causal chain for matches.
|
||||
switch {
|
||||
case errdefs.IsNotFound(err):
|
||||
statusCode = http.StatusNotFound
|
||||
case errdefs.IsInvalidParameter(err):
|
||||
statusCode = http.StatusBadRequest
|
||||
case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err):
|
||||
statusCode = http.StatusConflict
|
||||
case errdefs.IsUnauthorized(err):
|
||||
statusCode = http.StatusUnauthorized
|
||||
case errdefs.IsUnavailable(err):
|
||||
statusCode = http.StatusServiceUnavailable
|
||||
case errdefs.IsForbidden(err):
|
||||
statusCode = http.StatusForbidden
|
||||
case errdefs.IsNotModified(err):
|
||||
statusCode = http.StatusNotModified
|
||||
case errdefs.IsNotImplemented(err):
|
||||
statusCode = http.StatusNotImplemented
|
||||
case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
|
||||
statusCode = http.StatusInternalServerError
|
||||
default:
|
||||
statusCode = statusCodeFromGRPCError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
|
||||
if e, ok := err.(causer); ok {
|
||||
return GetHTTPErrorStatusCode(e.Cause())
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"module": "api",
|
||||
"error_type": fmt.Sprintf("%T", err),
|
||||
}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return statusCode
|
||||
}
|
||||
|
||||
func apiVersionSupportsJSONErrors(version string) bool {
|
||||
const firstAPIVersionWithJSONErrors = "1.23"
|
||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||
}
|
||||
|
||||
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and
|
||||
// returns it in the response.
|
||||
func MakeErrorHandler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
statusCode := GetHTTPErrorStatusCode(err)
|
||||
vars := mux.Vars(r)
|
||||
if apiVersionSupportsJSONErrors(vars["version"]) {
|
||||
response := &types.ErrorResponse{
|
||||
Message: err.Error(),
|
||||
}
|
||||
WriteJSON(w, statusCode, response)
|
||||
} else {
|
||||
http.Error(w, grpc.ErrorDesc(err), statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// statusCodeFromGRPCError returns status code according to gRPC error
|
||||
func statusCodeFromGRPCError(err error) int {
|
||||
switch grpc.Code(err) {
|
||||
case codes.InvalidArgument: // code 3
|
||||
return http.StatusBadRequest
|
||||
case codes.NotFound: // code 5
|
||||
return http.StatusNotFound
|
||||
case codes.AlreadyExists: // code 6
|
||||
return http.StatusConflict
|
||||
case codes.PermissionDenied: // code 7
|
||||
return http.StatusForbidden
|
||||
case codes.FailedPrecondition: // code 9
|
||||
return http.StatusBadRequest
|
||||
case codes.Unauthenticated: // code 16
|
||||
return http.StatusUnauthorized
|
||||
case codes.OutOfRange: // code 11
|
||||
return http.StatusBadRequest
|
||||
case codes.Unimplemented: // code 12
|
||||
return http.StatusNotImplemented
|
||||
case codes.Unavailable: // code 14
|
||||
return http.StatusServiceUnavailable
|
||||
default:
|
||||
if e, ok := err.(causer); ok {
|
||||
return statusCodeFromGRPCError(e.Cause())
|
||||
}
|
||||
// codes.Canceled(1)
|
||||
// codes.Unknown(2)
|
||||
// codes.DeadlineExceeded(4)
|
||||
// codes.ResourceExhausted(8)
|
||||
// codes.Aborted(10)
|
||||
// codes.Internal(13)
|
||||
// codes.DataLoss(15)
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func TestBoolValue(t *testing.T) {
|
||||
for c, e := range cases {
|
||||
v := url.Values{}
|
||||
v.Set("test", c)
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r, _ := http.NewRequest("POST", "", nil)
|
||||
r.Form = v
|
||||
|
||||
a := BoolValue(r, "test")
|
||||
@@ -34,14 +34,14 @@ func TestBoolValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBoolValueOrDefault(t *testing.T) {
|
||||
r, _ := http.NewRequest(http.MethodGet, "", nil)
|
||||
r, _ := http.NewRequest("GET", "", nil)
|
||||
if !BoolValueOrDefault(r, "queryparam", true) {
|
||||
t.Fatal("Expected to get true default value, got false")
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("param", "")
|
||||
r, _ = http.NewRequest(http.MethodGet, "", nil)
|
||||
r, _ = http.NewRequest("GET", "", nil)
|
||||
r.Form = v
|
||||
if BoolValueOrDefault(r, "param", true) {
|
||||
t.Fatal("Expected not to get true")
|
||||
@@ -59,7 +59,7 @@ func TestInt64ValueOrZero(t *testing.T) {
|
||||
for c, e := range cases {
|
||||
v := url.Values{}
|
||||
v.Set("test", c)
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r, _ := http.NewRequest("POST", "", nil)
|
||||
r.Form = v
|
||||
|
||||
a := Int64ValueOrZero(r, "test")
|
||||
@@ -79,7 +79,7 @@ func TestInt64ValueOrDefault(t *testing.T) {
|
||||
for c, e := range cases {
|
||||
v := url.Values{}
|
||||
v.Set("test", c)
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r, _ := http.NewRequest("POST", "", nil)
|
||||
r.Form = v
|
||||
|
||||
a, err := Int64ValueOrDefault(r, "test", -1)
|
||||
@@ -95,7 +95,7 @@ func TestInt64ValueOrDefault(t *testing.T) {
|
||||
func TestInt64ValueOrDefaultWithError(t *testing.T) {
|
||||
v := url.Values{}
|
||||
v.Set("test", "invalid")
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r, _ := http.NewRequest("POST", "", nil)
|
||||
r.Form = v
|
||||
|
||||
_, err := Int64ValueOrDefault(r, "test", -1)
|
||||
|
||||
@@ -2,7 +2,6 @@ package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
@@ -10,10 +9,13 @@ import (
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
// APIVersionKey is the client's requested API version.
|
||||
type APIVersionKey struct{}
|
||||
const APIVersionKey contextKey = "api-version"
|
||||
|
||||
// APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
|
||||
// Any function that has the appropriate signature can be registered as an API endpoint (e.g. getVersion).
|
||||
@@ -27,7 +29,7 @@ func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Flush the options to make sure the client sets the raw mode
|
||||
_, _ = conn.Write([]byte{})
|
||||
conn.Write([]byte{})
|
||||
return conn, conn, nil
|
||||
}
|
||||
|
||||
@@ -37,9 +39,9 @@ func CloseStreams(streams ...interface{}) {
|
||||
if tcpc, ok := stream.(interface {
|
||||
CloseWrite() error
|
||||
}); ok {
|
||||
_ = tcpc.CloseWrite()
|
||||
tcpc.CloseWrite()
|
||||
} else if closer, ok := stream.(io.Closer); ok {
|
||||
_ = closer.Close()
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,50 +51,17 @@ func CheckForJSON(r *http.Request) error {
|
||||
ct := r.Header.Get("Content-Type")
|
||||
|
||||
// No Content-Type header is ok as long as there's no Body
|
||||
if ct == "" && (r.Body == nil || r.ContentLength == 0) {
|
||||
return nil
|
||||
if ct == "" {
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise it better be json
|
||||
return matchesContentType(ct, "application/json")
|
||||
}
|
||||
|
||||
// ReadJSON validates the request to have the correct content-type, and decodes
|
||||
// the request's Body into out.
|
||||
func ReadJSON(r *http.Request, out interface{}) error {
|
||||
err := CheckForJSON(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
// an empty body is not invalid, so don't return an error; see
|
||||
// https://lists.w3.org/Archives/Public/ietf-http-wg/2010JulSep/0272.html
|
||||
if matchesContentType(ct, "application/json") {
|
||||
return nil
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
err = dec.Decode(out)
|
||||
defer r.Body.Close()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
|
||||
}
|
||||
return errdefs.InvalidParameter(errors.Wrap(err, "invalid JSON"))
|
||||
}
|
||||
|
||||
if dec.More() {
|
||||
return errdefs.InvalidParameter(errors.New("unexpected content after JSON"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc.Encode(v)
|
||||
return errdefs.InvalidParameter(errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct))
|
||||
}
|
||||
|
||||
// ParseForm ensures the request form is parsed even with invalid content types.
|
||||
@@ -114,7 +83,7 @@ func VersionFromContext(ctx context.Context) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
if val := ctx.Value(APIVersionKey{}); val != nil {
|
||||
if val := ctx.Value(APIVersionKey); val != nil {
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
@@ -122,13 +91,10 @@ func VersionFromContext(ctx context.Context) string {
|
||||
}
|
||||
|
||||
// matchesContentType validates the content type against the expected one
|
||||
func matchesContentType(contentType, expectedType string) error {
|
||||
func matchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(errors.Wrapf(err, "malformed Content-Type header (%s)", contentType))
|
||||
logrus.Errorf("Error parsing media type: %s error: %v", contentType, err)
|
||||
}
|
||||
if mimetype != expectedType {
|
||||
return errdefs.InvalidParameter(errors.Errorf("unsupported Content-Type header (%s): must be '%s'", contentType, expectedType))
|
||||
}
|
||||
return nil
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
|
||||
@@ -1,130 +1,18 @@
|
||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
// matchesContentType
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
err := matchesContentType("application/json", "application/json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
if !matchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
err = matchesContentType("application/json; charset=utf-8", "application/json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
if !matchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expected := "unsupported Content-Type header (dockerapplication/json): must be 'application/json'"
|
||||
err = matchesContentType("dockerapplication/json", "application/json")
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
||||
}
|
||||
|
||||
expected = "malformed Content-Type header (foo;;;bar): mime: invalid media parameter"
|
||||
err = matchesContentType("foo;;;bar", "application/json")
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
||||
if matchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadJSON(t *testing.T) {
|
||||
t.Run("nil body", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
foo := struct{}{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty body", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(""))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "" {
|
||||
t.Errorf("expected: '', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with valid request", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"}`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "some value" {
|
||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
t.Run("with whitespace", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`
|
||||
|
||||
{"SomeField":"some value"}
|
||||
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if foo.SomeField != "some value" {
|
||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with extra content", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"} and more content`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err == nil {
|
||||
t.Error("expected an error, got none")
|
||||
}
|
||||
expected := "unexpected content after JSON"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid JSON", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{invalid json`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
foo := struct{ SomeField string }{}
|
||||
err = ReadJSON(req, &foo)
|
||||
if err == nil {
|
||||
t.Error("expected an error, got none")
|
||||
}
|
||||
expected := "invalid JSON: invalid character 'i' looking for beginning of object key string"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
15
api/server/httputils/httputils_write_json.go
Normal file
15
api/server/httputils/httputils_write_json.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc.Encode(v)
|
||||
}
|
||||
@@ -51,10 +51,10 @@ func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMess
|
||||
logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
|
||||
}
|
||||
if msg.Source == "stdout" && config.ShowStdout {
|
||||
_, _ = outStream.Write(logLine)
|
||||
outStream.Write(logLine)
|
||||
}
|
||||
if msg.Source == "stderr" && config.ShowStderr {
|
||||
_, _ = errStream.Write(logLine)
|
||||
errStream.Write(logLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (s *Server) handlerWithGlobalMiddlewares(handler httputils.APIFunc) httputi
|
||||
next = m.WrapHandler(next)
|
||||
}
|
||||
|
||||
if logrus.GetLevel() == logrus.DebugLevel {
|
||||
if s.cfg.Logging && logrus.GetLevel() == logrus.DebugLevel {
|
||||
next = middleware.DebugRequestMiddleware(next)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
logrus.Debugf("Calling %s %s", r.Method, r.RequestURI)
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
if r.Method != "POST" {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
@@ -41,7 +41,7 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri
|
||||
|
||||
var postForm map[string]interface{}
|
||||
if err := json.Unmarshal(b, &postForm); err == nil {
|
||||
maskSecretKeys(postForm)
|
||||
maskSecretKeys(postForm, r.RequestURI)
|
||||
formStr, errMarshal := json.Marshal(postForm)
|
||||
if errMarshal == nil {
|
||||
logrus.Debugf("form data: %s", string(formStr))
|
||||
@@ -54,37 +54,41 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri
|
||||
}
|
||||
}
|
||||
|
||||
func maskSecretKeys(inp interface{}) {
|
||||
func maskSecretKeys(inp interface{}, path string) {
|
||||
// Remove any query string from the path
|
||||
idx := strings.Index(path, "?")
|
||||
if idx != -1 {
|
||||
path = path[:idx]
|
||||
}
|
||||
// Remove trailing / characters
|
||||
path = strings.TrimRight(path, "/")
|
||||
|
||||
if arr, ok := inp.([]interface{}); ok {
|
||||
for _, f := range arr {
|
||||
maskSecretKeys(f)
|
||||
maskSecretKeys(f, path)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if form, ok := inp.(map[string]interface{}); ok {
|
||||
scrub := []string{
|
||||
// Note: The Data field contains the base64-encoded secret in 'secret'
|
||||
// and 'config' create and update requests. Currently, no other POST
|
||||
// API endpoints use a data field, so we scrub this field unconditionally.
|
||||
// Change this handling to be conditional if a new endpoint is added
|
||||
// in future where this field should not be scrubbed.
|
||||
"data",
|
||||
"jointoken",
|
||||
"password",
|
||||
"secret",
|
||||
"signingcakey",
|
||||
"unlockkey",
|
||||
}
|
||||
loop0:
|
||||
for k, v := range form {
|
||||
for _, m := range scrub {
|
||||
for _, m := range []string{"password", "secret", "jointoken", "unlockkey", "signingcakey"} {
|
||||
if strings.EqualFold(m, k) {
|
||||
form[k] = "*****"
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
maskSecretKeys(v)
|
||||
maskSecretKeys(v, path)
|
||||
}
|
||||
|
||||
// Route-specific redactions
|
||||
if strings.HasSuffix(path, "/secrets/create") {
|
||||
for k := range form {
|
||||
if k == "Data" {
|
||||
form[k] = "*****"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,31 +3,37 @@ package middleware // import "github.com/docker/docker/api/server/middleware"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
)
|
||||
|
||||
func TestMaskSecretKeys(t *testing.T) {
|
||||
tests := []struct {
|
||||
doc string
|
||||
path string
|
||||
input map[string]interface{}
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
doc: "secret/config create and update requests",
|
||||
path: "/v1.30/secrets/create",
|
||||
input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
},
|
||||
{
|
||||
doc: "masking other fields (recursively)",
|
||||
path: "/v1.30/secrets/create//",
|
||||
input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
},
|
||||
|
||||
{
|
||||
path: "/secrets/create?key=val",
|
||||
input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}},
|
||||
},
|
||||
{
|
||||
path: "/v1.30/some/other/path",
|
||||
input: map[string]interface{}{
|
||||
"password": "pass",
|
||||
"secret": "secret",
|
||||
"jointoken": "jointoken",
|
||||
"unlockkey": "unlockkey",
|
||||
"signingcakey": "signingcakey",
|
||||
"password": "pass",
|
||||
"other": map[string]interface{}{
|
||||
"password": "pass",
|
||||
"secret": "secret",
|
||||
"jointoken": "jointoken",
|
||||
"unlockkey": "unlockkey",
|
||||
@@ -35,13 +41,8 @@ func TestMaskSecretKeys(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"password": "*****",
|
||||
"secret": "*****",
|
||||
"jointoken": "*****",
|
||||
"unlockkey": "*****",
|
||||
"signingcakey": "*****",
|
||||
"password": "*****",
|
||||
"other": map[string]interface{}{
|
||||
"password": "*****",
|
||||
"secret": "*****",
|
||||
"jointoken": "*****",
|
||||
"unlockkey": "*****",
|
||||
@@ -49,27 +50,10 @@ func TestMaskSecretKeys(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "case insensitive field matching",
|
||||
input: map[string]interface{}{
|
||||
"PASSWORD": "pass",
|
||||
"other": map[string]interface{}{
|
||||
"PASSWORD": "pass",
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"PASSWORD": "*****",
|
||||
"other": map[string]interface{}{
|
||||
"PASSWORD": "*****",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range tests {
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
maskSecretKeys(testcase.input)
|
||||
assert.Check(t, is.DeepEqual(testcase.expected, testcase.input))
|
||||
})
|
||||
maskSecretKeys(testcase.input, testcase.path)
|
||||
assert.Check(t, is.DeepEqual(testcase.expected, testcase.input))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.
|
||||
if versions.GreaterThan(apiVersion, v.defaultVersion) {
|
||||
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultVersion}
|
||||
}
|
||||
ctx = context.WithValue(ctx, httputils.APIVersionKey{}, apiVersion)
|
||||
ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
)
|
||||
|
||||
func TestVersionMiddlewareVersion(t *testing.T) {
|
||||
@@ -25,7 +25,7 @@ func TestVersionMiddlewareVersion(t *testing.T) {
|
||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||
h := m.WrapHandler(handler)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
|
||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||
h := m.WrapHandler(handler)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package build // import "github.com/docker/docker/api/server/router/build"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
@@ -33,30 +31,23 @@ func (r *buildRouter) Routes() []router.Route {
|
||||
|
||||
func (r *buildRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
router.NewPostRoute("/build", r.postBuild),
|
||||
router.NewPostRoute("/build/prune", r.postPrune),
|
||||
router.NewPostRoute("/build", r.postBuild, router.WithCancel),
|
||||
router.NewPostRoute("/build/prune", r.postPrune, router.WithCancel),
|
||||
router.NewPostRoute("/build/cancel", r.postCancel),
|
||||
}
|
||||
}
|
||||
|
||||
// BuilderVersion derives the default docker builder version from the config.
|
||||
//
|
||||
// The default on Linux is version "2" (BuildKit), but the daemon can be
|
||||
// configured to recommend version "1" (classic Builder). Windows does not
|
||||
// yet support BuildKit for native Windows images, and uses "1" (classic builder)
|
||||
// as a default.
|
||||
//
|
||||
// This value is only a recommendation as advertised by the daemon, and it is
|
||||
// up to the client to choose which builder to use.
|
||||
// BuilderVersion derives the default docker builder version from the config
|
||||
// Note: it is valid to have BuilderVersion unset which means it is up to the
|
||||
// client to choose which builder to use.
|
||||
func BuilderVersion(features map[string]bool) types.BuilderVersion {
|
||||
// TODO(thaJeztah) move the default to daemon/config
|
||||
if runtime.GOOS == "windows" {
|
||||
return types.BuilderV1
|
||||
}
|
||||
|
||||
bv := types.BuilderBuildKit
|
||||
if v, ok := features["buildkit"]; ok && !v {
|
||||
bv = types.BuilderV1
|
||||
var bv types.BuilderVersion
|
||||
if v, ok := features["buildkit"]; ok {
|
||||
if v {
|
||||
bv = types.BuilderBuildKit
|
||||
} else {
|
||||
bv = types.BuilderV1
|
||||
}
|
||||
}
|
||||
return bv
|
||||
}
|
||||
|
||||
@@ -38,36 +38,8 @@ func (e invalidIsolationError) Error() string {
|
||||
func (e invalidIsolationError) InvalidParameter() {}
|
||||
|
||||
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
|
||||
options := &types.ImageBuildOptions{
|
||||
Version: types.BuilderV1, // Builder V1 is the default, but can be overridden
|
||||
Dockerfile: r.FormValue("dockerfile"),
|
||||
SuppressOutput: httputils.BoolValue(r, "q"),
|
||||
NoCache: httputils.BoolValue(r, "nocache"),
|
||||
ForceRemove: httputils.BoolValue(r, "forcerm"),
|
||||
MemorySwap: httputils.Int64ValueOrZero(r, "memswap"),
|
||||
Memory: httputils.Int64ValueOrZero(r, "memory"),
|
||||
CPUShares: httputils.Int64ValueOrZero(r, "cpushares"),
|
||||
CPUPeriod: httputils.Int64ValueOrZero(r, "cpuperiod"),
|
||||
CPUQuota: httputils.Int64ValueOrZero(r, "cpuquota"),
|
||||
CPUSetCPUs: r.FormValue("cpusetcpus"),
|
||||
CPUSetMems: r.FormValue("cpusetmems"),
|
||||
CgroupParent: r.FormValue("cgroupparent"),
|
||||
NetworkMode: r.FormValue("networkmode"),
|
||||
Tags: r.Form["t"],
|
||||
ExtraHosts: r.Form["extrahosts"],
|
||||
SecurityOpt: r.Form["securityopt"],
|
||||
Squash: httputils.BoolValue(r, "squash"),
|
||||
Target: r.FormValue("target"),
|
||||
RemoteContext: r.FormValue("remote"),
|
||||
SessionID: r.FormValue("session"),
|
||||
BuildID: r.FormValue("buildid"),
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && options.SecurityOpt != nil {
|
||||
return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build"))
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
options := &types.ImageBuildOptions{}
|
||||
if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") {
|
||||
options.Remove = true
|
||||
} else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") {
|
||||
@@ -78,37 +50,52 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") {
|
||||
options.PullParent = true
|
||||
}
|
||||
|
||||
options.Dockerfile = r.FormValue("dockerfile")
|
||||
options.SuppressOutput = httputils.BoolValue(r, "q")
|
||||
options.NoCache = httputils.BoolValue(r, "nocache")
|
||||
options.ForceRemove = httputils.BoolValue(r, "forcerm")
|
||||
options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
|
||||
options.Memory = httputils.Int64ValueOrZero(r, "memory")
|
||||
options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
|
||||
options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
|
||||
options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
|
||||
options.CPUSetCPUs = r.FormValue("cpusetcpus")
|
||||
options.CPUSetMems = r.FormValue("cpusetmems")
|
||||
options.CgroupParent = r.FormValue("cgroupparent")
|
||||
options.NetworkMode = r.FormValue("networkmode")
|
||||
options.Tags = r.Form["t"]
|
||||
options.ExtraHosts = r.Form["extrahosts"]
|
||||
options.SecurityOpt = r.Form["securityopt"]
|
||||
options.Squash = httputils.BoolValue(r, "squash")
|
||||
options.Target = r.FormValue("target")
|
||||
options.RemoteContext = r.FormValue("remote")
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
options.Platform = r.FormValue("platform")
|
||||
}
|
||||
if versions.GreaterThanOrEqualTo(version, "1.40") {
|
||||
outputsJSON := r.FormValue("outputs")
|
||||
if outputsJSON != "" {
|
||||
var outputs []types.ImageBuildOutput
|
||||
if err := json.Unmarshal([]byte(outputsJSON), &outputs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Outputs = outputs
|
||||
}
|
||||
}
|
||||
|
||||
if s := r.Form.Get("shmsize"); s != "" {
|
||||
shmSize, err := strconv.ParseInt(s, 10, 64)
|
||||
if r.Form.Get("shmsize") != "" {
|
||||
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.ShmSize = shmSize
|
||||
}
|
||||
|
||||
if i := r.FormValue("isolation"); i != "" {
|
||||
options.Isolation = container.Isolation(i)
|
||||
if !options.Isolation.IsValid() {
|
||||
return nil, invalidIsolationError(options.Isolation)
|
||||
if i := container.Isolation(r.FormValue("isolation")); i != "" {
|
||||
if !container.Isolation.IsValid(i) {
|
||||
return nil, invalidIsolationError(i)
|
||||
}
|
||||
options.Isolation = i
|
||||
}
|
||||
|
||||
if ulimitsJSON := r.FormValue("ulimits"); ulimitsJSON != "" {
|
||||
var buildUlimits = []*units.Ulimit{}
|
||||
if runtime.GOOS != "windows" && options.SecurityOpt != nil {
|
||||
return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build"))
|
||||
}
|
||||
|
||||
var buildUlimits = []*units.Ulimit{}
|
||||
ulimitsJSON := r.FormValue("ulimits")
|
||||
if ulimitsJSON != "" {
|
||||
if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil {
|
||||
return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading ulimit settings")
|
||||
}
|
||||
@@ -127,7 +114,8 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
// the fact they mentioned it, we need to pass that along to the builder
|
||||
// so that it can print a warning about "foo" being unused if there is
|
||||
// no "ARG foo" in the Dockerfile.
|
||||
if buildArgsJSON := r.FormValue("buildargs"); buildArgsJSON != "" {
|
||||
buildArgsJSON := r.FormValue("buildargs")
|
||||
if buildArgsJSON != "" {
|
||||
var buildArgs = map[string]*string{}
|
||||
if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil {
|
||||
return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading build args")
|
||||
@@ -135,7 +123,8 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
options.BuildArgs = buildArgs
|
||||
}
|
||||
|
||||
if labelsJSON := r.FormValue("labels"); labelsJSON != "" {
|
||||
labelsJSON := r.FormValue("labels")
|
||||
if labelsJSON != "" {
|
||||
var labels = map[string]string{}
|
||||
if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
|
||||
return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading labels")
|
||||
@@ -143,41 +132,40 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
options.Labels = labels
|
||||
}
|
||||
|
||||
if cacheFromJSON := r.FormValue("cachefrom"); cacheFromJSON != "" {
|
||||
cacheFromJSON := r.FormValue("cachefrom")
|
||||
if cacheFromJSON != "" {
|
||||
var cacheFrom = []string{}
|
||||
if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.CacheFrom = cacheFrom
|
||||
}
|
||||
|
||||
if bv := r.FormValue("version"); bv != "" {
|
||||
v, err := parseVersion(bv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Version = v
|
||||
options.SessionID = r.FormValue("session")
|
||||
options.BuildID = r.FormValue("buildid")
|
||||
builderVersion, err := parseVersion(r.FormValue("version"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Version = builderVersion
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func parseVersion(s string) (types.BuilderVersion, error) {
|
||||
switch types.BuilderVersion(s) {
|
||||
case types.BuilderV1:
|
||||
if s == "" || s == string(types.BuilderV1) {
|
||||
return types.BuilderV1, nil
|
||||
case types.BuilderBuildKit:
|
||||
return types.BuilderBuildKit, nil
|
||||
default:
|
||||
return "", errors.Errorf("invalid version %q", s)
|
||||
}
|
||||
if s == string(types.BuilderBuildKit) {
|
||||
return types.BuilderBuildKit, nil
|
||||
}
|
||||
return "", errors.Errorf("invalid version %s", s)
|
||||
}
|
||||
|
||||
func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
fltrs, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
filters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse filters")
|
||||
}
|
||||
@@ -192,7 +180,7 @@ func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *
|
||||
|
||||
opts := types.BuildCachePruneOptions{
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Filters: fltrs,
|
||||
Filters: filters,
|
||||
KeepStorage: int64(ks),
|
||||
}
|
||||
|
||||
@@ -235,11 +223,12 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(ww)
|
||||
defer func() { _ = output.Close() }()
|
||||
defer output.Close()
|
||||
|
||||
errf := func(err error) error {
|
||||
|
||||
if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
|
||||
_, _ = output.Write(notVerboseBuffer.Bytes())
|
||||
output.Write(notVerboseBuffer.Bytes())
|
||||
}
|
||||
|
||||
// Do not write the error in the http output if it's still empty.
|
||||
@@ -290,7 +279,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
// Everything worked so if -q was provided the output from the daemon
|
||||
// should be just the image ID and we'll print that to stdout.
|
||||
if buildOptions.SuppressOutput {
|
||||
_, _ = fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID)
|
||||
fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -306,7 +295,7 @@ func getAuthConfigs(header http.Header) map[string]types.AuthConfig {
|
||||
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
||||
// Pulling an image does not error when no auth is provided so to remain
|
||||
// consistent with the existing api decode errors are ignored
|
||||
_ = json.NewDecoder(authConfigsJSON).Decode(&authConfigs)
|
||||
json.NewDecoder(authConfigsJSON).Decode(&authConfigs)
|
||||
return authConfigs
|
||||
}
|
||||
|
||||
@@ -428,7 +417,7 @@ func (w *wcf) notify() {
|
||||
w.mu.Lock()
|
||||
if !w.ready {
|
||||
if w.buf.Len() > 0 {
|
||||
_, _ = io.Copy(w.Writer, w.buf)
|
||||
io.Copy(w.Writer, w.buf)
|
||||
}
|
||||
if w.flushed {
|
||||
w.flusher.Flush()
|
||||
|
||||
@@ -2,6 +2,7 @@ package checkpoint // import "github.com/docker/docker/api/server/router/checkpo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
@@ -14,7 +15,9 @@ func (s *checkpointRouter) postContainerCheckpoint(ctx context.Context, w http.R
|
||||
}
|
||||
|
||||
var options types.CheckpointCreateOptions
|
||||
if err := httputils.ReadJSON(r, &options); err != nil {
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ type execBackend interface {
|
||||
ContainerExecCreate(name string, config *types.ExecConfig) (string, error)
|
||||
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
||||
ContainerExecResize(name string, height, width int) error
|
||||
ContainerExecStart(ctx context.Context, name string, options container.ExecStartOptions) error
|
||||
ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
|
||||
ExecExists(name string) (bool, error)
|
||||
}
|
||||
|
||||
@@ -32,15 +32,15 @@ type copyBackend interface {
|
||||
|
||||
// stateBackend includes functions to implement to provide container state lifecycle functionality.
|
||||
type stateBackend interface {
|
||||
ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
|
||||
ContainerKill(name string, signal string) error
|
||||
ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
|
||||
ContainerKill(name string, sig uint64) error
|
||||
ContainerPause(name string) error
|
||||
ContainerRename(oldName, newName string) error
|
||||
ContainerResize(name string, height, width int) error
|
||||
ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
|
||||
ContainerRestart(name string, seconds *int) error
|
||||
ContainerRm(name string, config *types.ContainerRmConfig) error
|
||||
ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
||||
ContainerStop(ctx context.Context, name string, options container.StopOptions) error
|
||||
ContainerStop(name string, seconds *int) error
|
||||
ContainerUnpause(name string) error
|
||||
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
|
||||
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
||||
|
||||
@@ -10,15 +10,13 @@ type containerRouter struct {
|
||||
backend Backend
|
||||
decoder httputils.ContainerDecoder
|
||||
routes []router.Route
|
||||
cgroup2 bool
|
||||
}
|
||||
|
||||
// NewRouter initializes a new container router
|
||||
func NewRouter(b Backend, decoder httputils.ContainerDecoder, cgroup2 bool) router.Router {
|
||||
func NewRouter(b Backend, decoder httputils.ContainerDecoder) router.Router {
|
||||
r := &containerRouter{
|
||||
backend: b,
|
||||
decoder: decoder,
|
||||
cgroup2: cgroup2,
|
||||
}
|
||||
r.initRoutes()
|
||||
return r
|
||||
@@ -40,8 +38,8 @@ func (r *containerRouter) initRoutes() {
|
||||
router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
|
||||
router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
|
||||
router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
|
||||
router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
|
||||
router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
|
||||
router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs, router.WithCancel),
|
||||
router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats, router.WithCancel),
|
||||
router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
|
||||
router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
|
||||
router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
|
||||
@@ -53,16 +51,16 @@ func (r *containerRouter) initRoutes() {
|
||||
router.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
|
||||
router.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
|
||||
router.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
|
||||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait, router.WithCancel),
|
||||
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8, Errors out since 1.12
|
||||
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
||||
router.NewPostRoute("/containers/prune", r.postContainersPrune),
|
||||
router.NewPostRoute("/containers/prune", r.postContainersPrune, router.WithCancel),
|
||||
router.NewPostRoute("/commit", r.postCommit),
|
||||
// PUT
|
||||
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||
|
||||
@@ -6,22 +6,19 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/api/server/httpstatus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/websocket"
|
||||
@@ -44,7 +41,7 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
config, _, _, err := s.decoder.DecodeConfig(r.Body)
|
||||
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
|
||||
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -108,14 +105,9 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||
if !stream {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
var oneShot bool
|
||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.41") {
|
||||
oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
|
||||
}
|
||||
|
||||
config := &backend.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OneShot: oneShot,
|
||||
OutStream: w,
|
||||
Version: httputils.VersionFromContext(ctx),
|
||||
}
|
||||
@@ -155,12 +147,6 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
||||
return err
|
||||
}
|
||||
|
||||
contentType := types.MediaTypeRawStream
|
||||
if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
||||
contentType = types.MediaTypeMultiplexedStream
|
||||
}
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
// if has a tty, we're not muxing streams. if it doesn't, we are. simple.
|
||||
// this is the point of no return for writing a response. once we call
|
||||
// WriteLogStream, the response has been started and errors will be
|
||||
@@ -227,26 +213,20 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
options container.StopOptions
|
||||
version = httputils.VersionFromContext(ctx)
|
||||
)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
options.Signal = r.Form.Get("signal")
|
||||
}
|
||||
var seconds *int
|
||||
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
||||
valSeconds, err := strconv.Atoi(tmpSeconds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Timeout = &valSeconds
|
||||
seconds = &valSeconds
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerStop(ctx, vars["name"], options); err != nil {
|
||||
if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -255,8 +235,18 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
|
||||
return err
|
||||
}
|
||||
|
||||
var sig syscall.Signal
|
||||
name := vars["name"]
|
||||
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
|
||||
|
||||
// If we have a signal, look at it. Otherwise, do nothing
|
||||
if sigStr := r.Form.Get("signal"); sigStr != "" {
|
||||
var err error
|
||||
if sig, err = signal.ParseSignal(sigStr); err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
|
||||
var isStopped bool
|
||||
if errdefs.IsConflict(err) {
|
||||
isStopped = true
|
||||
@@ -280,26 +270,21 @@ func (s *containerRouter) postContainersRestart(ctx context.Context, w http.Resp
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
options container.StopOptions
|
||||
version = httputils.VersionFromContext(ctx)
|
||||
)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
options.Signal = r.Form.Get("signal")
|
||||
}
|
||||
var seconds *int
|
||||
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
||||
valSeconds, err := strconv.Atoi(tmpSeconds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Timeout = &valSeconds
|
||||
seconds = &valSeconds
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerRestart(ctx, vars["name"], options); err != nil {
|
||||
if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -344,21 +329,18 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if v := r.Form.Get("condition"); v != "" {
|
||||
switch container.WaitCondition(v) {
|
||||
case container.WaitConditionNotRunning:
|
||||
waitCondition = containerpkg.WaitConditionNotRunning
|
||||
case container.WaitConditionNextExit:
|
||||
waitCondition = containerpkg.WaitConditionNextExit
|
||||
case container.WaitConditionRemoved:
|
||||
waitCondition = containerpkg.WaitConditionRemoved
|
||||
legacyRemovalWaitPre134 = versions.LessThan(version, "1.34")
|
||||
default:
|
||||
return errdefs.InvalidParameter(errors.Errorf("invalid condition: %q", v))
|
||||
}
|
||||
switch container.WaitCondition(r.Form.Get("condition")) {
|
||||
case container.WaitConditionNextExit:
|
||||
waitCondition = containerpkg.WaitConditionNextExit
|
||||
case container.WaitConditionRemoved:
|
||||
waitCondition = containerpkg.WaitConditionRemoved
|
||||
legacyRemovalWaitPre134 = versions.LessThan(version, "1.34")
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the context should get canceled if the client closes the
|
||||
// connection since this handler has been wrapped by the
|
||||
// router.WithCancel() wrapper.
|
||||
waitC, err := s.backend.ContainerWait(ctx, vars["name"], waitCondition)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -385,12 +367,12 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
return nil
|
||||
}
|
||||
|
||||
var waitError *container.WaitExitError
|
||||
var waitError *container.ContainerWaitOKBodyError
|
||||
if status.Err() != nil {
|
||||
waitError = &container.WaitExitError{Message: status.Err().Error()}
|
||||
waitError = &container.ContainerWaitOKBodyError{Message: status.Err().Error()}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(&container.WaitResponse{
|
||||
return json.NewEncoder(w).Encode(&container.ContainerWaitOKBody{
|
||||
StatusCode: int64(status.ExitCode()),
|
||||
Error: waitError,
|
||||
})
|
||||
@@ -436,26 +418,15 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var updateConfig container.UpdateConfig
|
||||
if err := httputils.ReadJSON(r, &updateConfig); err != nil {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.40") {
|
||||
updateConfig.PidsLimit = nil
|
||||
}
|
||||
|
||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
||||
// Ignore KernelMemory removed in API 1.42.
|
||||
updateConfig.KernelMemory = 0
|
||||
}
|
||||
var updateConfig container.UpdateConfig
|
||||
|
||||
if updateConfig.PidsLimit != nil && *updateConfig.PidsLimit <= 0 {
|
||||
// Both `0` and `-1` are accepted to set "unlimited" when updating.
|
||||
// Historically, any negative value was accepted, so treat them as
|
||||
// "unlimited" as well.
|
||||
var unlimited int64
|
||||
updateConfig.PidsLimit = &unlimited
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&updateConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostConfig := &container.HostConfig{
|
||||
@@ -484,9 +455,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||
|
||||
config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
|
||||
}
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
@@ -497,97 +465,12 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||
hostConfig.AutoRemove = false
|
||||
}
|
||||
|
||||
if hostConfig != nil && versions.LessThan(version, "1.40") {
|
||||
// Ignore BindOptions.NonRecursive because it was added in API 1.40.
|
||||
for _, m := range hostConfig.Mounts {
|
||||
if bo := m.BindOptions; bo != nil {
|
||||
bo.NonRecursive = false
|
||||
}
|
||||
}
|
||||
// Ignore KernelMemoryTCP because it was added in API 1.40.
|
||||
hostConfig.KernelMemoryTCP = 0
|
||||
|
||||
// Older clients (API < 1.40) expects the default to be shareable, make them happy
|
||||
if hostConfig.IpcMode.IsEmpty() {
|
||||
hostConfig.IpcMode = container.IPCModeShareable
|
||||
}
|
||||
}
|
||||
if hostConfig != nil && versions.LessThan(version, "1.41") && !s.cgroup2 {
|
||||
// Older clients expect the default to be "host" on cgroup v1 hosts
|
||||
if hostConfig.CgroupnsMode.IsEmpty() {
|
||||
hostConfig.CgroupnsMode = container.CgroupnsModeHost
|
||||
}
|
||||
}
|
||||
|
||||
if hostConfig != nil && versions.LessThan(version, "1.42") {
|
||||
for _, m := range hostConfig.Mounts {
|
||||
// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
|
||||
if bo := m.BindOptions; bo != nil {
|
||||
bo.CreateMountpoint = false
|
||||
}
|
||||
|
||||
// These combinations are invalid, but weren't validated in API < 1.42.
|
||||
// We reset them here, so that validation doesn't produce an error.
|
||||
if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
|
||||
m.VolumeOptions = nil
|
||||
}
|
||||
if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
|
||||
m.TmpfsOptions = nil
|
||||
}
|
||||
if bo := m.BindOptions; bo != nil {
|
||||
// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
|
||||
bo.CreateMountpoint = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hostConfig != nil && versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
// Ignore KernelMemory removed in API 1.42.
|
||||
hostConfig.KernelMemory = 0
|
||||
for _, m := range hostConfig.Mounts {
|
||||
if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
|
||||
return errdefs.InvalidParameter(fmt.Errorf("VolumeOptions must not be specified on mount type %q", m.Type))
|
||||
}
|
||||
if o := m.BindOptions; o != nil && m.Type != mount.TypeBind {
|
||||
return errdefs.InvalidParameter(fmt.Errorf("BindOptions must not be specified on mount type %q", m.Type))
|
||||
}
|
||||
if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
|
||||
return errdefs.InvalidParameter(fmt.Errorf("TmpfsOptions must not be specified on mount type %q", m.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hostConfig != nil && runtime.GOOS == "linux" && versions.LessThan(version, "1.42") {
|
||||
// ConsoleSize is not respected by Linux daemon before API 1.42
|
||||
hostConfig.ConsoleSize = [2]uint{0, 0}
|
||||
}
|
||||
|
||||
var platform *specs.Platform
|
||||
if versions.GreaterThanOrEqualTo(version, "1.41") {
|
||||
if v := r.Form.Get("platform"); v != "" {
|
||||
p, err := platforms.Parse(v)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
platform = &p
|
||||
}
|
||||
}
|
||||
|
||||
if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
|
||||
// Don't set a limit if either no limit was specified, or "unlimited" was
|
||||
// explicitly set.
|
||||
// Both `0` and `-1` are accepted as "unlimited", and historically any
|
||||
// negative value was accepted, so treat those as "unlimited" as well.
|
||||
hostConfig.PidsLimit = nil
|
||||
}
|
||||
|
||||
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
||||
Name: name,
|
||||
Config: config,
|
||||
HostConfig: hostConfig,
|
||||
NetworkingConfig: networkingConfig,
|
||||
AdjustCPUShares: adjustCPUShares,
|
||||
Platform: platform,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -649,8 +532,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||
return errdefs.InvalidParameter(errors.Errorf("error attaching to container %s, hijack connection missing", containerName))
|
||||
}
|
||||
|
||||
contentType := types.MediaTypeRawStream
|
||||
setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -660,10 +542,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||
conn.Write([]byte{})
|
||||
|
||||
if upgrade {
|
||||
if multiplexed && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
||||
contentType = types.MediaTypeMultiplexedStream
|
||||
}
|
||||
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
@@ -687,16 +566,16 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||
}
|
||||
|
||||
if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
|
||||
logrus.WithError(err).Errorf("Handler for %s %s returned error", r.Method, r.URL.Path)
|
||||
logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
// Remember to close stream if error happens
|
||||
conn, _, errHijack := hijacker.Hijack()
|
||||
if errHijack != nil {
|
||||
logrus.WithError(err).Errorf("Handler for %s %s: unable to close stream; error when hijacking connection", r.Method, r.URL.Path)
|
||||
} else {
|
||||
statusCode := httpstatus.FromError(err)
|
||||
if errHijack == nil {
|
||||
statusCode := httputils.GetHTTPErrorStatusCode(err)
|
||||
statusText := http.StatusText(statusCode)
|
||||
fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: %s\r\n\r\n%s\r\n", statusCode, statusText, contentType, err.Error())
|
||||
fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
|
||||
httputils.CloseStreams(conn)
|
||||
} else {
|
||||
logrus.Errorf("Error Hijacking: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -716,7 +595,7 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
wsChan := make(chan *websocket.Conn)
|
||||
h := func(conn *websocket.Conn) {
|
||||
wsChan <- conn
|
||||
@@ -738,22 +617,15 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||
return conn, conn, conn, nil
|
||||
}
|
||||
|
||||
useStdin, useStdout, useStderr := true, true, true
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
useStdin = httputils.BoolValue(r, "stdin")
|
||||
useStdout = httputils.BoolValue(r, "stdout")
|
||||
useStderr = httputils.BoolValue(r, "stderr")
|
||||
}
|
||||
|
||||
attachConfig := &backend.ContainerAttachConfig{
|
||||
GetStreams: setupStreams,
|
||||
UseStdin: useStdin,
|
||||
UseStdout: useStdout,
|
||||
UseStderr: useStderr,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: detachKeys,
|
||||
MuxStreams: false, // never multiplex, as we rely on websocket to manage distinct streams
|
||||
UseStdin: true,
|
||||
UseStdout: true,
|
||||
UseStderr: true,
|
||||
MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
|
||||
}
|
||||
|
||||
err = s.backend.ContainerAttach(containerName, attachConfig)
|
||||
@@ -778,7 +650,7 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon
|
||||
|
||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters)
|
||||
|
||||
@@ -24,17 +24,19 @@ func (pathError) Error() string {
|
||||
func (pathError) InvalidParameter() {}
|
||||
|
||||
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||
//
|
||||
// Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
||||
func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// Deprecated since 1.8, Errors out since 1.12
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := types.CopyConfig{}
|
||||
if err := httputils.ReadJSON(r, &cfg); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package container // import "github.com/docker/docker/api/server/router/containe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
@@ -37,9 +37,13 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
execConfig := &types.ExecConfig{}
|
||||
if err := httputils.ReadJSON(r, execConfig); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -47,16 +51,10 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
||||
return execCommandError{}
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.LessThan(version, "1.42") {
|
||||
// Not supported by API versions before 1.42
|
||||
execConfig.ConsoleSize = nil
|
||||
}
|
||||
|
||||
// Register an instance of Exec in container.
|
||||
id, err := s.backend.ContainerExecCreate(vars["name"], execConfig)
|
||||
id, err := s.backend.ContainerExecCreate(name, execConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", vars["name"], err)
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -72,11 +70,9 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.LessThan(version, "1.22") {
|
||||
// API versions before 1.22 did not enforce application/json content-type.
|
||||
// Allow older clients to work by patching the content-type.
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
if versions.GreaterThan(version, "1.21") {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +83,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
)
|
||||
|
||||
execStartCheck := &types.ExecStartCheck{}
|
||||
if err := httputils.ReadJSON(r, execStartCheck); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -95,18 +91,6 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
return err
|
||||
}
|
||||
|
||||
if execStartCheck.ConsoleSize != nil {
|
||||
// Not supported before 1.42
|
||||
if versions.LessThan(version, "1.42") {
|
||||
execStartCheck.ConsoleSize = nil
|
||||
}
|
||||
|
||||
// No console without tty
|
||||
if !execStartCheck.Tty {
|
||||
execStartCheck.ConsoleSize = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !execStartCheck.Detach {
|
||||
var err error
|
||||
// Setting up the streaming http interface.
|
||||
@@ -117,11 +101,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
defer httputils.CloseStreams(inStream, outStream)
|
||||
|
||||
if _, ok := r.Header["Upgrade"]; ok {
|
||||
contentType := types.MediaTypeRawStream
|
||||
if !execStartCheck.Tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
||||
contentType = types.MediaTypeMultiplexedStream
|
||||
}
|
||||
fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n")
|
||||
fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n")
|
||||
} else {
|
||||
fmt.Fprint(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n")
|
||||
}
|
||||
@@ -140,16 +120,9 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
}
|
||||
}
|
||||
|
||||
options := container.ExecStartOptions{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
ConsoleSize: execStartCheck.ConsoleSize,
|
||||
}
|
||||
|
||||
// Now run the user process in container.
|
||||
// Maybe we should we pass ctx here if we're not detaching?
|
||||
if err := s.backend.ContainerExecStart(context.Background(), execName, options); err != nil {
|
||||
if err := s.backend.ContainerExecStart(context.Background(), execName, stdin, stdout, stderr); err != nil {
|
||||
if execStartCheck.Detach {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@ import (
|
||||
// Backend is all the methods that need to be implemented
|
||||
// to provide image specific functionality.
|
||||
type Backend interface {
|
||||
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error)
|
||||
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ import (
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -43,10 +42,9 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res
|
||||
|
||||
image := vars["name"]
|
||||
|
||||
// TODO why is reference.ParseAnyReference() / reference.ParseNormalizedNamed() not using the reference.ErrTagInvalidFormat (and so on) errors?
|
||||
ref, err := reference.ParseAnyReference(image)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
return err
|
||||
}
|
||||
namedRef, ok := ref.(reference.Named)
|
||||
if !ok {
|
||||
@@ -54,10 +52,10 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res
|
||||
// full image ID
|
||||
return errors.Errorf("no manifest found for full image ID")
|
||||
}
|
||||
return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", image))
|
||||
return errors.Errorf("unknown image reference format: %s", image)
|
||||
}
|
||||
|
||||
distrepo, err := s.backend.GetRepository(ctx, namedRef, config)
|
||||
distrepo, _, err := s.backend.GetRepository(ctx, namedRef, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,7 +66,7 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res
|
||||
|
||||
taggedRef, ok := namedRef.(reference.NamedTagged)
|
||||
if !ok {
|
||||
return errdefs.InvalidParameter(errors.Errorf("image reference not tagged: %s", image))
|
||||
return errors.Errorf("image reference not tagged: %s", image)
|
||||
}
|
||||
|
||||
descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag())
|
||||
@@ -94,16 +92,6 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res
|
||||
}
|
||||
mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case reference.ErrReferenceInvalidFormat,
|
||||
reference.ErrTagInvalidFormat,
|
||||
reference.ErrDigestInvalidFormat,
|
||||
reference.ErrNameContainsUppercase,
|
||||
reference.ErrNameEmpty,
|
||||
reference.ErrNameTooLong,
|
||||
reference.ErrNameNotCanonical:
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func experimentalHandler(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
return notImplementedError{}
|
||||
}
|
||||
|
||||
// Handler returns the APIFunc to let the server wrap it in middlewares.
|
||||
// Handler returns returns the APIFunc to let the server wrap it in middlewares.
|
||||
func (r *experimentalRoute) Handler() httputils.APIFunc {
|
||||
return r.handler
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
||||
|
||||
import "google.golang.org/grpc"
|
||||
|
||||
// Backend abstracts a registerable GRPC service.
|
||||
type Backend interface {
|
||||
RegisterGRPC(*grpc.Server)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"golang.org/x/net/http2"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type grpcRouter struct {
|
||||
routes []router.Route
|
||||
grpcServer *grpc.Server
|
||||
h2Server *http2.Server
|
||||
}
|
||||
|
||||
// NewRouter initializes a new grpc http router
|
||||
func NewRouter(backends ...Backend) router.Router {
|
||||
r := &grpcRouter{
|
||||
h2Server: &http2.Server{},
|
||||
grpcServer: grpc.NewServer(
|
||||
grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor),
|
||||
grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor),
|
||||
),
|
||||
}
|
||||
for _, b := range backends {
|
||||
b.RegisterGRPC(r.grpcServer)
|
||||
}
|
||||
r.initRoutes()
|
||||
return r
|
||||
}
|
||||
|
||||
// Routes returns the available routers to the session controller
|
||||
func (gr *grpcRouter) Routes() []router.Route {
|
||||
return gr.routes
|
||||
}
|
||||
|
||||
func (gr *grpcRouter) initRoutes() {
|
||||
gr.routes = []router.Route{
|
||||
router.NewPostRoute("/grpc", gr.serveGRPC),
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func (gr *grpcRouter) serveGRPC(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
h, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return errors.New("handler does not support hijack")
|
||||
}
|
||||
proto := r.Header.Get("Upgrade")
|
||||
if proto == "" {
|
||||
return errors.New("no upgrade proto in request")
|
||||
}
|
||||
if proto != "h2c" {
|
||||
return errors.Errorf("protocol %s not supported", proto)
|
||||
}
|
||||
|
||||
conn, _, err := h.Hijack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp := &http.Response{
|
||||
StatusCode: http.StatusSwitchingProtocols,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{},
|
||||
}
|
||||
resp.Header.Set("Connection", "Upgrade")
|
||||
resp.Header.Set("Upgrade", proto)
|
||||
|
||||
// set raw mode
|
||||
conn.Write([]byte{})
|
||||
resp.Write(conn)
|
||||
|
||||
// https://godoc.org/golang.org/x/net/http2#Server.ServeConn
|
||||
// TODO: is it a problem that conn has already been written to?
|
||||
gr.h2Server.ServeConn(conn, &http2.ServeConnOpts{Handler: gr.grpcServer})
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
dockerimage "github.com/docker/docker/image"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@@ -23,20 +22,20 @@ type Backend interface {
|
||||
type imageBackend interface {
|
||||
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error)
|
||||
ImageHistory(imageName string) ([]*image.HistoryResponseItem, error)
|
||||
Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error)
|
||||
GetImage(refOrID string, platform *specs.Platform) (retImg *dockerimage.Image, retErr error)
|
||||
Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error)
|
||||
LookupImage(name string) (*types.ImageInspect, error)
|
||||
TagImage(imageName, repository, tag string) (string, error)
|
||||
ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
|
||||
}
|
||||
|
||||
type importExportBackend interface {
|
||||
LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
|
||||
ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
|
||||
ImportImage(src string, repository, platform string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
|
||||
ExportImage(names []string, outStream io.Writer) error
|
||||
}
|
||||
|
||||
type registryBackend interface {
|
||||
PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||
SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
|
||||
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
|
||||
}
|
||||
|
||||
@@ -2,28 +2,17 @@ package image // import "github.com/docker/docker/api/server/router/image"
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/reference"
|
||||
)
|
||||
|
||||
// imageRouter is a router to talk with the image controller
|
||||
type imageRouter struct {
|
||||
backend Backend
|
||||
referenceBackend reference.Store
|
||||
imageStore image.Store
|
||||
layerStore layer.Store
|
||||
routes []router.Route
|
||||
backend Backend
|
||||
routes []router.Route
|
||||
}
|
||||
|
||||
// NewRouter initializes a new image router
|
||||
func NewRouter(backend Backend, referenceBackend reference.Store, imageStore image.Store, layerStore layer.Store) router.Router {
|
||||
r := &imageRouter{
|
||||
backend: backend,
|
||||
referenceBackend: referenceBackend,
|
||||
imageStore: imageStore,
|
||||
layerStore: layerStore,
|
||||
}
|
||||
func NewRouter(backend Backend) router.Router {
|
||||
r := &imageRouter{backend: backend}
|
||||
r.initRoutes()
|
||||
return r
|
||||
}
|
||||
@@ -45,10 +34,10 @@ func (r *imageRouter) initRoutes() {
|
||||
router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
|
||||
// POST
|
||||
router.NewPostRoute("/images/load", r.postImagesLoad),
|
||||
router.NewPostRoute("/images/create", r.postImagesCreate),
|
||||
router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
|
||||
router.NewPostRoute("/images/create", r.postImagesCreate, router.WithCancel),
|
||||
router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush, router.WithCancel),
|
||||
router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
|
||||
router.NewPostRoute("/images/prune", r.postImagesPrune),
|
||||
router.NewPostRoute("/images/prune", r.postImagesPrune, router.WithCancel),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
|
||||
}
|
||||
|
||||
@@ -7,19 +7,17 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/registry"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -32,13 +30,13 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
}
|
||||
|
||||
var (
|
||||
image = r.Form.Get("fromImage")
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
progressErr error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
platform *specs.Platform
|
||||
image = r.Form.Get("fromImage")
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
platform *specs.Platform
|
||||
)
|
||||
defer output.Close()
|
||||
|
||||
@@ -46,43 +44,56 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
if p := r.FormValue("platform"); p != "" {
|
||||
sp, err := platforms.Parse(p)
|
||||
apiPlatform := r.FormValue("platform")
|
||||
if apiPlatform != "" {
|
||||
sp, err := platforms.Parse(apiPlatform)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
return err
|
||||
}
|
||||
if err := system.ValidatePlatform(sp); err != nil {
|
||||
return err
|
||||
}
|
||||
platform = &sp
|
||||
}
|
||||
}
|
||||
|
||||
if image != "" { // pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
if err == nil {
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &types.AuthConfig{}
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &types.AuthConfig{}
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &types.AuthConfig{}
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &types.AuthConfig{}
|
||||
}
|
||||
}
|
||||
err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
|
||||
} else { //import
|
||||
src := r.Form.Get("fromSrc")
|
||||
// 'err' MUST NOT be defined within this block, we need any error
|
||||
// generated from the download to be available to the output
|
||||
// stream processing below
|
||||
os := ""
|
||||
if platform != nil {
|
||||
os = platform.OS
|
||||
}
|
||||
err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"])
|
||||
}
|
||||
progressErr = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
|
||||
} else { // import
|
||||
src := r.Form.Get("fromSrc")
|
||||
progressErr = s.backend.ImportImage(src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
|
||||
}
|
||||
if progressErr != nil {
|
||||
if err != nil {
|
||||
if !output.Flushed() {
|
||||
return progressErr
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(progressErr))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -127,7 +138,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -152,7 +163,7 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -168,7 +179,7 @@ func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter,
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -204,12 +215,7 @@ func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
image, err := s.backend.GetImage(vars["name"], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imageInspect, err := s.toImageInspect(image)
|
||||
imageInspect, err := s.backend.LookupImage(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -217,85 +223,6 @@ func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter
|
||||
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
|
||||
}
|
||||
|
||||
func (s *imageRouter) toImageInspect(img *image.Image) (*types.ImageInspect, error) {
|
||||
refs := s.referenceBackend.References(img.ID().Digest())
|
||||
repoTags := []string{}
|
||||
repoDigests := []string{}
|
||||
for _, ref := range refs {
|
||||
switch ref.(type) {
|
||||
case reference.NamedTagged:
|
||||
repoTags = append(repoTags, reference.FamiliarString(ref))
|
||||
case reference.Canonical:
|
||||
repoDigests = append(repoDigests, reference.FamiliarString(ref))
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
var layerMetadata map[string]string
|
||||
layerID := img.RootFS.ChainID()
|
||||
if layerID != "" {
|
||||
l, err := s.layerStore.Get(layerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer layer.ReleaseAndLog(s.layerStore, l)
|
||||
size = l.Size()
|
||||
layerMetadata, err = l.Metadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comment := img.Comment
|
||||
if len(comment) == 0 && len(img.History) > 0 {
|
||||
comment = img.History[len(img.History)-1].Comment
|
||||
}
|
||||
|
||||
lastUpdated, err := s.imageStore.GetLastUpdated(img.ID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.ImageInspect{
|
||||
ID: img.ID().String(),
|
||||
RepoTags: repoTags,
|
||||
RepoDigests: repoDigests,
|
||||
Parent: img.Parent.String(),
|
||||
Comment: comment,
|
||||
Created: img.Created.Format(time.RFC3339Nano),
|
||||
Container: img.Container,
|
||||
ContainerConfig: &img.ContainerConfig,
|
||||
DockerVersion: img.DockerVersion,
|
||||
Author: img.Author,
|
||||
Config: img.Config,
|
||||
Architecture: img.Architecture,
|
||||
Variant: img.Variant,
|
||||
Os: img.OperatingSystem(),
|
||||
OsVersion: img.OSVersion,
|
||||
Size: size,
|
||||
VirtualSize: size, // TODO: field unused, deprecate
|
||||
GraphDriver: types.GraphDriverData{
|
||||
Name: s.layerStore.DriverName(),
|
||||
Data: layerMetadata,
|
||||
},
|
||||
RootFS: rootFSToAPIType(img.RootFS),
|
||||
Metadata: types.ImageMetadata{
|
||||
LastTagTime: lastUpdated,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
|
||||
var layers []string
|
||||
for _, l := range rootfs.DiffIDs {
|
||||
layers = append(layers, l.String())
|
||||
}
|
||||
return types.RootFS{
|
||||
Type: rootfs.Type,
|
||||
Layers: layers,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
@@ -306,26 +233,13 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
|
||||
return err
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.LessThan(version, "1.41") {
|
||||
// NOTE: filter is a shell glob string applied to repository names.
|
||||
filterParam := r.Form.Get("filter")
|
||||
if filterParam != "" {
|
||||
imageFilters.Add("reference", filterParam)
|
||||
}
|
||||
filterParam := r.Form.Get("filter")
|
||||
// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
|
||||
if filterParam != "" {
|
||||
imageFilters.Add("reference", filterParam)
|
||||
}
|
||||
|
||||
var sharedSize bool
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
// NOTE: Support for the "shared-size" parameter was added in API 1.42.
|
||||
sharedSize = httputils.BoolValue(r, "shared-size")
|
||||
}
|
||||
|
||||
images, err := s.backend.Images(ctx, types.ImageListOptions{
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Filters: imageFilters,
|
||||
SharedSize: sharedSize,
|
||||
})
|
||||
images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -377,21 +291,15 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
|
||||
headers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
var limit int
|
||||
limit := registry.DefaultSearchLimit
|
||||
if r.Form.Get("limit") != "" {
|
||||
var err error
|
||||
limit, err = strconv.Atoi(r.Form.Get("limit"))
|
||||
if err != nil || limit < 0 {
|
||||
return errdefs.InvalidParameter(errors.Wrap(err, "invalid limit specified"))
|
||||
limitValue, err := strconv.Atoi(r.Form.Get("limit"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limit = limitValue
|
||||
}
|
||||
searchFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query, err := s.backend.SearchRegistryForImages(ctx, searchFilters, r.Form.Get("term"), limit, config, headers)
|
||||
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package router // import "github.com/docker/docker/api/server/router"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
@@ -44,30 +45,60 @@ func NewRoute(method, path string, handler httputils.APIFunc, opts ...RouteWrapp
|
||||
|
||||
// NewGetRoute initializes a new route with the http method GET.
|
||||
func NewGetRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodGet, path, handler, opts...)
|
||||
return NewRoute("GET", path, handler, opts...)
|
||||
}
|
||||
|
||||
// NewPostRoute initializes a new route with the http method POST.
|
||||
func NewPostRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodPost, path, handler, opts...)
|
||||
return NewRoute("POST", path, handler, opts...)
|
||||
}
|
||||
|
||||
// NewPutRoute initializes a new route with the http method PUT.
|
||||
func NewPutRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodPut, path, handler, opts...)
|
||||
return NewRoute("PUT", path, handler, opts...)
|
||||
}
|
||||
|
||||
// NewDeleteRoute initializes a new route with the http method DELETE.
|
||||
func NewDeleteRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodDelete, path, handler, opts...)
|
||||
return NewRoute("DELETE", path, handler, opts...)
|
||||
}
|
||||
|
||||
// NewOptionsRoute initializes a new route with the http method OPTIONS.
|
||||
func NewOptionsRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodOptions, path, handler, opts...)
|
||||
return NewRoute("OPTIONS", path, handler, opts...)
|
||||
}
|
||||
|
||||
// NewHeadRoute initializes a new route with the http method HEAD.
|
||||
func NewHeadRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route {
|
||||
return NewRoute(http.MethodHead, path, handler, opts...)
|
||||
return NewRoute("HEAD", path, handler, opts...)
|
||||
}
|
||||
|
||||
func cancellableHandler(h httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||
notify := notifier.CloseNotify()
|
||||
notifyCtx, cancel := context.WithCancel(ctx)
|
||||
finished := make(chan struct{})
|
||||
defer close(finished)
|
||||
ctx = notifyCtx
|
||||
go func() {
|
||||
select {
|
||||
case <-notify:
|
||||
cancel()
|
||||
case <-finished:
|
||||
}
|
||||
}()
|
||||
}
|
||||
return h(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
|
||||
// WithCancel makes new route which embeds http.CloseNotifier feature to
|
||||
// context.Context of handler.
|
||||
func WithCancel(r Route) Route {
|
||||
return localRoute{
|
||||
method: r.Method(),
|
||||
path: r.Path(),
|
||||
handler: cancellableHandler(r.Handler()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
// Backend is all the methods that need to be implemented
|
||||
|
||||
@@ -36,7 +36,7 @@ func (r *networkRouter) initRoutes() {
|
||||
router.NewPostRoute("/networks/create", r.postNetworkCreate),
|
||||
router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
|
||||
router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
|
||||
router.NewPostRoute("/networks/prune", r.postNetworksPrune),
|
||||
router.NewPostRoute("/networks/prune", r.postNetworksPrune, router.WithCancel),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package network // import "github.com/docker/docker/api/server/router/network"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,8 +13,8 @@ import (
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
netconst "github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork"
|
||||
netconst "github.com/docker/libnetwork/datastore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -203,12 +204,17 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var create types.NetworkCreateRequest
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var create types.NetworkCreateRequest
|
||||
if err := httputils.ReadJSON(r, &create); err != nil {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -245,12 +251,16 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
|
||||
}
|
||||
|
||||
func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var connect types.NetworkConnect
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var connect types.NetworkConnect
|
||||
if err := httputils.ReadJSON(r, &connect); err != nil {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -262,12 +272,16 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
|
||||
}
|
||||
|
||||
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var disconnect types.NetworkDisconnect
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var disconnect types.NetworkDisconnect
|
||||
if err := httputils.ReadJSON(r, &disconnect); err != nil {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ func (r *pluginRouter) initRoutes() {
|
||||
router.NewGetRoute("/plugins/{name:.*}/json", r.inspectPlugin),
|
||||
router.NewGetRoute("/plugins/privileges", r.getPrivileges),
|
||||
router.NewDeleteRoute("/plugins/{name:.*}", r.removePlugin),
|
||||
router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin),
|
||||
router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin), // PATCH?
|
||||
router.NewPostRoute("/plugins/{name:.*}/disable", r.disablePlugin),
|
||||
router.NewPostRoute("/plugins/pull", r.pullPlugin),
|
||||
router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin),
|
||||
router.NewPostRoute("/plugins/{name:.*}/upgrade", r.upgradePlugin),
|
||||
router.NewPostRoute("/plugins/pull", r.pullPlugin, router.WithCancel),
|
||||
router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin, router.WithCancel),
|
||||
router.NewPostRoute("/plugins/{name:.*}/upgrade", r.upgradePlugin, router.WithCancel),
|
||||
router.NewPostRoute("/plugins/{name:.*}/set", r.setPlugin),
|
||||
router.NewPostRoute("/plugins/create", r.createPlugin),
|
||||
}
|
||||
|
||||
@@ -94,8 +94,12 @@ func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter
|
||||
}
|
||||
|
||||
var privileges types.PluginPrivileges
|
||||
if err := httputils.ReadJSON(r, &privileges); err != nil {
|
||||
return err
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&privileges); err != nil {
|
||||
return errors.Wrap(err, "failed to parse privileges")
|
||||
}
|
||||
if dec.More() {
|
||||
return errors.New("invalid privileges")
|
||||
}
|
||||
|
||||
metaHeaders, authConfig := parseHeaders(r.Header)
|
||||
@@ -117,7 +121,7 @@ func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -129,8 +133,12 @@ func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
var privileges types.PluginPrivileges
|
||||
if err := httputils.ReadJSON(r, &privileges); err != nil {
|
||||
return err
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&privileges); err != nil {
|
||||
return errors.Wrap(err, "failed to parse privileges")
|
||||
}
|
||||
if dec.More() {
|
||||
return errors.New("invalid privileges")
|
||||
}
|
||||
|
||||
metaHeaders, authConfig := parseHeaders(r.Header)
|
||||
@@ -152,7 +160,7 @@ func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -201,7 +209,7 @@ func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter,
|
||||
if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: send progress bar
|
||||
//TODO: send progress bar
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
@@ -260,14 +268,14 @@ func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, _ = output.Write(streamformatter.FormatError(err))
|
||||
output.Write(streamformatter.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var args []string
|
||||
if err := httputils.ReadJSON(r, &args); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pr.backend.Set(vars["name"], args); err != nil {
|
||||
|
||||
@@ -37,7 +37,7 @@ func (sr *swarmRouter) initRoutes() {
|
||||
router.NewPostRoute("/services/create", sr.createService),
|
||||
router.NewPostRoute("/services/{id}/update", sr.updateService),
|
||||
router.NewDeleteRoute("/services/{id}", sr.removeService),
|
||||
router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs),
|
||||
router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs, router.WithCancel),
|
||||
|
||||
router.NewGetRoute("/nodes", sr.getNodes),
|
||||
router.NewGetRoute("/nodes/{id}", sr.getNode),
|
||||
@@ -46,7 +46,7 @@ func (sr *swarmRouter) initRoutes() {
|
||||
|
||||
router.NewGetRoute("/tasks", sr.getTasks),
|
||||
router.NewGetRoute("/tasks/{id}", sr.getTask),
|
||||
router.NewGetRoute("/tasks/{id}/logs", sr.getTaskLogs),
|
||||
router.NewGetRoute("/tasks/{id}/logs", sr.getTaskLogs, router.WithCancel),
|
||||
|
||||
router.NewGetRoute("/secrets", sr.getSecrets),
|
||||
router.NewPostRoute("/secrets/create", sr.createSecret),
|
||||
|
||||
@@ -2,6 +2,7 @@ package swarm // import "github.com/docker/docker/api/server/router/swarm"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -19,23 +20,12 @@ import (
|
||||
|
||||
func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.InitRequest
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
// DefaultAddrPool and SubnetSize were added in API 1.39. Ignore on older API versions.
|
||||
if versions.LessThan(version, "1.39") {
|
||||
req.DefaultAddrPool = nil
|
||||
req.SubnetSize = 0
|
||||
}
|
||||
// DataPathPort was added in API 1.40. Ignore this option on older API versions.
|
||||
if versions.LessThan(version, "1.40") {
|
||||
req.DataPathPort = 0
|
||||
}
|
||||
nodeID, err := sr.backend.Init(req)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error initializing swarm")
|
||||
logrus.Errorf("Error initializing swarm: %v", err)
|
||||
return err
|
||||
}
|
||||
return httputils.WriteJSON(w, http.StatusOK, nodeID)
|
||||
@@ -43,7 +33,7 @@ func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.JoinRequest
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
return sr.backend.Join(req)
|
||||
@@ -61,7 +51,7 @@ func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter,
|
||||
func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
swarm, err := sr.backend.Inspect()
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error getting swarm")
|
||||
logrus.Errorf("Error getting swarm: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -70,7 +60,7 @@ func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter
|
||||
|
||||
func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var swarm types.Spec
|
||||
if err := httputils.ReadJSON(r, &swarm); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&swarm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -113,7 +103,7 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
if err := sr.backend.Update(version, swarm, flags); err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error configuring swarm")
|
||||
logrus.Errorf("Error configuring swarm: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -121,12 +111,12 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
|
||||
|
||||
func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var req types.UnlockRequest
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sr.backend.UnlockSwarm(req); err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error unlocking swarm")
|
||||
logrus.Errorf("Error unlocking swarm: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -135,7 +125,7 @@ func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter,
|
||||
func (sr *swarmRouter) getUnlockKey(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
unlockKey, err := sr.backend.GetUnlockKey()
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error retrieving swarm unlock key")
|
||||
logrus.WithError(err).Errorf("Error retrieving swarm unlock key")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -150,24 +140,12 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
// the status query parameter is only support in API versions >= 1.41. If
|
||||
// the client is using a lesser version, ignore the parameter.
|
||||
cliVersion := httputils.VersionFromContext(ctx)
|
||||
var status bool
|
||||
if value := r.URL.Query().Get("status"); value != "" && !versions.LessThan(cliVersion, "1.41") {
|
||||
var err error
|
||||
status, err = strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for status: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter, Status: status})
|
||||
services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error getting services")
|
||||
logrus.Errorf("Error getting services: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -176,27 +154,18 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var insertDefaults bool
|
||||
|
||||
if value := r.URL.Query().Get("insertDefaults"); value != "" {
|
||||
var err error
|
||||
insertDefaults, err = strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("invalid value for insertDefaults: %s", value)
|
||||
return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
// you may note that there is no code here to handle the "status" query
|
||||
// parameter, as in getServices. the Status field is not supported when
|
||||
// retrieving an individual service because the Backend API changes
|
||||
// required to accommodate it would be too disruptive, and because that
|
||||
// field is so rarely needed as part of an individual service inspection.
|
||||
|
||||
service, err := sr.backend.GetService(vars["id"], insertDefaults)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"service-id": vars["id"],
|
||||
}).Debug("Error getting service")
|
||||
logrus.Errorf("Error getting service %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -205,25 +174,21 @@ func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var service types.ServiceSpec
|
||||
if err := httputils.ReadJSON(r, &service); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns "" if the header does not exist
|
||||
encodedAuth := r.Header.Get("X-Registry-Auth")
|
||||
cliVersion := r.Header.Get("version")
|
||||
queryRegistry := false
|
||||
if v := httputils.VersionFromContext(ctx); v != "" {
|
||||
if versions.LessThan(v, "1.30") {
|
||||
queryRegistry = true
|
||||
}
|
||||
adjustForAPIVersion(v, &service)
|
||||
if cliVersion != "" && versions.LessThan(cliVersion, "1.30") {
|
||||
queryRegistry = true
|
||||
}
|
||||
|
||||
resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"service-name": service.Name,
|
||||
}).Debug("Error creating service")
|
||||
logrus.Errorf("Error creating service %s: %v", service.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -232,7 +197,7 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
|
||||
|
||||
func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var service types.ServiceSpec
|
||||
if err := httputils.ReadJSON(r, &service); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -249,20 +214,15 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
|
||||
flags.EncodedRegistryAuth = r.Header.Get("X-Registry-Auth")
|
||||
flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom")
|
||||
flags.Rollback = r.URL.Query().Get("rollback")
|
||||
cliVersion := r.Header.Get("version")
|
||||
queryRegistry := false
|
||||
if v := httputils.VersionFromContext(ctx); v != "" {
|
||||
if versions.LessThan(v, "1.30") {
|
||||
queryRegistry = true
|
||||
}
|
||||
adjustForAPIVersion(v, &service)
|
||||
if cliVersion != "" && versions.LessThan(cliVersion, "1.30") {
|
||||
queryRegistry = true
|
||||
}
|
||||
|
||||
resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"service-id": vars["id"],
|
||||
}).Debug("Error updating service")
|
||||
logrus.Errorf("Error updating service %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
return httputils.WriteJSON(w, http.StatusOK, resp)
|
||||
@@ -270,10 +230,7 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
|
||||
|
||||
func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := sr.backend.RemoveService(vars["id"]); err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"service-id": vars["id"],
|
||||
}).Debug("Error removing service")
|
||||
logrus.Errorf("Error removing service %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -314,7 +271,7 @@ func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *h
|
||||
|
||||
nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error getting nodes")
|
||||
logrus.Errorf("Error getting nodes: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -324,10 +281,7 @@ func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *h
|
||||
func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
node, err := sr.backend.GetNode(vars["id"])
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"node-id": vars["id"],
|
||||
}).Debug("Error getting node")
|
||||
logrus.Errorf("Error getting node %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -336,7 +290,7 @@ func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
|
||||
func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var node types.NodeSpec
|
||||
if err := httputils.ReadJSON(r, &node); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -348,10 +302,7 @@ func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"node-id": vars["id"],
|
||||
}).Debug("Error updating node")
|
||||
logrus.Errorf("Error updating node %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -365,10 +316,7 @@ func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r
|
||||
force := httputils.BoolValue(r, "force")
|
||||
|
||||
if err := sr.backend.RemoveNode(vars["id"], force); err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"node-id": vars["id"],
|
||||
}).Debug("Error removing node")
|
||||
logrus.Errorf("Error removing node %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -385,7 +333,7 @@ func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *h
|
||||
|
||||
tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithError(err).Debug("Error getting tasks")
|
||||
logrus.Errorf("Error getting tasks: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -395,10 +343,7 @@ func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *h
|
||||
func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
task, err := sr.backend.GetTask(vars["id"])
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"task-id": vars["id"],
|
||||
}).Debug("Error getting task")
|
||||
logrus.Errorf("Error getting task %s: %v", vars["id"], err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -424,7 +369,7 @@ func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var secret types.SecretSpec
|
||||
if err := httputils.ReadJSON(r, &secret); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
@@ -462,8 +407,8 @@ func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *
|
||||
|
||||
func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var secret types.SecretSpec
|
||||
if err := httputils.ReadJSON(r, &secret); err != nil {
|
||||
return err
|
||||
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
@@ -495,7 +440,7 @@ func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := httputils.ReadJSON(r, &config); err != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -534,8 +479,8 @@ func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *
|
||||
|
||||
func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := httputils.ReadJSON(r, &config); err != nil {
|
||||
return err
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
|
||||
@@ -3,18 +3,17 @@ package swarm // import "github.com/docker/docker/api/server/router/swarm"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
basictypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
)
|
||||
|
||||
// swarmLogs takes an http response, request, and selector, and writes the logs
|
||||
// specified by the selector to the response
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, selector *backend.LogSelector) error {
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w io.Writer, r *http.Request, selector *backend.LogSelector) error {
|
||||
// Args are validated before the stream starts because when it starts we're
|
||||
// sending HTTP 200 by writing an empty chunk of data to tell the client that
|
||||
// daemon is going to stream. By sending this initial HTTP 200 we can't report
|
||||
@@ -62,60 +61,6 @@ func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *
|
||||
return err
|
||||
}
|
||||
|
||||
contentType := basictypes.MediaTypeRawStream
|
||||
if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
||||
contentType = basictypes.MediaTypeMultiplexedStream
|
||||
}
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty)
|
||||
return nil
|
||||
}
|
||||
|
||||
// adjustForAPIVersion takes a version and service spec and removes fields to
|
||||
// make the spec compatible with the specified version.
|
||||
func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) {
|
||||
if cliVersion == "" {
|
||||
return
|
||||
}
|
||||
if versions.LessThan(cliVersion, "1.40") {
|
||||
if service.TaskTemplate.ContainerSpec != nil {
|
||||
// Sysctls for docker swarm services weren't supported before
|
||||
// API version 1.40
|
||||
service.TaskTemplate.ContainerSpec.Sysctls = nil
|
||||
|
||||
if service.TaskTemplate.ContainerSpec.Privileges != nil && service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec != nil {
|
||||
// Support for setting credential-spec through configs was added in API 1.40
|
||||
service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = ""
|
||||
}
|
||||
for _, config := range service.TaskTemplate.ContainerSpec.Configs {
|
||||
// support for the Runtime target was added in API 1.40
|
||||
config.Runtime = nil
|
||||
}
|
||||
}
|
||||
|
||||
if service.TaskTemplate.Placement != nil {
|
||||
// MaxReplicas for docker swarm services weren't supported before
|
||||
// API version 1.40
|
||||
service.TaskTemplate.Placement.MaxReplicas = 0
|
||||
}
|
||||
}
|
||||
if versions.LessThan(cliVersion, "1.41") {
|
||||
if service.TaskTemplate.ContainerSpec != nil {
|
||||
// Capabilities and Ulimits for docker swarm services weren't
|
||||
// supported before API version 1.41
|
||||
service.TaskTemplate.ContainerSpec.CapabilityAdd = nil
|
||||
service.TaskTemplate.ContainerSpec.CapabilityDrop = nil
|
||||
service.TaskTemplate.ContainerSpec.Ulimits = nil
|
||||
}
|
||||
if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil {
|
||||
// Limits.Pids not supported before API version 1.41
|
||||
service.TaskTemplate.Resources.Limits.Pids = 0
|
||||
}
|
||||
|
||||
// jobs were only introduced in API version 1.41. Nil out both Job
|
||||
// modes; if the service is one of these modes and subsequently has no
|
||||
// mode, then something down the pipe will thrown an error.
|
||||
service.Mode.ReplicatedJob = nil
|
||||
service.Mode.GlobalJob = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
package swarm // import "github.com/docker/docker/api/server/router/swarm"
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
func TestAdjustForAPIVersion(t *testing.T) {
|
||||
var (
|
||||
expectedSysctls = map[string]string{"foo": "bar"}
|
||||
)
|
||||
// testing the negative -- does this leave everything else alone? -- is
|
||||
// prohibitively time-consuming to write, because it would need an object
|
||||
// with literally every field filled in.
|
||||
spec := &swarm.ServiceSpec{
|
||||
TaskTemplate: swarm.TaskSpec{
|
||||
ContainerSpec: &swarm.ContainerSpec{
|
||||
Sysctls: expectedSysctls,
|
||||
Privileges: &swarm.Privileges{
|
||||
CredentialSpec: &swarm.CredentialSpec{
|
||||
Config: "someconfig",
|
||||
},
|
||||
},
|
||||
Configs: []*swarm.ConfigReference{
|
||||
{
|
||||
File: &swarm.ConfigReferenceFileTarget{
|
||||
Name: "foo",
|
||||
UID: "bar",
|
||||
GID: "baz",
|
||||
},
|
||||
ConfigID: "configFile",
|
||||
ConfigName: "configFile",
|
||||
},
|
||||
{
|
||||
Runtime: &swarm.ConfigReferenceRuntimeTarget{},
|
||||
ConfigID: "configRuntime",
|
||||
ConfigName: "configRuntime",
|
||||
},
|
||||
},
|
||||
Ulimits: []*units.Ulimit{
|
||||
{
|
||||
Name: "nofile",
|
||||
Soft: 100,
|
||||
Hard: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
Placement: &swarm.Placement{
|
||||
MaxReplicas: 222,
|
||||
},
|
||||
Resources: &swarm.ResourceRequirements{
|
||||
Limits: &swarm.Limit{
|
||||
Pids: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// first, does calling this with a later version correctly NOT strip
|
||||
// fields? do the later version first, so we can reuse this spec in the
|
||||
// next test.
|
||||
adjustForAPIVersion("1.41", spec)
|
||||
if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls) {
|
||||
t.Error("Sysctls was stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.Resources.Limits.Pids == 0 {
|
||||
t.Error("PidsLimit was stripped from spec")
|
||||
}
|
||||
if spec.TaskTemplate.Resources.Limits.Pids != 300 {
|
||||
t.Error("PidsLimit did not preserve the value from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "someconfig" {
|
||||
t.Error("CredentialSpec.Config field was stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Configs[1].Runtime == nil {
|
||||
t.Error("ConfigReferenceRuntimeTarget was stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.Placement.MaxReplicas != 222 {
|
||||
t.Error("MaxReplicas was stripped from spec")
|
||||
}
|
||||
|
||||
if len(spec.TaskTemplate.ContainerSpec.Ulimits) == 0 {
|
||||
t.Error("Ulimits were stripped from spec")
|
||||
}
|
||||
|
||||
// next, does calling this with an earlier version correctly strip fields?
|
||||
adjustForAPIVersion("1.29", spec)
|
||||
if spec.TaskTemplate.ContainerSpec.Sysctls != nil {
|
||||
t.Error("Sysctls was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.Resources.Limits.Pids != 0 {
|
||||
t.Error("PidsLimit was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "" {
|
||||
t.Error("CredentialSpec.Config field was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Configs[1].Runtime != nil {
|
||||
t.Error("ConfigReferenceRuntimeTarget was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.Placement.MaxReplicas != 0 {
|
||||
t.Error("MaxReplicas was not stripped from spec")
|
||||
}
|
||||
|
||||
if len(spec.TaskTemplate.ContainerSpec.Ulimits) != 0 {
|
||||
t.Error("Ulimits were not stripped from spec")
|
||||
}
|
||||
}
|
||||
@@ -10,24 +10,12 @@ import (
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// DiskUsageOptions holds parameters for system disk usage query.
|
||||
type DiskUsageOptions struct {
|
||||
// Containers controls whether container disk usage should be computed.
|
||||
Containers bool
|
||||
|
||||
// Images controls whether image disk usage should be computed.
|
||||
Images bool
|
||||
|
||||
// Volumes controls whether volume disk usage should be computed.
|
||||
Volumes bool
|
||||
}
|
||||
|
||||
// Backend is the methods that need to be implemented to provide
|
||||
// system specific functionality.
|
||||
type Backend interface {
|
||||
SystemInfo() *types.Info
|
||||
SystemInfo() (*types.Info, error)
|
||||
SystemVersion() types.Version
|
||||
SystemDiskUsage(ctx context.Context, opts DiskUsageOptions) (*types.DiskUsage, error)
|
||||
SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error)
|
||||
SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
|
||||
UnsubscribeFromEvents(chan interface{})
|
||||
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
|
||||
@@ -38,8 +26,3 @@ type Backend interface {
|
||||
type ClusterBackend interface {
|
||||
Info() swarm.Info
|
||||
}
|
||||
|
||||
// StatusProvider provides methods to get the swarm status of the current node.
|
||||
type StatusProvider interface {
|
||||
Status() string
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package system // import "github.com/docker/docker/api/server/router/system"
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
buildkit "github.com/docker/docker/builder/builder-next"
|
||||
"github.com/docker/docker/builder/builder-next"
|
||||
"github.com/docker/docker/builder/fscache"
|
||||
)
|
||||
|
||||
// systemRouter provides information about the Docker system overall.
|
||||
@@ -11,15 +12,17 @@ type systemRouter struct {
|
||||
backend Backend
|
||||
cluster ClusterBackend
|
||||
routes []router.Route
|
||||
fscache *fscache.FSCache // legacy
|
||||
builder *buildkit.Builder
|
||||
features *map[string]bool
|
||||
}
|
||||
|
||||
// NewRouter initializes a new system router
|
||||
func NewRouter(b Backend, c ClusterBackend, builder *buildkit.Builder, features *map[string]bool) router.Router {
|
||||
func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder, features *map[string]bool) router.Router {
|
||||
r := &systemRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
fscache: fscache,
|
||||
builder: builder,
|
||||
features: features,
|
||||
}
|
||||
@@ -27,11 +30,10 @@ func NewRouter(b Backend, c ClusterBackend, builder *buildkit.Builder, features
|
||||
r.routes = []router.Route{
|
||||
router.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
|
||||
router.NewGetRoute("/_ping", r.pingHandler),
|
||||
router.NewHeadRoute("/_ping", r.pingHandler),
|
||||
router.NewGetRoute("/events", r.getEvents),
|
||||
router.NewGetRoute("/events", r.getEvents, router.WithCancel),
|
||||
router.NewGetRoute("/info", r.getInfo),
|
||||
router.NewGetRoute("/version", r.getVersion),
|
||||
router.NewGetRoute("/system/df", r.getDiskUsage),
|
||||
router.NewGetRoute("/system/df", r.getDiskUsage, router.WithCancel),
|
||||
router.NewPostRoute("/auth", r.postAuth),
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@ import (
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
timetypes "github.com/docker/docker/api/types/time"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/pkg/errors"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -28,44 +27,24 @@ func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Add("Pragma", "no-cache")
|
||||
|
||||
builderVersion := build.BuilderVersion(*s.features)
|
||||
if bv := builderVersion; bv != "" {
|
||||
w.Header().Set("Builder-Version", string(bv))
|
||||
}
|
||||
|
||||
w.Header().Set("Swarm", s.swarmStatus())
|
||||
|
||||
if r.Method == http.MethodHead {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Header().Set("Content-Length", "0")
|
||||
return nil
|
||||
}
|
||||
_, err := w.Write([]byte{'O', 'K'})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *systemRouter) swarmStatus() string {
|
||||
if s.cluster != nil {
|
||||
if p, ok := s.cluster.(StatusProvider); ok {
|
||||
return p.Status()
|
||||
}
|
||||
}
|
||||
return string(swarm.LocalNodeStateInactive)
|
||||
}
|
||||
|
||||
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
info := s.backend.SystemInfo()
|
||||
|
||||
info, err := s.backend.SystemInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.cluster != nil {
|
||||
info.Swarm = s.cluster.Info()
|
||||
info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.LessThan(version, "1.25") {
|
||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
|
||||
// TODO: handle this conversion in engine-api
|
||||
type oldInfo struct {
|
||||
*types.Info
|
||||
@@ -86,7 +65,7 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
old.SecurityOptions = nameOnlySecurityOptions
|
||||
return httputils.WriteJSON(w, http.StatusOK, old)
|
||||
}
|
||||
if versions.LessThan(version, "1.39") {
|
||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.39") {
|
||||
if info.KernelVersion == "" {
|
||||
info.KernelVersion = "<unknown>"
|
||||
}
|
||||
@@ -94,9 +73,6 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
info.OperatingSystem = "<unknown>"
|
||||
}
|
||||
}
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
info.KernelMemory = false
|
||||
}
|
||||
return httputils.WriteJSON(w, http.StatusOK, info)
|
||||
}
|
||||
|
||||
@@ -107,95 +83,46 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
var getContainers, getImages, getVolumes, getBuildCache bool
|
||||
typeStrs, ok := r.Form["type"]
|
||||
if versions.LessThan(version, "1.42") || !ok {
|
||||
getContainers, getImages, getVolumes, getBuildCache = true, true, true, true
|
||||
} else {
|
||||
for _, typ := range typeStrs {
|
||||
switch types.DiskUsageObject(typ) {
|
||||
case types.ContainerObject:
|
||||
getContainers = true
|
||||
case types.ImageObject:
|
||||
getImages = true
|
||||
case types.VolumeObject:
|
||||
getVolumes = true
|
||||
case types.BuildCacheObject:
|
||||
getBuildCache = true
|
||||
default:
|
||||
return invalidRequestError{Err: fmt.Errorf("unknown object type: %s", typ)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var systemDiskUsage *types.DiskUsage
|
||||
if getContainers || getImages || getVolumes {
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, DiskUsageOptions{
|
||||
Containers: getContainers,
|
||||
Images: getImages,
|
||||
Volumes: getVolumes,
|
||||
})
|
||||
return err
|
||||
})
|
||||
}
|
||||
var du *types.DiskUsage
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
du, err = s.backend.SystemDiskUsage(ctx)
|
||||
return err
|
||||
})
|
||||
|
||||
var builderSize int64 // legacy
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
builderSize, err = s.fscache.DiskUsage(ctx)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "error getting fscache build cache usage")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var buildCache []*types.BuildCache
|
||||
if getBuildCache {
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
buildCache, err = s.builder.DiskUsage(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting build cache usage")
|
||||
}
|
||||
if buildCache == nil {
|
||||
// Ensure empty `BuildCache` field is represented as empty JSON array(`[]`)
|
||||
// instead of `null` to be consistent with `Images`, `Containers` etc.
|
||||
buildCache = []*types.BuildCache{}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
buildCache, err = s.builder.DiskUsage(ctx)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "error getting build cache usage")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var builderSize int64
|
||||
if versions.LessThan(version, "1.42") {
|
||||
for _, b := range buildCache {
|
||||
builderSize += b.Size
|
||||
// Parents field was added in API 1.42 to replace the Parent field.
|
||||
b.Parents = nil
|
||||
}
|
||||
}
|
||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
||||
for _, b := range buildCache {
|
||||
// Parent field is deprecated in API v1.42 and up, as it is deprecated
|
||||
// in BuildKit. Empty the field to omit it in the API response.
|
||||
b.Parent = "" //nolint:staticcheck // ignore SA1019 (Parent field is deprecated)
|
||||
}
|
||||
for _, b := range buildCache {
|
||||
builderSize += b.Size
|
||||
}
|
||||
|
||||
du := types.DiskUsage{
|
||||
BuildCache: buildCache,
|
||||
BuilderSize: builderSize,
|
||||
}
|
||||
if systemDiskUsage != nil {
|
||||
du.LayersSize = systemDiskUsage.LayersSize
|
||||
du.Images = systemDiskUsage.Images
|
||||
du.Containers = systemDiskUsage.Containers
|
||||
du.Volumes = systemDiskUsage.Volumes
|
||||
}
|
||||
du.BuilderSize = builderSize
|
||||
du.BuildCache = buildCache
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, du)
|
||||
}
|
||||
|
||||
@@ -238,9 +165,7 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
|
||||
|
||||
if !onlyPastEvents {
|
||||
dur := until.Sub(now)
|
||||
timer := time.NewTimer(dur)
|
||||
defer timer.Stop()
|
||||
timeout = timer.C
|
||||
timeout = time.After(dur)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,28 +7,14 @@ import (
|
||||
// TODO return types need to be refactored into pkg
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
)
|
||||
|
||||
// Backend is the methods that need to be implemented to provide
|
||||
// volume specific functionality
|
||||
type Backend interface {
|
||||
List(ctx context.Context, filter filters.Args) ([]*volume.Volume, []string, error)
|
||||
Get(ctx context.Context, name string, opts ...opts.GetOption) (*volume.Volume, error)
|
||||
Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*volume.Volume, error)
|
||||
List(ctx context.Context, filter filters.Args) ([]*types.Volume, []string, error)
|
||||
Get(ctx context.Context, name string, opts ...opts.GetOption) (*types.Volume, error)
|
||||
Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*types.Volume, error)
|
||||
Remove(ctx context.Context, name string, opts ...opts.RemoveOption) error
|
||||
Prune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error)
|
||||
}
|
||||
|
||||
// ClusterBackend is the backend used for Swarm Cluster Volumes. Regular
|
||||
// volumes go through the volume service, but to avoid across-dependency
|
||||
// between the cluster package and the volume package, we simply provide two
|
||||
// backends here.
|
||||
type ClusterBackend interface {
|
||||
GetVolume(nameOrID string) (volume.Volume, error)
|
||||
GetVolumes(options volume.ListOptions) ([]*volume.Volume, error)
|
||||
CreateVolume(volume volume.CreateOptions) (*volume.Volume, error)
|
||||
RemoveVolume(nameOrID string, force bool) error
|
||||
UpdateVolume(nameOrID string, version uint64, volume volume.UpdateOptions) error
|
||||
IsManager() bool
|
||||
}
|
||||
|
||||
@@ -5,15 +5,13 @@ import "github.com/docker/docker/api/server/router"
|
||||
// volumeRouter is a router to talk with the volumes controller
|
||||
type volumeRouter struct {
|
||||
backend Backend
|
||||
cluster ClusterBackend
|
||||
routes []router.Route
|
||||
}
|
||||
|
||||
// NewRouter initializes a new volume router
|
||||
func NewRouter(b Backend, cb ClusterBackend) router.Router {
|
||||
func NewRouter(b Backend) router.Router {
|
||||
r := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: cb,
|
||||
}
|
||||
r.initRoutes()
|
||||
return r
|
||||
@@ -31,9 +29,7 @@ func (r *volumeRouter) initRoutes() {
|
||||
router.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
|
||||
// POST
|
||||
router.NewPostRoute("/volumes/create", r.postVolumesCreate),
|
||||
router.NewPostRoute("/volumes/prune", r.postVolumesPrune),
|
||||
// PUT
|
||||
router.NewPutRoute("/volumes/{name:.*}", r.putVolumesUpdate),
|
||||
router.NewPostRoute("/volumes/prune", r.postVolumesPrune, router.WithCancel),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
|
||||
}
|
||||
|
||||
@@ -2,24 +2,16 @@ package volume // import "github.com/docker/docker/api/server/router/volume"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
volumetypes "github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/volume/service/opts"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// clusterVolumesVersion defines the API version that swarm cluster volume
|
||||
// functionality was introduced. avoids the use of magic numbers.
|
||||
clusterVolumesVersion = "1.42"
|
||||
)
|
||||
|
||||
func (v *volumeRouter) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -29,62 +21,25 @@ func (v *volumeRouter) getVolumesList(ctx context.Context, w http.ResponseWriter
|
||||
|
||||
filters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error reading volume filters")
|
||||
return errdefs.InvalidParameter(errors.Wrap(err, "error reading volume filters"))
|
||||
}
|
||||
volumes, warnings, err := v.backend.List(ctx, filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, clusterVolumesVersion) && v.cluster.IsManager() {
|
||||
clusterVolumes, swarmErr := v.cluster.GetVolumes(volume.ListOptions{Filters: filters})
|
||||
if swarmErr != nil {
|
||||
// if there is a swarm error, we may not want to error out right
|
||||
// away. the local list probably worked. instead, let's do what we
|
||||
// do if there's a bad driver while trying to list: add the error
|
||||
// to the warnings. don't do this if swarm is not initialized.
|
||||
warnings = append(warnings, swarmErr.Error())
|
||||
}
|
||||
// add the cluster volumes to the return
|
||||
volumes = append(volumes, clusterVolumes...)
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, &volume.ListResponse{Volumes: volumes, Warnings: warnings})
|
||||
return httputils.WriteJSON(w, http.StatusOK, &volumetypes.VolumeListOKBody{Volumes: volumes, Warnings: warnings})
|
||||
}
|
||||
|
||||
func (v *volumeRouter) getVolumeByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
// re: volume name duplication
|
||||
//
|
||||
// we prefer to get volumes locally before attempting to get them from the
|
||||
// cluster. Local volumes can only be looked up by name, but cluster
|
||||
// volumes can also be looked up by ID.
|
||||
vol, err := v.backend.Get(ctx, vars["name"], opts.WithGetResolveStatus)
|
||||
|
||||
// if the volume is not found in the regular volume backend, and the client
|
||||
// is using an API version greater than 1.42 (when cluster volumes were
|
||||
// introduced), then check if Swarm has the volume.
|
||||
if errdefs.IsNotFound(err) && versions.GreaterThanOrEqualTo(version, clusterVolumesVersion) && v.cluster.IsManager() {
|
||||
swarmVol, err := v.cluster.GetVolume(vars["name"])
|
||||
// if swarm returns an error and that error indicates that swarm is not
|
||||
// initialized, return original NotFound error. Otherwise, we'd return
|
||||
// a weird swarm unavailable error on non-swarm engines.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vol = &swarmVol
|
||||
} else if err != nil {
|
||||
// otherwise, if this isn't NotFound, or this isn't a high enough version,
|
||||
// just return the error by itself.
|
||||
volume, err := v.backend.Get(ctx, vars["name"], opts.WithGetResolveStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, vol)
|
||||
return httputils.WriteJSON(w, http.StatusOK, volume)
|
||||
}
|
||||
|
||||
func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -92,65 +47,23 @@ func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWri
|
||||
return err
|
||||
}
|
||||
|
||||
var req volume.CreateOptions
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
vol *volume.Volume
|
||||
err error
|
||||
version = httputils.VersionFromContext(ctx)
|
||||
)
|
||||
|
||||
// if the ClusterVolumeSpec is filled in, then this is a cluster volume
|
||||
// and is created through the swarm cluster volume backend.
|
||||
//
|
||||
// re: volume name duplication
|
||||
//
|
||||
// As it happens, there is no good way to prevent duplication of a volume
|
||||
// name between local and cluster volumes. This is because Swarm volumes
|
||||
// can be created from any manager node, bypassing most of the protections
|
||||
// we could put into the engine side.
|
||||
//
|
||||
// Instead, we will allow creating a volume with a duplicate name, which
|
||||
// should not break anything.
|
||||
if req.ClusterVolumeSpec != nil && versions.GreaterThanOrEqualTo(version, clusterVolumesVersion) {
|
||||
logrus.Debug("using cluster volume")
|
||||
vol, err = v.cluster.CreateVolume(req)
|
||||
} else {
|
||||
logrus.Debug("using regular volume")
|
||||
vol, err = v.backend.Create(ctx, req.Name, req.Driver, opts.WithCreateOptions(req.DriverOpts), opts.WithCreateLabels(req.Labels))
|
||||
var req volumetypes.VolumeCreateBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
if err == io.EOF {
|
||||
return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
volume, err := v.backend.Create(ctx, req.Name, req.Driver, opts.WithCreateOptions(req.DriverOpts), opts.WithCreateLabels(req.Labels))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return httputils.WriteJSON(w, http.StatusCreated, vol)
|
||||
}
|
||||
|
||||
func (v *volumeRouter) putVolumesUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if !v.cluster.IsManager() {
|
||||
return errdefs.Unavailable(errors.New("volume update only valid for cluster volumes, but swarm is unavailable"))
|
||||
}
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
version, err := strconv.ParseUint(rawVersion, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid swarm object version '%s': %v", rawVersion, err)
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
var req volume.UpdateOptions
|
||||
if err := httputils.ReadJSON(r, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return v.cluster.UpdateVolume(vars["name"], version, req)
|
||||
return httputils.WriteJSON(w, http.StatusCreated, volume)
|
||||
}
|
||||
|
||||
func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -158,28 +71,7 @@ func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter,
|
||||
return err
|
||||
}
|
||||
force := httputils.BoolValue(r, "force")
|
||||
|
||||
// First we try deleting local volume. The volume may not be found as a
|
||||
// local volume, but could be a cluster volume, so we ignore "not found"
|
||||
// errors at this stage. Note that no "not found" error is produced if
|
||||
// "force" is enabled.
|
||||
err := v.backend.Remove(ctx, vars["name"], opts.WithPurgeOnError(force))
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no volume was found, the volume may be a cluster volume. If force
|
||||
// is enabled, the volume backend won't return an error for non-existing
|
||||
// volumes, so we don't know if removal succeeded (or not volume existed).
|
||||
// In that case we always try to delete cluster volumes as well.
|
||||
if errdefs.IsNotFound(err) || force {
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, clusterVolumesVersion) && v.cluster.IsManager() {
|
||||
err = v.cluster.RemoveVolume(vars["name"], force)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err := v.backend.Remove(ctx, vars["name"], opts.WithPurgeOnError(force)); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@@ -196,12 +88,6 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit
|
||||
return err
|
||||
}
|
||||
|
||||
// API version 1.42 changes behavior where prune should only prune anonymous volumes.
|
||||
// To keep older API behavior working, we need to add this filter option to consider all (local) volumes for pruning, not just anonymous ones.
|
||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
|
||||
pruneReport, err := v.backend.Prune(ctx, pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,760 +0,0 @@
|
||||
package volume
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/volume/service/opts"
|
||||
)
|
||||
|
||||
func callGetVolume(v *volumeRouter, name string) (*httptest.ResponseRecorder, error) {
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
vars := map[string]string{"name": name}
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/volumes/%s", name), nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.getVolumeByName(ctx, resp, req, vars)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func callListVolumes(v *volumeRouter) (*httptest.ResponseRecorder, error) {
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
vars := map[string]string{}
|
||||
req := httptest.NewRequest("GET", "/volumes", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.getVolumesList(ctx, resp, req, vars)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func TestGetVolumeByNameNotFoundNoSwarm(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{},
|
||||
cluster: &fakeClusterBackend{},
|
||||
}
|
||||
|
||||
_, err := callGetVolume(v, "notReal")
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestGetVolumeByNameNotFoundNotManager(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{},
|
||||
cluster: &fakeClusterBackend{swarm: true},
|
||||
}
|
||||
|
||||
_, err := callGetVolume(v, "notReal")
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestGetVolumeByNameNotFound(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{},
|
||||
cluster: &fakeClusterBackend{swarm: true, manager: true},
|
||||
}
|
||||
|
||||
_, err := callGetVolume(v, "notReal")
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestGetVolumeByNameFoundRegular(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
|
||||
"volume1": {
|
||||
Name: "volume1",
|
||||
},
|
||||
},
|
||||
},
|
||||
cluster: &fakeClusterBackend{swarm: true, manager: true},
|
||||
}
|
||||
|
||||
_, err := callGetVolume(v, "volume1")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestGetVolumeByNameFoundSwarm(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{},
|
||||
cluster: &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{
|
||||
"volume1": {
|
||||
Name: "volume1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := callGetVolume(v, "volume1")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
func TestListVolumes(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"v1": {Name: "v1"},
|
||||
"v2": {Name: "v2"},
|
||||
},
|
||||
},
|
||||
cluster: &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{
|
||||
"v3": {Name: "v3"},
|
||||
"v4": {Name: "v4"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := callListVolumes(v)
|
||||
assert.NilError(t, err)
|
||||
d := json.NewDecoder(resp.Result().Body)
|
||||
respVols := volume.ListResponse{}
|
||||
assert.NilError(t, d.Decode(&respVols))
|
||||
|
||||
assert.Assert(t, respVols.Volumes != nil)
|
||||
assert.Equal(t, len(respVols.Volumes), 4, "volumes %v", respVols.Volumes)
|
||||
}
|
||||
|
||||
func TestListVolumesNoSwarm(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"v1": {Name: "v1"},
|
||||
"v2": {Name: "v2"},
|
||||
},
|
||||
},
|
||||
cluster: &fakeClusterBackend{},
|
||||
}
|
||||
|
||||
_, err := callListVolumes(v)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestListVolumesNoManager(t *testing.T) {
|
||||
v := &volumeRouter{
|
||||
backend: &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"v1": {Name: "v1"},
|
||||
"v2": {Name: "v2"},
|
||||
},
|
||||
},
|
||||
cluster: &fakeClusterBackend{swarm: true},
|
||||
}
|
||||
|
||||
resp, err := callListVolumes(v)
|
||||
assert.NilError(t, err)
|
||||
|
||||
d := json.NewDecoder(resp.Result().Body)
|
||||
respVols := volume.ListResponse{}
|
||||
assert.NilError(t, d.Decode(&respVols))
|
||||
|
||||
assert.Equal(t, len(respVols.Volumes), 2)
|
||||
assert.Equal(t, len(respVols.Warnings), 0)
|
||||
}
|
||||
|
||||
func TestCreateRegularVolume(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
}
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeCreate := volume.CreateOptions{
|
||||
Name: "vol1",
|
||||
Driver: "foodriver",
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
e := json.NewEncoder(&buf)
|
||||
e.Encode(volumeCreate)
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
err := v.postVolumesCreate(ctx, resp, req, nil)
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
||||
respVolume := volume.Volume{}
|
||||
|
||||
assert.NilError(t, json.NewDecoder(resp.Result().Body).Decode(&respVolume))
|
||||
|
||||
assert.Equal(t, respVolume.Name, "vol1")
|
||||
assert.Equal(t, respVolume.Driver, "foodriver")
|
||||
|
||||
assert.Equal(t, 1, len(b.volumes))
|
||||
assert.Equal(t, 0, len(c.volumes))
|
||||
}
|
||||
|
||||
func TestCreateSwarmVolumeNoSwarm(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeCreate := volume.CreateOptions{
|
||||
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
||||
Name: "volCluster",
|
||||
Driver: "someCSI",
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeCreate)
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
err := v.postVolumesCreate(ctx, resp, req, nil)
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsUnavailable(err))
|
||||
}
|
||||
|
||||
func TestCreateSwarmVolumeNotManager(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{swarm: true}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeCreate := volume.CreateOptions{
|
||||
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
||||
Name: "volCluster",
|
||||
Driver: "someCSI",
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeCreate)
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
err := v.postVolumesCreate(ctx, resp, req, nil)
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsUnavailable(err))
|
||||
}
|
||||
|
||||
func TestCreateVolumeCluster(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeCreate := volume.CreateOptions{
|
||||
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
||||
Name: "volCluster",
|
||||
Driver: "someCSI",
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeCreate)
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
err := v.postVolumesCreate(ctx, resp, req, nil)
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
||||
respVolume := volume.Volume{}
|
||||
|
||||
assert.NilError(t, json.NewDecoder(resp.Result().Body).Decode(&respVolume))
|
||||
|
||||
assert.Equal(t, respVolume.Name, "volCluster")
|
||||
assert.Equal(t, respVolume.Driver, "someCSI")
|
||||
|
||||
assert.Equal(t, 0, len(b.volumes))
|
||||
assert.Equal(t, 1, len(c.volumes))
|
||||
}
|
||||
|
||||
func TestUpdateVolume(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{
|
||||
"vol1": {
|
||||
Name: "vo1",
|
||||
ClusterVolume: &volume.ClusterVolume{
|
||||
ID: "vol1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeUpdate := volume.UpdateOptions{
|
||||
Spec: &volume.ClusterVolumeSpec{},
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeUpdate)
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, c.volumes["vol1"].ClusterVolume.Meta.Version.Index, uint64(1))
|
||||
}
|
||||
|
||||
func TestUpdateVolumeNoSwarm(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeUpdate := volume.UpdateOptions{
|
||||
Spec: &volume.ClusterVolumeSpec{},
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeUpdate)
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsUnavailable(err))
|
||||
}
|
||||
|
||||
func TestUpdateVolumeNotFound(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{},
|
||||
}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
volumeUpdate := volume.UpdateOptions{
|
||||
Spec: &volume.ClusterVolumeSpec{},
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(volumeUpdate)
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestVolumeRemove(t *testing.T) {
|
||||
b := &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"vol1": {
|
||||
Name: "vol1",
|
||||
},
|
||||
},
|
||||
}
|
||||
c := &fakeClusterBackend{swarm: true, manager: true}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(b.volumes), 0)
|
||||
}
|
||||
|
||||
func TestVolumeRemoveSwarm(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{
|
||||
"vol1": {
|
||||
Name: "vol1",
|
||||
ClusterVolume: &volume.ClusterVolume{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(c.volumes), 0)
|
||||
}
|
||||
|
||||
func TestVolumeRemoveNotFoundNoSwarm(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{}
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err), err.Error())
|
||||
}
|
||||
|
||||
func TestVolumeRemoveNotFoundNoManager(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{swarm: true}
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
|
||||
b := &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"vol1": {
|
||||
Name: "vol1",
|
||||
},
|
||||
},
|
||||
}
|
||||
c := &fakeClusterBackend{}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(b.volumes), 0)
|
||||
}
|
||||
|
||||
func TestVolumeRemoveNoSwarmInUse(t *testing.T) {
|
||||
b := &fakeVolumeBackend{
|
||||
volumes: map[string]*volume.Volume{
|
||||
"inuse": {
|
||||
Name: "inuse",
|
||||
},
|
||||
},
|
||||
}
|
||||
c := &fakeClusterBackend{}
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/inuse", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "inuse"})
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsConflict(err))
|
||||
}
|
||||
|
||||
func TestVolumeRemoveSwarmForce(t *testing.T) {
|
||||
b := &fakeVolumeBackend{}
|
||||
c := &fakeClusterBackend{
|
||||
swarm: true,
|
||||
manager: true,
|
||||
volumes: map[string]*volume.Volume{
|
||||
"vol1": {
|
||||
Name: "vol1",
|
||||
ClusterVolume: &volume.ClusterVolume{},
|
||||
Options: map[string]string{"mustforce": "yes"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
v := &volumeRouter{
|
||||
backend: b,
|
||||
cluster: c,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
|
||||
assert.Assert(t, err != nil)
|
||||
assert.Assert(t, errdefs.IsConflict(err))
|
||||
|
||||
ctx = context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
||||
req = httptest.NewRequest("DELETE", "/volumes/vol1?force=1", nil)
|
||||
resp = httptest.NewRecorder()
|
||||
|
||||
err = v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(b.volumes), 0)
|
||||
assert.Equal(t, len(c.volumes), 0)
|
||||
}
|
||||
|
||||
type fakeVolumeBackend struct {
|
||||
volumes map[string]*volume.Volume
|
||||
}
|
||||
|
||||
func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]*volume.Volume, []string, error) {
|
||||
volumes := []*volume.Volume{}
|
||||
for _, v := range b.volumes {
|
||||
volumes = append(volumes, v)
|
||||
}
|
||||
return volumes, nil, nil
|
||||
}
|
||||
|
||||
func (b *fakeVolumeBackend) Get(_ context.Context, name string, _ ...opts.GetOption) (*volume.Volume, error) {
|
||||
if v, ok := b.volumes[name]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, errdefs.NotFound(fmt.Errorf("volume %s not found", name))
|
||||
}
|
||||
|
||||
func (b *fakeVolumeBackend) Create(_ context.Context, name, driverName string, _ ...opts.CreateOption) (*volume.Volume, error) {
|
||||
if _, ok := b.volumes[name]; ok {
|
||||
// TODO(dperny): return appropriate error type
|
||||
return nil, fmt.Errorf("already exists")
|
||||
}
|
||||
|
||||
v := &volume.Volume{
|
||||
Name: name,
|
||||
Driver: driverName,
|
||||
}
|
||||
if b.volumes == nil {
|
||||
b.volumes = map[string]*volume.Volume{
|
||||
name: v,
|
||||
}
|
||||
} else {
|
||||
b.volumes[name] = v
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *fakeVolumeBackend) Remove(_ context.Context, name string, o ...opts.RemoveOption) error {
|
||||
removeOpts := &opts.RemoveConfig{}
|
||||
for _, opt := range o {
|
||||
opt(removeOpts)
|
||||
}
|
||||
|
||||
if v, ok := b.volumes[name]; !ok {
|
||||
if !removeOpts.PurgeOnError {
|
||||
return errdefs.NotFound(fmt.Errorf("volume %s not found", name))
|
||||
}
|
||||
} else if v.Name == "inuse" {
|
||||
return errdefs.Conflict(fmt.Errorf("volume in use"))
|
||||
}
|
||||
|
||||
delete(b.volumes, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *fakeVolumeBackend) Prune(_ context.Context, _ filters.Args) (*types.VolumesPruneReport, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type fakeClusterBackend struct {
|
||||
swarm bool
|
||||
manager bool
|
||||
idCount int
|
||||
volumes map[string]*volume.Volume
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) checkSwarm() error {
|
||||
if !c.swarm {
|
||||
return errdefs.Unavailable(fmt.Errorf("this node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again"))
|
||||
} else if !c.manager {
|
||||
return errdefs.Unavailable(fmt.Errorf("this node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) IsManager() bool {
|
||||
return c.swarm && c.manager
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) GetVolume(nameOrID string) (volume.Volume, error) {
|
||||
if err := c.checkSwarm(); err != nil {
|
||||
return volume.Volume{}, err
|
||||
}
|
||||
|
||||
if v, ok := c.volumes[nameOrID]; ok {
|
||||
return *v, nil
|
||||
}
|
||||
return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) GetVolumes(options volume.ListOptions) ([]*volume.Volume, error) {
|
||||
if err := c.checkSwarm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
volumes := []*volume.Volume{}
|
||||
|
||||
for _, v := range c.volumes {
|
||||
volumes = append(volumes, v)
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) CreateVolume(volumeCreate volume.CreateOptions) (*volume.Volume, error) {
|
||||
if err := c.checkSwarm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := c.volumes[volumeCreate.Name]; ok {
|
||||
// TODO(dperny): return appropriate already exists error
|
||||
return nil, fmt.Errorf("already exists")
|
||||
}
|
||||
|
||||
v := &volume.Volume{
|
||||
Name: volumeCreate.Name,
|
||||
Driver: volumeCreate.Driver,
|
||||
Labels: volumeCreate.Labels,
|
||||
Options: volumeCreate.DriverOpts,
|
||||
Scope: "global",
|
||||
}
|
||||
|
||||
v.ClusterVolume = &volume.ClusterVolume{
|
||||
ID: fmt.Sprintf("cluster_%d", c.idCount),
|
||||
Spec: *volumeCreate.ClusterVolumeSpec,
|
||||
}
|
||||
|
||||
c.idCount = c.idCount + 1
|
||||
if c.volumes == nil {
|
||||
c.volumes = map[string]*volume.Volume{
|
||||
v.Name: v,
|
||||
}
|
||||
} else {
|
||||
c.volumes[v.Name] = v
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) RemoveVolume(nameOrID string, force bool) error {
|
||||
if err := c.checkSwarm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, ok := c.volumes[nameOrID]
|
||||
if !ok {
|
||||
return errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
|
||||
}
|
||||
|
||||
if _, mustforce := v.Options["mustforce"]; mustforce && !force {
|
||||
return errdefs.Conflict(fmt.Errorf("volume %s must be force removed", nameOrID))
|
||||
}
|
||||
|
||||
delete(c.volumes, nameOrID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClusterBackend) UpdateVolume(nameOrID string, version uint64, _ volume.UpdateOptions) error {
|
||||
if err := c.checkSwarm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, ok := c.volumes[nameOrID]; ok {
|
||||
if v.ClusterVolume.Meta.Version.Index != version {
|
||||
return fmt.Errorf("wrong version")
|
||||
}
|
||||
v.ClusterVolume.Meta.Version.Index = v.ClusterVolume.Meta.Version.Index + 1
|
||||
// for testing, we don't actually need to change anything about the
|
||||
// volume object. let's just increment the version so we can see the
|
||||
// call happened.
|
||||
} else {
|
||||
return errdefs.NotFound(fmt.Errorf("volume %q not found", nameOrID))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
30
api/server/router_swapper.go
Normal file
30
api/server/router_swapper.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package server // import "github.com/docker/docker/api/server"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// routerSwapper is an http.Handler that allows you to swap
|
||||
// mux routers.
|
||||
type routerSwapper struct {
|
||||
mu sync.Mutex
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
// Swap changes the old router with the new one.
|
||||
func (rs *routerSwapper) Swap(newRouter *mux.Router) {
|
||||
rs.mu.Lock()
|
||||
rs.router = newRouter
|
||||
rs.mu.Unlock()
|
||||
}
|
||||
|
||||
// ServeHTTP makes the routerSwapper to implement the http.Handler interface.
|
||||
func (rs *routerSwapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
rs.mu.Lock()
|
||||
router := rs.router
|
||||
rs.mu.Unlock()
|
||||
router.ServeHTTP(w, r)
|
||||
}
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/server/httpstatus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/server/middleware"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
@@ -24,20 +22,20 @@ const versionMatcher = "/v{version:[0-9.]+}"
|
||||
|
||||
// Config provides the configuration for the API server
|
||||
type Config struct {
|
||||
Logging bool
|
||||
CorsHeaders string
|
||||
Version string
|
||||
SocketGroup string
|
||||
TLSConfig *tls.Config
|
||||
// Hosts is a list of addresses for the API to listen on.
|
||||
Hosts []string
|
||||
}
|
||||
|
||||
// Server contains instance details for the server
|
||||
type Server struct {
|
||||
cfg *Config
|
||||
servers []*HTTPServer
|
||||
routers []router.Router
|
||||
middlewares []middleware.Middleware
|
||||
cfg *Config
|
||||
servers []*HTTPServer
|
||||
routers []router.Router
|
||||
routerSwapper *routerSwapper
|
||||
middlewares []middleware.Middleware
|
||||
}
|
||||
|
||||
// New returns a new instance of the server based on the specified configuration.
|
||||
@@ -59,8 +57,7 @@ func (s *Server) Accept(addr string, listeners ...net.Listener) {
|
||||
for _, listener := range listeners {
|
||||
httpServer := &HTTPServer{
|
||||
srv: &http.Server{
|
||||
Addr: addr,
|
||||
ReadHeaderTimeout: 5 * time.Minute, // "G112: Potential Slowloris Attack (gosec)"; not a real concern for our use, so setting a long timeout.
|
||||
Addr: addr,
|
||||
},
|
||||
l: listener,
|
||||
}
|
||||
@@ -82,7 +79,7 @@ func (s *Server) Close() {
|
||||
func (s *Server) serveAPI() error {
|
||||
var chErrors = make(chan error, len(s.servers))
|
||||
for _, srv := range s.servers {
|
||||
srv.srv.Handler = s.createMux()
|
||||
srv.srv.Handler = s.routerSwapper
|
||||
go func(srv *HTTPServer) {
|
||||
var err error
|
||||
logrus.Infof("API listen on %s", srv.l.Addr())
|
||||
@@ -132,8 +129,8 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
|
||||
// use intermediate variable to prevent "should not use basic type
|
||||
// string as key in context.WithValue" golint errors
|
||||
ctx := context.WithValue(r.Context(), dockerversion.UAStringKey{}, r.Header.Get("User-Agent"))
|
||||
r = r.WithContext(ctx)
|
||||
var ki interface{} = dockerversion.UAStringKey
|
||||
ctx := context.WithValue(context.Background(), ki, r.Header.Get("User-Agent"))
|
||||
handlerFunc := s.handlerWithGlobalMiddlewares(handler)
|
||||
|
||||
vars := mux.Vars(r)
|
||||
@@ -142,11 +139,11 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
}
|
||||
|
||||
if err := handlerFunc(ctx, w, r, vars); err != nil {
|
||||
statusCode := httpstatus.FromError(err)
|
||||
statusCode := httputils.GetHTTPErrorStatusCode(err)
|
||||
if statusCode >= 500 {
|
||||
logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
}
|
||||
makeErrorHandler(err)(w, r)
|
||||
httputils.MakeErrorHandler(err)(w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,6 +152,11 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
// This method also enables the Go profiler.
|
||||
func (s *Server) InitRouter(routers ...router.Router) {
|
||||
s.routers = append(s.routers, routers...)
|
||||
|
||||
m := s.createMux()
|
||||
s.routerSwapper = &routerSwapper{
|
||||
router: m,
|
||||
}
|
||||
}
|
||||
|
||||
type pageNotFoundError struct{}
|
||||
@@ -187,10 +189,9 @@ func (s *Server) createMux() *mux.Router {
|
||||
m.Path("/debug" + r.Path()).Handler(f)
|
||||
}
|
||||
|
||||
notFoundHandler := makeErrorHandler(pageNotFoundError{})
|
||||
notFoundHandler := httputils.MakeErrorHandler(pageNotFoundError{})
|
||||
m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
|
||||
m.NotFoundHandler = notFoundHandler
|
||||
m.MethodNotAllowedHandler = notFoundHandler
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestMiddlewares(t *testing.T) {
|
||||
|
||||
srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinVersion))
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
4035
api/swagger.yaml
4035
api/swagger.yaml
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
package {{ .Package }} // import "github.com/docker/docker/api/types/{{ .Package }}"
|
||||
package {{ .Package }}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -10,15 +10,18 @@ import (
|
||||
|
||||
// ContainerAttachConfig holds the streams to use when connecting to a container to view logs.
|
||||
type ContainerAttachConfig struct {
|
||||
GetStreams func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error)
|
||||
GetStreams func() (io.ReadCloser, io.Writer, io.Writer, error)
|
||||
UseStdin bool
|
||||
UseStdout bool
|
||||
UseStderr bool
|
||||
Logs bool
|
||||
Stream bool
|
||||
DetachKeys string
|
||||
// Used to signify that streams must be multiplexed by producer as endpoint can't manage multiple streams.
|
||||
// This is typically set by HTTP endpoint, while websocket can transport raw streams
|
||||
|
||||
// Used to signify that streams are multiplexed and therefore need a StdWriter to encode stdout/stderr messages accordingly.
|
||||
// TODO @cpuguy83: This shouldn't be needed. It was only added so that http and websocket endpoints can use the same function, and the websocket function was not using a stdwriter prior to this change...
|
||||
// HOWEVER, the websocket endpoint is using a single stream and SHOULD be encoded with stdout/stderr as is done for HTTP since it is still just a single stream.
|
||||
// Since such a change is an API change unrelated to the current changeset we'll keep it as is here and change separately.
|
||||
MuxStreams bool
|
||||
}
|
||||
|
||||
@@ -27,7 +30,7 @@ type ContainerAttachConfig struct {
|
||||
// expectation is for the logger endpoints to assemble the chunks using this
|
||||
// metadata.
|
||||
type PartialLogMetaData struct {
|
||||
Last bool // true if this message is last of a partial
|
||||
Last bool //true if this message is last of a partial
|
||||
ID string // identifies group of messages comprising a single record
|
||||
Ordinal int // ordering of message in partial group
|
||||
}
|
||||
@@ -35,6 +38,8 @@ type PartialLogMetaData struct {
|
||||
// LogMessage is datastructure that represents piece of output produced by some
|
||||
// container. The Line member is a slice of an array whose contents can be
|
||||
// changed after a log driver's Log() method returns.
|
||||
// changes to this struct need to be reflect in the reset method in
|
||||
// daemon/logger/logger.go
|
||||
type LogMessage struct {
|
||||
Line []byte
|
||||
Source string
|
||||
@@ -68,7 +73,6 @@ type LogSelector struct {
|
||||
// behavior of a backend.ContainerStats() call.
|
||||
type ContainerStatsConfig struct {
|
||||
Stream bool
|
||||
OneShot bool
|
||||
OutStream io.Writer
|
||||
Version string
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ type ContainerCommitOptions struct {
|
||||
|
||||
// ContainerExecInspect holds information returned by exec inspect.
|
||||
type ContainerExecInspect struct {
|
||||
ExecID string `json:"ID"`
|
||||
ExecID string
|
||||
ContainerID string
|
||||
Running bool
|
||||
ExitCode int
|
||||
@@ -59,6 +59,7 @@ type ContainerExecInspect struct {
|
||||
|
||||
// ContainerListOptions holds parameters to list containers with.
|
||||
type ContainerListOptions struct {
|
||||
Quiet bool
|
||||
Size bool
|
||||
All bool
|
||||
Latest bool
|
||||
@@ -112,16 +113,10 @@ type NetworkListOptions struct {
|
||||
Filters filters.Args
|
||||
}
|
||||
|
||||
// NewHijackedResponse intializes a HijackedResponse type
|
||||
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
|
||||
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
|
||||
}
|
||||
|
||||
// HijackedResponse holds connection information for a hijacked request.
|
||||
type HijackedResponse struct {
|
||||
mediaType string
|
||||
Conn net.Conn
|
||||
Reader *bufio.Reader
|
||||
Conn net.Conn
|
||||
Reader *bufio.Reader
|
||||
}
|
||||
|
||||
// Close closes the hijacked connection and reader.
|
||||
@@ -129,15 +124,6 @@ func (h *HijackedResponse) Close() {
|
||||
h.Conn.Close()
|
||||
}
|
||||
|
||||
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
|
||||
// returns false if HTTP Content-Type is not relevant, and container must be inspected
|
||||
func (h *HijackedResponse) MediaType() (string, bool) {
|
||||
if h.mediaType == "" {
|
||||
return "", false
|
||||
}
|
||||
return h.mediaType, true
|
||||
}
|
||||
|
||||
// CloseWriter is an interface that implements structs
|
||||
// that close input streams to prevent from writing.
|
||||
type CloseWriter interface {
|
||||
@@ -201,15 +187,6 @@ type ImageBuildOptions struct {
|
||||
// build request. The same identifier can be used to gracefully cancel the
|
||||
// build with the cancel request.
|
||||
BuildID string
|
||||
// Outputs defines configurations for exporting build results. Only supported
|
||||
// in BuildKit mode
|
||||
Outputs []ImageBuildOutput
|
||||
}
|
||||
|
||||
// ImageBuildOutput defines configuration for exporting a build result
|
||||
type ImageBuildOutput struct {
|
||||
Type string
|
||||
Attrs map[string]string
|
||||
}
|
||||
|
||||
// BuilderVersion sets the version of underlying builder to use
|
||||
@@ -219,7 +196,7 @@ const (
|
||||
// BuilderV1 is the first generation builder in docker daemon
|
||||
BuilderV1 BuilderVersion = "1"
|
||||
// BuilderBuildKit is builder based on moby/buildkit project
|
||||
BuilderBuildKit BuilderVersion = "2"
|
||||
BuilderBuildKit = "2"
|
||||
)
|
||||
|
||||
// ImageBuildResponse holds information
|
||||
@@ -250,20 +227,10 @@ type ImageImportOptions struct {
|
||||
Platform string // Platform is the target platform of the image
|
||||
}
|
||||
|
||||
// ImageListOptions holds parameters to list images with.
|
||||
// ImageListOptions holds parameters to filter the list of images with.
|
||||
type ImageListOptions struct {
|
||||
// All controls whether all images in the graph are filtered, or just
|
||||
// the heads.
|
||||
All bool
|
||||
|
||||
// Filters is a JSON-encoded set of filter arguments.
|
||||
All bool
|
||||
Filters filters.Args
|
||||
|
||||
// SharedSize indicates whether the shared size of images should be computed.
|
||||
SharedSize bool
|
||||
|
||||
// ContainerCount indicates whether container count should be computed.
|
||||
ContainerCount bool
|
||||
}
|
||||
|
||||
// ImageLoadResponse returns information to the client about a load process.
|
||||
@@ -289,7 +256,7 @@ type ImagePullOptions struct {
|
||||
// if the privilege request fails.
|
||||
type RequestPrivilegeFunc func() (string, error)
|
||||
|
||||
// ImagePushOptions holds information to push images.
|
||||
//ImagePushOptions holds information to push images.
|
||||
type ImagePushOptions ImagePullOptions
|
||||
|
||||
// ImageRemoveOptions holds parameters to remove images.
|
||||
@@ -387,10 +354,6 @@ type ServiceUpdateOptions struct {
|
||||
// ServiceListOptions holds parameters to list services with.
|
||||
type ServiceListOptions struct {
|
||||
Filters filters.Args
|
||||
|
||||
// Status indicates whether the server should include the service task
|
||||
// count of running and desired tasks.
|
||||
Status bool
|
||||
}
|
||||
|
||||
// ServiceInspectOptions holds parameters related to the "service inspect"
|
||||
|
||||
@@ -3,7 +3,6 @@ package types // import "github.com/docker/docker/api/types"
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// configs holds structs used for internal communication between the
|
||||
@@ -16,7 +15,6 @@ type ContainerCreateConfig struct {
|
||||
Config *container.Config
|
||||
HostConfig *container.HostConfig
|
||||
NetworkingConfig *network.NetworkingConfig
|
||||
Platform *specs.Platform
|
||||
AdjustCPUShares bool
|
||||
}
|
||||
|
||||
@@ -33,7 +31,6 @@ type ExecConfig struct {
|
||||
User string // User that will run the command
|
||||
Privileged bool // Is the container in privileged mode
|
||||
Tty bool // Attach standard streams to a tty.
|
||||
ConsoleSize *[2]uint `json:",omitempty"` // Initial console size [height, width]
|
||||
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||
AttachStderr bool // Attach the standard error
|
||||
AttachStdout bool // Attach the standard output
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
@@ -14,24 +13,6 @@ import (
|
||||
// Docker interprets it as 3 nanoseconds.
|
||||
const MinimumDuration = 1 * time.Millisecond
|
||||
|
||||
// StopOptions holds the options to stop or restart a container.
|
||||
type StopOptions struct {
|
||||
// Signal (optional) is the signal to send to the container to (gracefully)
|
||||
// stop it before forcibly terminating the container with SIGKILL after the
|
||||
// timeout expires. If not value is set, the default (SIGTERM) is used.
|
||||
Signal string `json:",omitempty"`
|
||||
|
||||
// Timeout (optional) is the timeout (in seconds) to wait for the container
|
||||
// to stop gracefully before forcibly terminating it with SIGKILL.
|
||||
//
|
||||
// - Use nil to use the default timeout (10 seconds).
|
||||
// - Use '-1' to wait indefinitely.
|
||||
// - Use '0' to not wait for the container to exit gracefully, and
|
||||
// immediately proceeds to forcibly terminating the container.
|
||||
// - Other positive values are used as timeout (in seconds).
|
||||
Timeout *int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||
type HealthConfig struct {
|
||||
// Test is the test to perform to check that the container is healthy.
|
||||
@@ -53,14 +34,6 @@ type HealthConfig struct {
|
||||
Retries int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ExecStartOptions holds the options to start container's exec.
|
||||
type ExecStartOptions struct {
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
ConsoleSize *[2]uint `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Config contains the configuration data about a container.
|
||||
// It should hold only portable information about the container.
|
||||
// Here, "portable" means "independent from the host we are running on".
|
||||
@@ -81,7 +54,7 @@ type Config struct {
|
||||
Env []string // List of environment variable to set in the container
|
||||
Cmd strslice.StrSlice // Command to run when starting the container
|
||||
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific).
|
||||
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
21
api/types/container/container_create.go
Normal file
21
api/types/container/container_create.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerCreateCreatedBody OK response to ContainerCreate operation
|
||||
// swagger:model ContainerCreateCreatedBody
|
||||
type ContainerCreateCreatedBody struct {
|
||||
|
||||
// The ID of the created container
|
||||
// Required: true
|
||||
ID string `json:"Id"`
|
||||
|
||||
// Warnings encountered when creating the container
|
||||
// Required: true
|
||||
Warnings []string `json:"Warnings"`
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -10,9 +11,7 @@ package container // import "github.com/docker/docker/api/types/container"
|
||||
// swagger:model ContainerTopOKBody
|
||||
type ContainerTopOKBody struct {
|
||||
|
||||
// Each process running in the container, where each is process
|
||||
// is an array of values corresponding to the titles.
|
||||
//
|
||||
// Each process running in the container, where each is process is an array of values corresponding to the titles
|
||||
// Required: true
|
||||
Processes [][]string `json:"Processes"`
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
29
api/types/container/container_wait.go
Normal file
29
api/types/container/container_wait.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerWaitOKBodyError container waiting error, if any
|
||||
// swagger:model ContainerWaitOKBodyError
|
||||
type ContainerWaitOKBodyError struct {
|
||||
|
||||
// Details of an error
|
||||
Message string `json:"Message,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerWaitOKBody OK response to ContainerWait operation
|
||||
// swagger:model ContainerWaitOKBody
|
||||
type ContainerWaitOKBody struct {
|
||||
|
||||
// error
|
||||
// Required: true
|
||||
Error *ContainerWaitOKBodyError `json:"Error"`
|
||||
|
||||
// Exit code of the container
|
||||
// Required: true
|
||||
StatusCode int64 `json:"StatusCode"`
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// CreateResponse ContainerCreateResponse
|
||||
//
|
||||
// OK response to ContainerCreate operation
|
||||
// swagger:model CreateResponse
|
||||
type CreateResponse struct {
|
||||
|
||||
// The ID of the created container
|
||||
// Required: true
|
||||
ID string `json:"Id"`
|
||||
|
||||
// Warnings encountered when creating the container
|
||||
// Required: true
|
||||
Warnings []string `json:"Warnings"`
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ContainerCreateCreatedBody OK response to ContainerCreate operation
|
||||
//
|
||||
// Deprecated: use CreateResponse
|
||||
type ContainerCreateCreatedBody = CreateResponse
|
||||
|
||||
// ContainerWaitOKBody OK response to ContainerWait operation
|
||||
//
|
||||
// Deprecated: use WaitResponse
|
||||
type ContainerWaitOKBody = WaitResponse
|
||||
|
||||
// ContainerWaitOKBodyError container waiting error, if any
|
||||
//
|
||||
// Deprecated: use WaitExitError
|
||||
type ContainerWaitOKBodyError = WaitExitError
|
||||
@@ -7,106 +7,67 @@ import (
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/go-connections/nat"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// CgroupnsMode represents the cgroup namespace mode of the container
|
||||
type CgroupnsMode string
|
||||
|
||||
// cgroup namespace modes for containers
|
||||
const (
|
||||
CgroupnsModeEmpty CgroupnsMode = ""
|
||||
CgroupnsModePrivate CgroupnsMode = "private"
|
||||
CgroupnsModeHost CgroupnsMode = "host"
|
||||
)
|
||||
|
||||
// IsPrivate indicates whether the container uses its own private cgroup namespace
|
||||
func (c CgroupnsMode) IsPrivate() bool {
|
||||
return c == CgroupnsModePrivate
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container shares the host's cgroup namespace
|
||||
func (c CgroupnsMode) IsHost() bool {
|
||||
return c == CgroupnsModeHost
|
||||
}
|
||||
|
||||
// IsEmpty indicates whether the container cgroup namespace mode is unset
|
||||
func (c CgroupnsMode) IsEmpty() bool {
|
||||
return c == CgroupnsModeEmpty
|
||||
}
|
||||
|
||||
// Valid indicates whether the cgroup namespace mode is valid
|
||||
func (c CgroupnsMode) Valid() bool {
|
||||
return c.IsEmpty() || c.IsPrivate() || c.IsHost()
|
||||
}
|
||||
|
||||
// Isolation represents the isolation technology of a container. The supported
|
||||
// values are platform specific
|
||||
type Isolation string
|
||||
|
||||
// Isolation modes for containers
|
||||
const (
|
||||
IsolationEmpty Isolation = "" // IsolationEmpty is unspecified (same behavior as default)
|
||||
IsolationDefault Isolation = "default" // IsolationDefault is the default isolation mode on current daemon
|
||||
IsolationProcess Isolation = "process" // IsolationProcess is process isolation mode
|
||||
IsolationHyperV Isolation = "hyperv" // IsolationHyperV is HyperV isolation mode
|
||||
)
|
||||
|
||||
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||
// is the native driver. On Windows, this is a Windows Server Container.
|
||||
func (i Isolation) IsDefault() bool {
|
||||
// TODO consider making isolation-mode strict (case-sensitive)
|
||||
v := Isolation(strings.ToLower(string(i)))
|
||||
return v == IsolationDefault || v == IsolationEmpty
|
||||
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||
}
|
||||
|
||||
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||
func (i Isolation) IsHyperV() bool {
|
||||
// TODO consider making isolation-mode strict (case-sensitive)
|
||||
return Isolation(strings.ToLower(string(i))) == IsolationHyperV
|
||||
return strings.ToLower(string(i)) == "hyperv"
|
||||
}
|
||||
|
||||
// IsProcess indicates the use of process isolation
|
||||
func (i Isolation) IsProcess() bool {
|
||||
// TODO consider making isolation-mode strict (case-sensitive)
|
||||
return Isolation(strings.ToLower(string(i))) == IsolationProcess
|
||||
return strings.ToLower(string(i)) == "process"
|
||||
}
|
||||
|
||||
const (
|
||||
// IsolationEmpty is unspecified (same behavior as default)
|
||||
IsolationEmpty = Isolation("")
|
||||
// IsolationDefault is the default isolation mode on current daemon
|
||||
IsolationDefault = Isolation("default")
|
||||
// IsolationProcess is process isolation mode
|
||||
IsolationProcess = Isolation("process")
|
||||
// IsolationHyperV is HyperV isolation mode
|
||||
IsolationHyperV = Isolation("hyperv")
|
||||
)
|
||||
|
||||
// IpcMode represents the container ipc stack.
|
||||
type IpcMode string
|
||||
|
||||
// IpcMode constants
|
||||
const (
|
||||
IPCModeNone IpcMode = "none"
|
||||
IPCModeHost IpcMode = "host"
|
||||
IPCModeContainer IpcMode = "container"
|
||||
IPCModePrivate IpcMode = "private"
|
||||
IPCModeShareable IpcMode = "shareable"
|
||||
)
|
||||
|
||||
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
|
||||
func (n IpcMode) IsPrivate() bool {
|
||||
return n == IPCModePrivate
|
||||
return n == "private"
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container shares the host's ipc namespace.
|
||||
func (n IpcMode) IsHost() bool {
|
||||
return n == IPCModeHost
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsShareable indicates whether the container's ipc namespace can be shared with another container.
|
||||
func (n IpcMode) IsShareable() bool {
|
||||
return n == IPCModeShareable
|
||||
return n == "shareable"
|
||||
}
|
||||
|
||||
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||
func (n IpcMode) IsContainer() bool {
|
||||
return strings.HasPrefix(string(n), string(IPCModeContainer)+":")
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
}
|
||||
|
||||
// IsNone indicates whether container IpcMode is set to "none".
|
||||
func (n IpcMode) IsNone() bool {
|
||||
return n == IPCModeNone
|
||||
return n == "none"
|
||||
}
|
||||
|
||||
// IsEmpty indicates whether container IpcMode is empty
|
||||
@@ -121,8 +82,9 @@ func (n IpcMode) Valid() bool {
|
||||
|
||||
// Container returns the name of the container ipc stack is going to be used.
|
||||
func (n IpcMode) Container() string {
|
||||
if n.IsContainer() {
|
||||
return strings.TrimPrefix(string(n), string(IPCModeContainer)+":")
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 && parts[0] == "container" {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -160,7 +122,7 @@ func (n NetworkMode) ConnectedContainer() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// UserDefined indicates user-created network
|
||||
//UserDefined indicates user-created network
|
||||
func (n NetworkMode) UserDefined() string {
|
||||
if n.IsUserDefined() {
|
||||
return string(n)
|
||||
@@ -282,16 +244,6 @@ func (n PidMode) Container() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// DeviceRequest represents a request for devices from a device driver.
|
||||
// Used by GPU device drivers.
|
||||
type DeviceRequest struct {
|
||||
Driver string // Name of device driver
|
||||
Count int // Number of devices to request (-1 = All)
|
||||
DeviceIDs []string // List of device IDs as recognizable by the device driver
|
||||
Capabilities [][]string // An OR list of AND lists of device capabilities (e.g. "gpu")
|
||||
Options map[string]string // Options to pass onto the device driver
|
||||
}
|
||||
|
||||
// DeviceMapping represents the device mapping between the host and the container.
|
||||
type DeviceMapping struct {
|
||||
PathOnHost string
|
||||
@@ -341,7 +293,7 @@ type LogMode string
|
||||
|
||||
// Available logging modes
|
||||
const (
|
||||
LogModeUnset LogMode = ""
|
||||
LogModeUnset = ""
|
||||
LogModeBlocking LogMode = "blocking"
|
||||
LogModeNonBlock LogMode = "non-blocking"
|
||||
)
|
||||
@@ -375,18 +327,14 @@ type Resources struct {
|
||||
CpusetMems string // CpusetMems 0-2, 0,1
|
||||
Devices []DeviceMapping // List of devices to map inside the container
|
||||
DeviceCgroupRules []string // List of rule to be added to the device cgroup
|
||||
DeviceRequests []DeviceRequest // List of device requests for device drivers
|
||||
|
||||
// KernelMemory specifies the kernel memory limit (in bytes) for the container.
|
||||
// Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes.
|
||||
KernelMemory int64 `json:",omitempty"`
|
||||
KernelMemoryTCP int64 `json:",omitempty"` // Hard limit for kernel TCP buffer memory (in bytes)
|
||||
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
|
||||
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||
DiskQuota int64 // Disk limit (in bytes)
|
||||
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||
PidsLimit int64 // Setting pids limit for a container
|
||||
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||
|
||||
// Applicable to Windows
|
||||
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||
@@ -417,12 +365,10 @@ type HostConfig struct {
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
|
||||
// Applicable to UNIX platforms
|
||||
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
|
||||
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||
@@ -446,7 +392,8 @@ type HostConfig struct {
|
||||
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||
|
||||
// Applicable to Windows
|
||||
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||
|
||||
// Contains container's resources (cgroups, ulimits)
|
||||
Resources
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// WaitExitError container waiting error, if any
|
||||
// swagger:model WaitExitError
|
||||
type WaitExitError struct {
|
||||
|
||||
// Details of an error
|
||||
Message string `json:"Message,omitempty"`
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// WaitResponse ContainerWaitResponse
|
||||
//
|
||||
// OK response to ContainerWait operation
|
||||
// swagger:model WaitResponse
|
||||
type WaitResponse struct {
|
||||
|
||||
// error
|
||||
Error *WaitExitError `json:"Error,omitempty"`
|
||||
|
||||
// Exit code of the container
|
||||
// Required: true
|
||||
StatusCode int64 `json:"StatusCode"`
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package types // import "github.com/docker/docker/api/types"
|
||||
|
||||
import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// Volume volume
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.Volume
|
||||
type Volume = volume.Volume
|
||||
|
||||
// VolumeUsageData Usage details about the volume. This information is used by the
|
||||
// `GET /system/df` endpoint, and omitted in other endpoints.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.UsageData
|
||||
type VolumeUsageData = volume.UsageData
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user