| ... | ... |
@@ -2,7 +2,7 @@ |
| 2 | 2 |
|
| 3 | 3 |
# ----------------------------------------------------------------------------------------- |
| 4 | 4 |
# This file describes the standard way to build Docker in a container on Windows |
| 5 |
-# Server 2016. |
|
| 5 |
+# Server 2016 or Windows 10. |
|
| 6 | 6 |
# |
| 7 | 7 |
# Maintainer: @jhowardmsft |
| 8 | 8 |
# ----------------------------------------------------------------------------------------- |
| ... | ... |
@@ -11,21 +11,25 @@ |
| 11 | 11 |
# Prerequisites: |
| 12 | 12 |
# -------------- |
| 13 | 13 |
# |
| 14 |
-# 1. Windows Server 2016 with all Windows updates applied. Pre-release versions |
|
| 15 |
-# of Windows are not supported (eg Windows Server 2016 TP5). The build number |
|
| 16 |
-# must be at least 14393. This can be confirmed, for example, by running the |
|
| 17 |
-# following from an elevated PowerShell prompt - this sample output is from a |
|
| 18 |
-# fully up to date machine as at late October 2016: |
|
| 14 |
+# 1. Windows Server 2016 or Windows 10 with all Windows updates applied. The major |
|
| 15 |
+# build number must be at least 14393. This can be confirmed, for example, by |
|
| 16 |
+# running the following from an elevated PowerShell prompt - this sample output |
|
| 17 |
+# is from a fully up to date machine as at mid-November 2016: |
|
| 19 | 18 |
# |
| 20 | 19 |
# >> PS C:\> $(gin).WindowsBuildLabEx |
| 21 |
-# >> 14393.321.amd64fre.rs1_release_inmarket.161004-2338 |
|
| 20 |
+# >> 14393.447.amd64fre.rs1_release_inmarket.161102-0100 |
|
| 22 | 21 |
# |
| 23 | 22 |
# 2. Git for Windows (or another git client) must be installed. https://git-scm.com/download/win. |
| 24 | 23 |
# |
| 25 | 24 |
# 3. The machine must be configured to run containers. For example, by following |
| 26 | 25 |
# the quick start guidance at https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start or |
| 27 | 26 |
# https://github.com/docker/labs/blob/master/windows/windows-containers/Setup.md |
| 28 |
- |
|
| 27 |
+# |
|
| 28 |
+# 4. If building in a Hyper-V VM: For Windows Server 2016 using Windows Server |
|
| 29 |
+# containers as the default option, it is recommended you have at least 1GB |
|
| 30 |
+# of memory assigned; For Windows 10 where Hyper-V Containers are employed, you |
|
| 31 |
+# should have at least 4GB of memory assigned. Note also, to run Hyper-V |
|
| 32 |
+# containers in a VM, it is necessary to configure the VM for nested virtualization. |
|
| 29 | 33 |
|
| 30 | 34 |
# ----------------------------------------------------------------------------------------- |
| 31 | 35 |
|
| ... | ... |
@@ -63,18 +67,16 @@ |
| 63 | 63 |
# >> docker build -t nativebuildimage -f Dockerfile.windows . |
| 64 | 64 |
# |
| 65 | 65 |
# |
| 66 |
-# 4. Build the docker executable binaries in a container: |
|
| 66 |
+# 4. Build the docker executable binaries: |
|
| 67 | 67 |
# |
| 68 |
-# >> docker run --name binaries nativebuildimage sh -c 'cd /c/go/src/github.com/docker/docker; hack/make.sh binary' |
|
| 68 |
+# >> docker run --name binaries nativebuildimage hack\make.ps1 -Binary |
|
| 69 | 69 |
# |
| 70 | 70 |
# |
| 71 |
-# 5. Copy the binaries out of the above container, replacing HostPath with an appropriate destination |
|
| 71 |
+# 5. Copy the binaries out of the container, replacing HostPath with an appropriate destination |
|
| 72 | 72 |
# folder on the host system where you want the binaries to be located. |
| 73 | 73 |
# |
| 74 |
-# >> $v=$(Get-Content ".\VERSION" -raw).ToString().Replace("`n","").Trim()
|
|
| 75 |
-# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\$v\binary-client\docker-$v.exe C:\HostPath\docker.exe |
|
| 76 |
-# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\$v\binary-daemon\dockerd.exe C:\HostPath\dockerd.exe |
|
| 77 |
-# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\$v\binary-daemon\docker-proxy-$v.exe C:\HostPath\docker-proxy.exe |
|
| 74 |
+# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe |
|
| 75 |
+# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe |
|
| 78 | 76 |
# |
| 79 | 77 |
# |
| 80 | 78 |
# 6. (Optional) Remove the interim container holding the built executable binaries: |
| ... | ... |
@@ -88,6 +90,39 @@ |
| 88 | 88 |
# image which has all the components required to build the binaries already installed. |
| 89 | 89 |
# |
| 90 | 90 |
# >> docker rmi nativebuildimage |
| 91 |
+# |
|
| 92 |
+ |
|
| 93 |
+# ----------------------------------------------------------------------------------------- |
|
| 94 |
+ |
|
| 95 |
+ |
|
| 96 |
+# The validation tests can either run in a container, or directly on the host. To run in a |
|
| 97 |
+# container, ensure you have created the nativebuildimage above. Then run the following |
|
| 98 |
+# from an (elevated) Windows PowerShell prompt: |
|
| 99 |
+# |
|
| 100 |
+# >> docker run --rm nativebuildimage hack\make.ps1 -DCO -PkgImports -GoFormat |
|
| 101 |
+ |
|
| 102 |
+# To run the validation tests on the host, from the root of the repository, run the |
|
| 103 |
+# following from a Windows PowerShell prompt (elevation is not required): (Note Go |
|
| 104 |
+# must be installed to run these tests) |
|
| 105 |
+# |
|
| 106 |
+# >> hack\make.ps1 -DCO -PkgImports -GoFormat |
|
| 107 |
+ |
|
| 108 |
+# ----------------------------------------------------------------------------------------- |
|
| 109 |
+ |
|
| 110 |
+ |
|
| 111 |
+# To run unit tests, ensure you have created the nativebuildimage above. Then run the |
|
| 112 |
+# following from an (elevated) Windows PowerShell prompt: |
|
| 113 |
+# |
|
| 114 |
+# >> docker run --rm nativebuildimage hack\make.ps1 -TestUnit |
|
| 115 |
+ |
|
| 116 |
+ |
|
| 117 |
+# ----------------------------------------------------------------------------------------- |
|
| 118 |
+ |
|
| 119 |
+ |
|
| 120 |
+# To run all tests and binary build, ensure you have created the nativebuildimage above. Then |
|
| 121 |
+# run the following from an (elevated) Windows PowerShell prompt: |
|
| 122 |
+# |
|
| 123 |
+# >> docker run nativebuildimage hack\make.ps1 -All |
|
| 91 | 124 |
|
| 92 | 125 |
|
| 93 | 126 |
# ----------------------------------------------------------------------------------------- |
| ... | ... |
@@ -96,28 +131,26 @@ |
| 96 | 96 |
# Important notes: |
| 97 | 97 |
# --------------- |
| 98 | 98 |
# |
| 99 |
-# The posix utilities from git aren't usable interactively as at October 2016. This |
|
| 100 |
-# is because they require a console window which isn't present in a container in Windows. |
|
| 101 |
-# See the example at the top of this file. Do NOT use -it in that docker run. It will not work. |
|
| 99 |
+# Don't attempt to use a bind-mount to pass a local directory as the bundles target |
|
| 100 |
+# directory. It does not work (golang attempts for follow a mapped folder incorrectly). |
|
| 101 |
+# Instead, use docker cp as per the example. |
|
| 102 | 102 |
# |
| 103 |
-# Don't attempt to use a volume for passing the source through to the container. The posix utilities will |
|
| 104 |
-# balk at reparse points. |
|
| 103 |
+# go.zip is not removed from the image as it is used by the Windows CI servers |
|
| 104 |
+# to ensure the host and image are running consistent versions of go. |
|
| 105 | 105 |
# |
| 106 |
-# The downloaded files are not cleared from the image. go.zip is used by the Windows |
|
| 107 |
-# CI servers to ensure the host and image are running consistent versions of go. |
|
| 106 |
+# Nanoserver support is a work in progress. Although the image will build if the |
|
| 107 |
+# FROM statement is updated, it will not work when running autogen through hack\make.ps1. |
|
| 108 |
+# It is suspected that the required GCC utilities (eg gcc, windres, windmc) silently |
|
| 109 |
+# quit due to the use of console hooks which are not available. |
|
| 108 | 110 |
# |
| 109 |
-# The GIT installer isn't very good at unattended install. We use techniques described |
|
| 110 |
-# at the links below to force it to set the path and other options accordingly. |
|
| 111 |
-# >> http://superuser.com/questions/944576/git-for-windows-silent-install-silent-arguments |
|
| 112 |
-# and follow through to installer at |
|
| 113 |
-# >> https://github.com/ferventcoder/chocolatey-packages/blob/master/automatic/git.install/tools/chocolateyInstall.ps1 |
|
| 111 |
+# The docker integration tests do not currently run in a container on Windows, predominantly |
|
| 112 |
+# due to Windows not supporting privileged mode, so anything using a volume would fail. |
|
| 113 |
+# They (along with the rest of the docker CI suite) can be run using |
|
| 114 |
+# https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. |
|
| 114 | 115 |
# |
| 115 |
-# As of October 2016, this does not work on Windows 10 client, just Windows Server 2016, |
|
| 116 |
-# and only with the default isolation mode (process). It does not work with isolation mode |
|
| 117 |
-# set to Hyper-V containers (hyperv). |
|
| 118 |
- |
|
| 119 | 116 |
# ----------------------------------------------------------------------------------------- |
| 120 | 117 |
|
| 118 |
+ |
|
| 121 | 119 |
# The number of build steps below are explicitly minimised to improve performance. |
| 122 | 120 |
FROM microsoft/windowsservercore |
| 123 | 121 |
|
| ... | ... |
@@ -128,55 +161,102 @@ SHELL ["powershell", "-command"] |
| 128 | 128 |
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux. |
| 129 | 129 |
# - FROM_DOCKERFILE is used for detection of building within a container. |
| 130 | 130 |
ENV GO_VERSION=1.7.3 ` |
| 131 |
- GIT_LOCATION=https://github.com/git-for-windows/git/releases/download/v2.10.1.windows.1/Git-2.10.1-64-bit.exe ` |
|
| 131 |
+ GIT_VERSION=2.10.2 ` |
|
| 132 | 132 |
GOPATH=C:\go ` |
| 133 | 133 |
FROM_DOCKERFILE=1 |
| 134 | 134 |
|
| 135 |
-WORKDIR C:\ |
|
| 136 |
- |
|
| 137 | 135 |
RUN ` |
| 138 |
- setx /M Path $($Env:PATH+';C:\gcc\bin;C:\go\bin'); ` |
|
| 139 |
- ` |
|
| 140 | 136 |
$ErrorActionPreference = 'Stop'; ` |
| 137 |
+ $ProgressPreference = 'SilentlyContinue'; ` |
|
| 138 |
+ ` |
|
| 139 |
+ Function Test-Nano() { `
|
|
| 140 |
+ $EditionId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'EditionID').EditionId; ` |
|
| 141 |
+ return (($EditionId -eq 'ServerStandardNano') -or ($EditionId -eq 'ServerDataCenterNano') -or ($EditionId -eq 'NanoServer')); ` |
|
| 142 |
+ }` |
|
| 143 |
+ ` |
|
| 141 | 144 |
Function Download-File([string] $source, [string] $target) { `
|
| 142 |
- $wc = New-Object net.webclient; $wc.Downloadfile($source, $target) ` |
|
| 145 |
+ if (Test-Nano) { `
|
|
| 146 |
+ $handler = New-Object System.Net.Http.HttpClientHandler; ` |
|
| 147 |
+ $client = New-Object System.Net.Http.HttpClient($handler); ` |
|
| 148 |
+ $client.Timeout = New-Object System.TimeSpan(0, 30, 0); ` |
|
| 149 |
+ $cancelTokenSource = [System.Threading.CancellationTokenSource]::new(); ` |
|
| 150 |
+ $responseMsg = $client.GetAsync([System.Uri]::new($source), $cancelTokenSource.Token); ` |
|
| 151 |
+ $responseMsg.Wait(); ` |
|
| 152 |
+ if (!$responseMsg.IsCanceled) { `
|
|
| 153 |
+ $response = $responseMsg.Result; ` |
|
| 154 |
+ if ($response.IsSuccessStatusCode) { `
|
|
| 155 |
+ $downloadedFileStream = [System.IO.FileStream]::new($target, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write); ` |
|
| 156 |
+ $copyStreamOp = $response.Content.CopyToAsync($downloadedFileStream); ` |
|
| 157 |
+ $copyStreamOp.Wait(); ` |
|
| 158 |
+ $downloadedFileStream.Close(); ` |
|
| 159 |
+ if ($copyStreamOp.Exception -ne $null) { throw $copyStreamOp.Exception } `
|
|
| 160 |
+ } ` |
|
| 161 |
+ } else { `
|
|
| 162 |
+ Throw ("Failed to download " + $source) `
|
|
| 163 |
+ }` |
|
| 164 |
+ } else { `
|
|
| 165 |
+ $webClient = New-Object System.Net.WebClient; ` |
|
| 166 |
+ $webClient.DownloadFile($source, $target); ` |
|
| 167 |
+ } ` |
|
| 143 | 168 |
} ` |
| 144 | 169 |
` |
| 170 |
+ setx /M PATH $('C:\git\bin;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin'); `
|
|
| 171 |
+ ` |
|
| 145 | 172 |
Write-Host INFO: Downloading git...; ` |
| 146 |
- Download-File $Env:GIT_LOCATION gitsetup.exe; ` |
|
| 173 |
+ $location='https://github.com/git-for-windows/git/releases/download/v'+$env:GIT_VERSION+'.windows.1/PortableGit-'+$env:GIT_VERSION+'-64-bit.7z.exe'; ` |
|
| 174 |
+ Download-File $location C:\gitsetup.7z.exe; ` |
|
| 147 | 175 |
` |
| 148 | 176 |
Write-Host INFO: Downloading go...; ` |
| 149 |
- Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') go.zip; `
|
|
| 177 |
+ Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') C:\go.zip; `
|
|
| 150 | 178 |
` |
| 151 | 179 |
Write-Host INFO: Downloading compiler 1 of 3...; ` |
| 152 |
- Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip gcc.zip; ` |
|
| 180 |
+ Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; ` |
|
| 153 | 181 |
` |
| 154 | 182 |
Write-Host INFO: Downloading compiler 2 of 3...; ` |
| 155 |
- Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip runtime.zip; ` |
|
| 183 |
+ Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip C:\runtime.zip; ` |
|
| 156 | 184 |
` |
| 157 | 185 |
Write-Host INFO: Downloading compiler 3 of 3...; ` |
| 158 |
- Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip binutils.zip; ` |
|
| 186 |
+ Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip C:\binutils.zip; ` |
|
| 159 | 187 |
` |
| 160 |
- Write-Host INFO: Installing git...; ` |
|
| 161 |
- $installPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'; ` |
|
| 162 |
- $installItem = 'Git_is1'; ` |
|
| 163 |
- New-Item -Path $installPath -Name $installItem -Force; ` |
|
| 164 |
- $installKey = $installPath+'\'+$installItem; ` |
|
| 165 |
- New-ItemProperty $installKey -Name 'Inno Setup CodeFile: Path Option' -Value 'CmdTools' -PropertyType 'String' -Force; ` |
|
| 166 |
- New-ItemProperty $installKey -Name 'Inno Setup CodeFile: Bash Terminal Option' -Value 'ConHost' -PropertyType 'String' -Force; ` |
|
| 167 |
- New-ItemProperty $installKey -Name 'Inno Setup CodeFile: CRLF Option' -Value 'CRLFCommitAsIs' -PropertyType 'String' -Force; ` |
|
| 168 |
- Start-Process gitsetup.exe -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /CLOSEAPPLICATIONS /DIR=C:\git\' -Wait; ` |
|
| 188 |
+ Write-Host INFO: Installing PS7Zip package...; ` |
|
| 189 |
+ Install-Package PS7Zip -Force | Out-Null; ` |
|
| 190 |
+ Write-Host INFO: Importing PS7Zip...; ` |
|
| 191 |
+ Import-Module PS7Zip -Force; ` |
|
| 192 |
+ New-Item C:\git -ItemType Directory | Out-Null ; ` |
|
| 193 |
+ cd C:\git; ` |
|
| 194 |
+ Write-Host INFO: Extracting git...; ` |
|
| 195 |
+ Expand-7Zip C:\gitsetup.7z.exe | Out-Null; ` |
|
| 196 |
+ cd C:\; ` |
|
| 169 | 197 |
` |
| 170 | 198 |
Write-Host INFO: Expanding go...; ` |
| 171 | 199 |
Expand-Archive C:\go.zip -DestinationPath C:\; ` |
| 172 | 200 |
` |
| 173 |
- Write-Host INFO: Expanding compiler...; ` |
|
| 201 |
+ Write-Host INFO: Expanding compiler 1 of 3...; ` |
|
| 174 | 202 |
Expand-Archive C:\gcc.zip -DestinationPath C:\gcc -Force; ` |
| 203 |
+ Write-Host INFO: Expanding compiler 2 of 3...; ` |
|
| 175 | 204 |
Expand-Archive C:\runtime.zip -DestinationPath C:\gcc -Force; ` |
| 205 |
+ Write-Host INFO: Expanding compiler 3 of 3...; ` |
|
| 176 | 206 |
Expand-Archive C:\binutils.zip -DestinationPath C:\gcc -Force; ` |
| 177 | 207 |
` |
| 208 |
+ Write-Host INFO: Removing downloaded files...; ` |
|
| 209 |
+ Remove-Item C:\gcc.zip; ` |
|
| 210 |
+ Remove-Item C:\runtime.zip; ` |
|
| 211 |
+ Remove-Item C:\binutils.zip; ` |
|
| 212 |
+ Remove-Item C:\gitsetup.7z.exe; ` |
|
| 213 |
+ ` |
|
| 214 |
+ Write-Host INFO: Creating source directory...; ` |
|
| 215 |
+ New-Item -ItemType Directory -Path C:\go\src\github.com\docker\docker | Out-Null; ` |
|
| 216 |
+ ` |
|
| 217 |
+ Write-Host INFO: Configuring git core.autocrlf...; ` |
|
| 218 |
+ C:\git\bin\git config --global core.autocrlf true; ` |
|
| 219 |
+ ` |
|
| 178 | 220 |
Write-Host INFO: Completed |
| 179 | 221 |
|
| 180 |
-# Prepare for building |
|
| 181 |
-COPY . C:\go\src\github.com\docker\docker |
|
| 222 |
+# Make PowerShell the default entrypoint |
|
| 223 |
+ENTRYPOINT ["powershell.exe"] |
|
| 224 |
+ |
|
| 225 |
+# Set the working directory to the location of the sources |
|
| 226 |
+WORKDIR C:\go\src\github.com\docker\docker |
|
| 182 | 227 |
|
| 228 |
+# Copy the sources into the container |
|
| 229 |
+COPY . . |
| ... | ... |
@@ -32,17 +32,17 @@ type copyBackend interface {
|
| 32 | 32 |
|
| 33 | 33 |
// stateBackend includes functions to implement to provide container state lifecycle functionality. |
| 34 | 34 |
type stateBackend interface {
|
| 35 |
- ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (container.ContainerCreateCreatedBody, error) |
|
| 35 |
+ ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) |
|
| 36 | 36 |
ContainerKill(name string, sig uint64) error |
| 37 | 37 |
ContainerPause(name string) error |
| 38 | 38 |
ContainerRename(oldName, newName string) error |
| 39 | 39 |
ContainerResize(name string, height, width int) error |
| 40 | 40 |
ContainerRestart(name string, seconds *int) error |
| 41 | 41 |
ContainerRm(name string, config *types.ContainerRmConfig) error |
| 42 |
- ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error |
|
| 42 |
+ ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error |
|
| 43 | 43 |
ContainerStop(name string, seconds *int) error |
| 44 | 44 |
ContainerUnpause(name string) error |
| 45 |
- ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (container.ContainerUpdateOKBody, error) |
|
| 45 |
+ ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) |
|
| 46 | 46 |
ContainerWait(name string, timeout time.Duration) (int, error) |
| 47 | 47 |
} |
| 48 | 48 |
|
| ... | ... |
@@ -156,8 +156,7 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon |
| 156 | 156 |
|
| 157 | 157 |
checkpoint := r.Form.Get("checkpoint")
|
| 158 | 158 |
checkpointDir := r.Form.Get("checkpoint-dir")
|
| 159 |
- validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") |
|
| 160 |
- if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint, checkpointDir); err != nil {
|
|
| 159 |
+ if err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
|
|
| 161 | 160 |
return err |
| 162 | 161 |
} |
| 163 | 162 |
|
| ... | ... |
@@ -332,7 +331,6 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon |
| 332 | 332 |
return err |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
- version := httputils.VersionFromContext(ctx) |
|
| 336 | 335 |
var updateConfig container.UpdateConfig |
| 337 | 336 |
|
| 338 | 337 |
decoder := json.NewDecoder(r.Body) |
| ... | ... |
@@ -346,8 +344,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon |
| 346 | 346 |
} |
| 347 | 347 |
|
| 348 | 348 |
name := vars["name"] |
| 349 |
- validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") |
|
| 350 |
- resp, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname) |
|
| 349 |
+ resp, err := s.backend.ContainerUpdate(name, hostConfig) |
|
| 351 | 350 |
if err != nil {
|
| 352 | 351 |
return err |
| 353 | 352 |
} |
| ... | ... |
@@ -372,14 +369,13 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 372 | 372 |
version := httputils.VersionFromContext(ctx) |
| 373 | 373 |
adjustCPUShares := versions.LessThan(version, "1.19") |
| 374 | 374 |
|
| 375 |
- validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") |
|
| 376 | 375 |
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
| 377 | 376 |
Name: name, |
| 378 | 377 |
Config: config, |
| 379 | 378 |
HostConfig: hostConfig, |
| 380 | 379 |
NetworkingConfig: networkingConfig, |
| 381 | 380 |
AdjustCPUShares: adjustCPUShares, |
| 382 |
- }, validateHostname) |
|
| 381 |
+ }) |
|
| 383 | 382 |
if err != nil {
|
| 384 | 383 |
return err |
| 385 | 384 |
} |
| ... | ... |
@@ -116,7 +116,7 @@ type Backend interface {
|
| 116 | 116 |
// ContainerAttachRaw attaches to container. |
| 117 | 117 |
ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error |
| 118 | 118 |
// ContainerCreate creates a new Docker container and returns potential warnings |
| 119 |
- ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (container.ContainerCreateCreatedBody, error) |
|
| 119 |
+ ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) |
|
| 120 | 120 |
// ContainerRm removes a container specified by `id`. |
| 121 | 121 |
ContainerRm(name string, config *types.ContainerRmConfig) error |
| 122 | 122 |
// Commit creates a new Docker image from an existing Docker container. |
| ... | ... |
@@ -124,7 +124,7 @@ type Backend interface {
|
| 124 | 124 |
// ContainerKill stops the container execution abruptly. |
| 125 | 125 |
ContainerKill(containerID string, sig uint64) error |
| 126 | 126 |
// ContainerStart starts a new container |
| 127 |
- ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error |
|
| 127 |
+ ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error |
|
| 128 | 128 |
// ContainerWait stops processing until the given container is stopped. |
| 129 | 129 |
ContainerWait(containerID string, timeout time.Duration) (int, error) |
| 130 | 130 |
// ContainerUpdateCmdOnBuild updates container.Path and container.Args |
| ... | ... |
@@ -307,7 +307,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str |
| 307 | 307 |
return nil |
| 308 | 308 |
} |
| 309 | 309 |
|
| 310 |
- container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig}, true)
|
|
| 310 |
+ container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig})
|
|
| 311 | 311 |
if err != nil {
|
| 312 | 312 |
return err |
| 313 | 313 |
} |
| ... | ... |
@@ -180,7 +180,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD |
| 180 | 180 |
return nil |
| 181 | 181 |
} |
| 182 | 182 |
|
| 183 |
- container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig}, true)
|
|
| 183 |
+ container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig})
|
|
| 184 | 184 |
if err != nil {
|
| 185 | 185 |
return err |
| 186 | 186 |
} |
| ... | ... |
@@ -496,7 +496,7 @@ func (b *Builder) create() (string, error) {
|
| 496 | 496 |
c, err := b.docker.ContainerCreate(types.ContainerCreateConfig{
|
| 497 | 497 |
Config: b.runConfig, |
| 498 | 498 |
HostConfig: hostConfig, |
| 499 |
- }, true) |
|
| 499 |
+ }) |
|
| 500 | 500 |
if err != nil {
|
| 501 | 501 |
return "", err |
| 502 | 502 |
} |
| ... | ... |
@@ -537,7 +537,7 @@ func (b *Builder) run(cID string) (err error) {
|
| 537 | 537 |
} |
| 538 | 538 |
}() |
| 539 | 539 |
|
| 540 |
- if err := b.docker.ContainerStart(cID, nil, true, "", ""); err != nil {
|
|
| 540 |
+ if err := b.docker.ContainerStart(cID, nil, "", ""); err != nil {
|
|
| 541 | 541 |
close(finished) |
| 542 | 542 |
if cancelErr := <-cancelErrCh; cancelErr != nil {
|
| 543 | 543 |
logrus.Debugf("Build cancelled (%v) and got an error from ContainerStart: %v",
|
| ... | ... |
@@ -176,17 +176,10 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) {
|
| 176 | 176 |
newline := scanner.Text() |
| 177 | 177 |
currentLine++ |
| 178 | 178 |
|
| 179 |
- // If escape followed by a comment line then stop |
|
| 180 |
- // Note here that comment line starts with `#` at |
|
| 181 |
- // the first pos of the line |
|
| 182 |
- if stripComments(newline) == "" {
|
|
| 183 |
- break |
|
| 179 |
+ if stripComments(strings.TrimSpace(newline)) == "" {
|
|
| 180 |
+ continue |
|
| 184 | 181 |
} |
| 185 | 182 |
|
| 186 |
- // If escape followed by an empty line then stop |
|
| 187 |
- if strings.TrimSpace(newline) == "" {
|
|
| 188 |
- break |
|
| 189 |
- } |
|
| 190 | 183 |
line, child, err = ParseLine(line+newline, d, false) |
| 191 | 184 |
if err != nil {
|
| 192 | 185 |
return nil, err |
| ... | ... |
@@ -150,8 +150,8 @@ func TestLineInformation(t *testing.T) {
|
| 150 | 150 |
t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
|
| 151 | 151 |
} |
| 152 | 152 |
|
| 153 |
- if ast.StartLine != 5 || ast.EndLine != 27 {
|
|
| 154 |
- fmt.Fprintf(os.Stderr, "Wrong root line information: expected(%d-%d), actual(%d-%d)\n", 5, 27, ast.StartLine, ast.EndLine) |
|
| 153 |
+ if ast.StartLine != 5 || ast.EndLine != 31 {
|
|
| 154 |
+ fmt.Fprintf(os.Stderr, "Wrong root line information: expected(%d-%d), actual(%d-%d)\n", 5, 31, ast.StartLine, ast.EndLine) |
|
| 155 | 155 |
t.Fatalf("Root line information doesn't match result.")
|
| 156 | 156 |
} |
| 157 | 157 |
if len(ast.Children) != 3 {
|
| ... | ... |
@@ -161,7 +161,7 @@ func TestLineInformation(t *testing.T) {
|
| 161 | 161 |
expected := [][]int{
|
| 162 | 162 |
{5, 5},
|
| 163 | 163 |
{11, 12},
|
| 164 |
- {17, 27},
|
|
| 164 |
+ {17, 31},
|
|
| 165 | 165 |
} |
| 166 | 166 |
for i, child := range ast.Children {
|
| 167 | 167 |
if child.StartLine != expected[i][0] || child.EndLine != expected[i][1] {
|
| ... | ... |
@@ -16,11 +16,15 @@ ENV GOPATH \ |
| 16 | 16 |
# Install the packages we need, clean up after them and us |
| 17 | 17 |
RUN apt-get update \ |
| 18 | 18 |
&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean \
|
| 19 |
+ |
|
| 20 |
+ |
|
| 19 | 21 |
&& apt-get install -y --no-install-recommends git golang ca-certificates \ |
| 20 | 22 |
&& apt-get clean \ |
| 21 | 23 |
&& rm -rf /var/lib/apt/lists \ |
| 24 |
+ |
|
| 22 | 25 |
&& go get -v github.com/brimstone/consuldock \ |
| 23 | 26 |
&& mv $GOPATH/bin/consuldock /usr/local/bin/consuldock \ |
| 27 |
+ |
|
| 24 | 28 |
&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \
|
| 25 | 29 |
&& apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \
|
| 26 | 30 |
&& rm /tmp/dpkg.* \ |
| ... | ... |
@@ -16,8 +16,10 @@ RUN apt-get update \ |
| 16 | 16 |
&& apt-get install -y --no-install-recommends git golang ca-certificates \ |
| 17 | 17 |
&& apt-get clean \ |
| 18 | 18 |
&& rm -rf /var/lib/apt/lists \ |
| 19 |
+ |
|
| 19 | 20 |
&& go get -v github.com/brimstone/consuldock \ |
| 20 | 21 |
&& mv $GOPATH/bin/consuldock /usr/local/bin/consuldock \ |
| 22 |
+ |
|
| 21 | 23 |
&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \
|
| 22 | 24 |
&& apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \
|
| 23 | 25 |
&& rm /tmp/dpkg.* \ |
| ... | ... |
@@ -23,12 +23,14 @@ RUN apt-get update \ |
| 23 | 23 |
&& apt-get install -y --no-install-recommends unzip wget \ |
| 24 | 24 |
&& apt-get clean \ |
| 25 | 25 |
&& rm -rf /var/lib/apt/lists \ |
| 26 |
+ |
|
| 26 | 27 |
&& cd /tmp \ |
| 27 | 28 |
&& wget https://dl.bintray.com/mitchellh/consul/0.3.1_web_ui.zip \ |
| 28 | 29 |
-O web_ui.zip \ |
| 29 | 30 |
&& unzip web_ui.zip \ |
| 30 | 31 |
&& mv dist /webui \ |
| 31 | 32 |
&& rm web_ui.zip \ |
| 33 |
+ |
|
| 32 | 34 |
&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \
|
| 33 | 35 |
&& apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \
|
| 34 | 36 |
&& rm /tmp/dpkg.* |
| ... | ... |
@@ -40,8 +42,10 @@ RUN apt-get update \ |
| 40 | 40 |
&& apt-get install -y --no-install-recommends git golang ca-certificates build-essential \ |
| 41 | 41 |
&& apt-get clean \ |
| 42 | 42 |
&& rm -rf /var/lib/apt/lists \ |
| 43 |
+ |
|
| 43 | 44 |
&& go get -v github.com/hashicorp/consul \ |
| 44 | 45 |
&& mv $GOPATH/bin/consul /usr/bin/consul \ |
| 46 |
+ |
|
| 45 | 47 |
&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \
|
| 46 | 48 |
&& apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \
|
| 47 | 49 |
&& rm /tmp/dpkg.* \ |
| 11 | 10 |
deleted file mode 100644 |
| ... | ... |
@@ -1,15 +0,0 @@ |
| 1 |
-FROM busybox |
|
| 2 |
- |
|
| 3 |
-# The following will create two instructions |
|
| 4 |
-# `Run foo` |
|
| 5 |
-# `bar` |
|
| 6 |
-# because empty line will break the escape. |
|
| 7 |
-# The parser will generate the following: |
|
| 8 |
-# (from "busybox") |
|
| 9 |
-# (run "foo") |
|
| 10 |
-# (bar "") |
|
| 11 |
-# And `bar` will return an error instruction later |
|
| 12 |
-# Note: Parse() will not immediately error out. |
|
| 13 |
-RUN foo \ |
|
| 14 |
- |
|
| 15 |
-bar |
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/docker/cli" |
| 9 | 9 |
"github.com/docker/docker/cli/command" |
| 10 |
+ "github.com/docker/docker/pkg/stringid" |
|
| 10 | 11 |
"github.com/docker/docker/pkg/stringutils" |
| 11 | 12 |
"github.com/spf13/cobra" |
| 12 | 13 |
"golang.org/x/net/context" |
| ... | ... |
@@ -43,17 +44,19 @@ func runList(dockerCli *command.DockerCli, opts listOptions) error {
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0) |
| 46 |
- fmt.Fprintf(w, "NAME \tTAG \tDESCRIPTION\tENABLED") |
|
| 46 |
+ fmt.Fprintf(w, "ID \tNAME \tTAG \tDESCRIPTION\tENABLED") |
|
| 47 | 47 |
fmt.Fprintf(w, "\n") |
| 48 | 48 |
|
| 49 | 49 |
for _, p := range plugins {
|
| 50 |
+ id := p.ID |
|
| 50 | 51 |
desc := strings.Replace(p.Config.Description, "\n", " ", -1) |
| 51 | 52 |
desc = strings.Replace(desc, "\r", " ", -1) |
| 52 | 53 |
if !opts.noTrunc {
|
| 54 |
+ id = stringid.TruncateID(p.ID) |
|
| 53 | 55 |
desc = stringutils.Ellipsis(desc, 45) |
| 54 | 56 |
} |
| 55 | 57 |
|
| 56 |
- fmt.Fprintf(w, "%s\t%s\t%s\t%v\n", p.Name, p.Tag, desc, p.Enabled) |
|
| 58 |
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%v\n", id, p.Name, p.Tag, desc, p.Enabled) |
|
| 57 | 59 |
} |
| 58 | 60 |
w.Flush() |
| 59 | 61 |
return nil |
| ... | ... |
@@ -45,7 +45,7 @@ func NewInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 45 | 45 |
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
| 46 | 46 |
var elementSearcher inspect.GetRefFunc |
| 47 | 47 |
switch opts.inspectType {
|
| 48 |
- case "", "container", "image", "node", "network", "service", "volume", "task": |
|
| 48 |
+ case "", "container", "image", "node", "network", "service", "volume", "task", "plugin": |
|
| 49 | 49 |
elementSearcher = inspectAll(context.Background(), dockerCli, opts.size, opts.inspectType) |
| 50 | 50 |
default: |
| 51 | 51 |
return fmt.Errorf("%q is not a valid value for --type", opts.inspectType)
|
| ... | ... |
@@ -95,6 +95,12 @@ func inspectVolume(ctx context.Context, dockerCli *command.DockerCli) inspect.Ge |
| 95 | 95 |
} |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
+func inspectPlugin(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
|
|
| 99 |
+ return func(ref string) (interface{}, []byte, error) {
|
|
| 100 |
+ return dockerCli.Client().PluginInspectWithRaw(ctx, ref) |
|
| 101 |
+ } |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 98 | 104 |
func inspectAll(ctx context.Context, dockerCli *command.DockerCli, getSize bool, typeConstraint string) inspect.GetRefFunc {
|
| 99 | 105 |
var inspectAutodetect = []struct {
|
| 100 | 106 |
ObjectType string |
| ... | ... |
@@ -108,6 +114,7 @@ func inspectAll(ctx context.Context, dockerCli *command.DockerCli, getSize bool, |
| 108 | 108 |
{"service", false, inspectService(ctx, dockerCli)},
|
| 109 | 109 |
{"task", false, inspectTasks(ctx, dockerCli)},
|
| 110 | 110 |
{"node", false, inspectNode(ctx, dockerCli)},
|
| 111 |
+ {"plugin", false, inspectPlugin(ctx, dockerCli)},
|
|
| 111 | 112 |
} |
| 112 | 113 |
|
| 113 | 114 |
isErrNotSwarmManager := func(err error) bool {
|
| ... | ... |
@@ -255,3 +255,24 @@ func IsErrSecretNotFound(err error) bool {
|
| 255 | 255 |
_, ok := err.(secretNotFoundError) |
| 256 | 256 |
return ok |
| 257 | 257 |
} |
| 258 |
+ |
|
| 259 |
+// pluginNotFoundError implements an error returned when a plugin is not in the docker host. |
|
| 260 |
+type pluginNotFoundError struct {
|
|
| 261 |
+ name string |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// NotFound indicates that this error type is of NotFound |
|
| 265 |
+func (e pluginNotFoundError) NotFound() bool {
|
|
| 266 |
+ return true |
|
| 267 |
+} |
|
| 268 |
+ |
|
| 269 |
+// Error returns a string representation of a pluginNotFoundError |
|
| 270 |
+func (e pluginNotFoundError) Error() string {
|
|
| 271 |
+ return fmt.Sprintf("Error: No such plugin: %s", e.name)
|
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+// IsErrPluginNotFound returns true if the error is caused |
|
| 275 |
+// when a plugin is not found in the docker host. |
|
| 276 |
+func IsErrPluginNotFound(err error) bool {
|
|
| 277 |
+ return IsErrNotFound(err) |
|
| 278 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"io/ioutil" |
| 7 |
+ "net/http" |
|
| 7 | 8 |
|
| 8 | 9 |
"github.com/docker/docker/api/types" |
| 9 | 10 |
"golang.org/x/net/context" |
| ... | ... |
@@ -13,6 +14,9 @@ import ( |
| 13 | 13 |
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
| 14 | 14 |
resp, err := cli.get(ctx, "/plugins/"+name, nil, nil) |
| 15 | 15 |
if err != nil {
|
| 16 |
+ if resp.statusCode == http.StatusNotFound {
|
|
| 17 |
+ return nil, nil, pluginNotFoundError{name}
|
|
| 18 |
+ } |
|
| 16 | 19 |
return nil, nil, err |
| 17 | 20 |
} |
| 18 | 21 |
|
| ... | ... |
@@ -28,8 +28,8 @@ type Backend interface {
|
| 28 | 28 |
FindNetwork(idName string) (libnetwork.Network, error) |
| 29 | 29 |
SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error |
| 30 | 30 |
PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error |
| 31 |
- CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (container.ContainerCreateCreatedBody, error) |
|
| 32 |
- ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error |
|
| 31 |
+ CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) |
|
| 32 |
+ ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error |
|
| 33 | 33 |
ContainerStop(name string, seconds *int) error |
| 34 | 34 |
ContainerLogs(context.Context, string, *backend.ContainerLogsConfig, chan struct{}) error
|
| 35 | 35 |
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error |
| ... | ... |
@@ -11,12 +11,10 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
"github.com/Sirupsen/logrus" |
| 13 | 13 |
"github.com/docker/distribution/digest" |
| 14 |
- "github.com/docker/docker/api/server/httputils" |
|
| 15 | 14 |
"github.com/docker/docker/api/types" |
| 16 | 15 |
"github.com/docker/docker/api/types/backend" |
| 17 | 16 |
containertypes "github.com/docker/docker/api/types/container" |
| 18 | 17 |
"github.com/docker/docker/api/types/events" |
| 19 |
- "github.com/docker/docker/api/types/versions" |
|
| 20 | 18 |
"github.com/docker/docker/daemon/cluster/convert" |
| 21 | 19 |
executorpkg "github.com/docker/docker/daemon/cluster/executor" |
| 22 | 20 |
"github.com/docker/docker/reference" |
| ... | ... |
@@ -215,8 +213,6 @@ func (c *containerAdapter) waitForDetach(ctx context.Context) error {
|
| 215 | 215 |
func (c *containerAdapter) create(ctx context.Context) error {
|
| 216 | 216 |
var cr containertypes.ContainerCreateCreatedBody |
| 217 | 217 |
var err error |
| 218 |
- version := httputils.VersionFromContext(ctx) |
|
| 219 |
- validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") |
|
| 220 | 218 |
|
| 221 | 219 |
if cr, err = c.backend.CreateManagedContainer(types.ContainerCreateConfig{
|
| 222 | 220 |
Name: c.container.name(), |
| ... | ... |
@@ -224,7 +220,7 @@ func (c *containerAdapter) create(ctx context.Context) error {
|
| 224 | 224 |
HostConfig: c.container.hostConfig(), |
| 225 | 225 |
// Use the first network in container create |
| 226 | 226 |
NetworkingConfig: c.container.createNetworkingConfig(), |
| 227 |
- }, validateHostname); err != nil {
|
|
| 227 |
+ }); err != nil {
|
|
| 228 | 228 |
return err |
| 229 | 229 |
} |
| 230 | 230 |
|
| ... | ... |
@@ -263,9 +259,7 @@ func (c *containerAdapter) create(ctx context.Context) error {
|
| 263 | 263 |
} |
| 264 | 264 |
|
| 265 | 265 |
func (c *containerAdapter) start(ctx context.Context) error {
|
| 266 |
- version := httputils.VersionFromContext(ctx) |
|
| 267 |
- validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") |
|
| 268 |
- return c.backend.ContainerStart(c.container.name(), nil, validateHostname, "", "") |
|
| 266 |
+ return c.backend.ContainerStart(c.container.name(), nil, "", "") |
|
| 269 | 267 |
} |
| 270 | 268 |
|
| 271 | 269 |
func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
|
| ... | ... |
@@ -3,7 +3,6 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"path/filepath" |
| 6 |
- "regexp" |
|
| 7 | 6 |
"time" |
| 8 | 7 |
|
| 9 | 8 |
"github.com/docker/docker/api/errors" |
| ... | ... |
@@ -204,7 +203,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig * |
| 204 | 204 |
|
| 205 | 205 |
// verifyContainerSettings performs validation of the hostconfig and config |
| 206 | 206 |
// structures. |
| 207 |
-func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool, validateHostname bool) ([]string, error) {
|
|
| 207 |
+func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
|
|
| 208 | 208 |
|
| 209 | 209 |
// First perform verification of settings common across all platforms. |
| 210 | 210 |
if config != nil {
|
| ... | ... |
@@ -222,18 +221,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon |
| 222 | 222 |
} |
| 223 | 223 |
} |
| 224 | 224 |
|
| 225 |
- // Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant. |
|
| 226 |
- if validateHostname && len(config.Hostname) > 0 {
|
|
| 227 |
- // RFC1123 specifies that 63 bytes is the maximium length |
|
| 228 |
- // Windows has the limitation of 63 bytes in length |
|
| 229 |
- // Linux hostname is limited to HOST_NAME_MAX=64, not including the terminating null byte. |
|
| 230 |
- // We limit the length to 63 bytes here to match RFC1035 and RFC1123. |
|
| 231 |
- matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname)
|
|
| 232 |
- if len(config.Hostname) > 63 || !matched {
|
|
| 233 |
- return nil, fmt.Errorf("invalid hostname format: %s", config.Hostname)
|
|
| 234 |
- } |
|
| 235 |
- } |
|
| 236 |
- |
|
| 237 | 225 |
// Validate if Env contains empty variable or not (e.g., ``, `=foo`) |
| 238 | 226 |
for _, env := range config.Env {
|
| 239 | 227 |
if _, err := opts.ValidateEnv(env); err != nil {
|
| ... | ... |
@@ -25,22 +25,22 @@ import ( |
| 25 | 25 |
) |
| 26 | 26 |
|
| 27 | 27 |
// CreateManagedContainer creates a container that is managed by a Service |
| 28 |
-func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig, validateHostname bool) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 29 |
- return daemon.containerCreate(params, true, validateHostname) |
|
| 28 |
+func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 29 |
+ return daemon.containerCreate(params, true) |
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
// ContainerCreate creates a regular container |
| 33 |
-func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig, validateHostname bool) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 34 |
- return daemon.containerCreate(params, false, validateHostname) |
|
| 33 |
+func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 34 |
+ return daemon.containerCreate(params, false) |
|
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
-func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool, validateHostname bool) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 37 |
+func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) {
|
|
| 38 | 38 |
start := time.Now() |
| 39 | 39 |
if params.Config == nil {
|
| 40 | 40 |
return containertypes.ContainerCreateCreatedBody{}, fmt.Errorf("Config cannot be empty in order to create a container")
|
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
- warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false, validateHostname) |
|
| 43 |
+ warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false) |
|
| 44 | 44 |
if err != nil {
|
| 45 | 45 |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err
|
| 46 | 46 |
} |
| ... | ... |
@@ -19,7 +19,7 @@ import ( |
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 | 21 |
// ContainerStart starts a container. |
| 22 |
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error {
|
|
| 22 |
+func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
|
|
| 23 | 23 |
if checkpoint != "" && !daemon.HasExperimental() {
|
| 24 | 24 |
return apierrors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode"))
|
| 25 | 25 |
} |
| ... | ... |
@@ -73,7 +73,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos |
| 73 | 73 |
|
| 74 | 74 |
// check if hostConfig is in line with the current system settings. |
| 75 | 75 |
// It may happen cgroups are umounted or the like. |
| 76 |
- if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false, validateHostname); err != nil {
|
|
| 76 |
+ if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false); err != nil {
|
|
| 77 | 77 |
return err |
| 78 | 78 |
} |
| 79 | 79 |
// Adapt for old containers in case we have updates in this function and |
| ... | ... |
@@ -7,10 +7,10 @@ import ( |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 | 9 |
// ContainerUpdate updates configuration of the container |
| 10 |
-func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (container.ContainerUpdateOKBody, error) {
|
|
| 10 |
+func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) {
|
|
| 11 | 11 |
var warnings []string |
| 12 | 12 |
|
| 13 |
- warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true, validateHostname) |
|
| 13 |
+ warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true) |
|
| 14 | 14 |
if err != nil {
|
| 15 | 15 |
return container.ContainerUpdateOKBody{Warnings: warnings}, err
|
| 16 | 16 |
} |
| ... | ... |
@@ -3962,8 +3962,9 @@ List nodes |
| 3962 | 3962 |
|
| 3963 | 3963 |
**Status codes**: |
| 3964 | 3964 |
|
| 3965 |
-- **200** – no error |
|
| 3966 |
-- **500** – server error |
|
| 3965 |
+- **200** – no error |
|
| 3966 |
+- **406** - node is not part of a swarm |
|
| 3967 |
+- **500** – server error |
|
| 3967 | 3968 |
|
| 3968 | 3969 |
#### Inspect a node |
| 3969 | 3970 |
|
| ... | ... |
@@ -4045,6 +4046,7 @@ Return low-level information on the node `id` |
| 4045 | 4045 |
|
| 4046 | 4046 |
- **200** – no error |
| 4047 | 4047 |
- **404** – no such node |
| 4048 |
+- **406** – node is not part of a swarm |
|
| 4048 | 4049 |
- **500** – server error |
| 4049 | 4050 |
|
| 4050 | 4051 |
#### Remove a node |
| ... | ... |
@@ -4073,6 +4075,7 @@ Remove a node from the swarm. |
| 4073 | 4073 |
|
| 4074 | 4074 |
- **200** – no error |
| 4075 | 4075 |
- **404** – no such node |
| 4076 |
+- **406** – node is not part of a swarm |
|
| 4076 | 4077 |
- **500** – server error |
| 4077 | 4078 |
|
| 4078 | 4079 |
#### Update a node |
| ... | ... |
@@ -4128,6 +4131,7 @@ JSON Parameters: |
| 4128 | 4128 |
|
| 4129 | 4129 |
- **200** – no error |
| 4130 | 4130 |
- **404** – no such node |
| 4131 |
+- **406** – node is not part of a swarm |
|
| 4131 | 4132 |
- **500** – server error |
| 4132 | 4133 |
|
| 4133 | 4134 |
### 3.8 Swarm |
| ... | ... |
@@ -4178,7 +4182,9 @@ Inspect swarm |
| 4178 | 4178 |
|
| 4179 | 4179 |
**Status codes**: |
| 4180 | 4180 |
|
| 4181 |
-- **200** - no error |
|
| 4181 |
+- **200** - no error |
|
| 4182 |
+- **406** – node is not part of a swarm |
|
| 4183 |
+- **500** - sever error |
|
| 4182 | 4184 |
|
| 4183 | 4185 |
#### Initialize a new swarm |
| 4184 | 4186 |
|
| ... | ... |
@@ -4216,9 +4222,10 @@ Initialize a new swarm. The body of the HTTP response includes the node ID. |
| 4216 | 4216 |
|
| 4217 | 4217 |
**Status codes**: |
| 4218 | 4218 |
|
| 4219 |
-- **200** – no error |
|
| 4220 |
-- **400** – bad parameter |
|
| 4221 |
-- **406** – node is already part of a swarm |
|
| 4219 |
+- **200** – no error |
|
| 4220 |
+- **400** – bad parameter |
|
| 4221 |
+- **406** – node is already part of a swarm |
|
| 4222 |
+- **500** - server error |
|
| 4222 | 4223 |
|
| 4223 | 4224 |
JSON Parameters: |
| 4224 | 4225 |
|
| ... | ... |
@@ -4282,9 +4289,10 @@ Join an existing swarm |
| 4282 | 4282 |
|
| 4283 | 4283 |
**Status codes**: |
| 4284 | 4284 |
|
| 4285 |
-- **200** – no error |
|
| 4286 |
-- **400** – bad parameter |
|
| 4287 |
-- **406** – node is already part of a swarm |
|
| 4285 |
+- **200** – no error |
|
| 4286 |
+- **400** – bad parameter |
|
| 4287 |
+- **406** – node is already part of a swarm |
|
| 4288 |
+- **500** - server error |
|
| 4288 | 4289 |
|
| 4289 | 4290 |
JSON Parameters: |
| 4290 | 4291 |
|
| ... | ... |
@@ -4321,8 +4329,9 @@ Leave a swarm |
| 4321 | 4321 |
|
| 4322 | 4322 |
**Status codes**: |
| 4323 | 4323 |
|
| 4324 |
-- **200** – no error |
|
| 4325 |
-- **406** – node is not part of a swarm |
|
| 4324 |
+- **200** – no error |
|
| 4325 |
+- **406** – node is not part of a swarm |
|
| 4326 |
+- **500** - server error |
|
| 4326 | 4327 |
|
| 4327 | 4328 |
#### Update a swarm |
| 4328 | 4329 |
|
| ... | ... |
@@ -4374,9 +4383,10 @@ Update a swarm |
| 4374 | 4374 |
|
| 4375 | 4375 |
**Status codes**: |
| 4376 | 4376 |
|
| 4377 |
-- **200** – no error |
|
| 4378 |
-- **400** – bad parameter |
|
| 4379 |
-- **406** – node is not part of a swarm |
|
| 4377 |
+- **200** – no error |
|
| 4378 |
+- **400** – bad parameter |
|
| 4379 |
+- **406** – node is not part of a swarm |
|
| 4380 |
+- **500** - server error |
|
| 4380 | 4381 |
|
| 4381 | 4382 |
JSON Parameters: |
| 4382 | 4383 |
|
| ... | ... |
@@ -4511,8 +4521,9 @@ List services |
| 4511 | 4511 |
|
| 4512 | 4512 |
**Status codes**: |
| 4513 | 4513 |
|
| 4514 |
-- **200** – no error |
|
| 4515 |
-- **500** – server error |
|
| 4514 |
+- **200** – no error |
|
| 4515 |
+- **406** – node is not part of a swarm |
|
| 4516 |
+- **500** – server error |
|
| 4516 | 4517 |
|
| 4517 | 4518 |
#### Create a service |
| 4518 | 4519 |
|
| ... | ... |
@@ -4606,9 +4617,10 @@ image](#create-an-image) section for more details. |
| 4606 | 4606 |
|
| 4607 | 4607 |
**Status codes**: |
| 4608 | 4608 |
|
| 4609 |
-- **201** – no error |
|
| 4610 |
-- **406** – server error or node is not part of a swarm |
|
| 4611 |
-- **409** – name conflicts with an existing object |
|
| 4609 |
+- **201** – no error |
|
| 4610 |
+- **406** – node is not part of a swarm |
|
| 4611 |
+- **409** – name conflicts with an existing object |
|
| 4612 |
+- **500** - server error |
|
| 4612 | 4613 |
|
| 4613 | 4614 |
**JSON Parameters**: |
| 4614 | 4615 |
|
| ... | ... |
@@ -4708,6 +4720,7 @@ Stop and remove the service `id` |
| 4708 | 4708 |
|
| 4709 | 4709 |
- **200** – no error |
| 4710 | 4710 |
- **404** – no such service |
| 4711 |
+- **406** - node is not part of a swarm |
|
| 4711 | 4712 |
- **500** – server error |
| 4712 | 4713 |
|
| 4713 | 4714 |
#### Inspect one or more services |
| ... | ... |
@@ -4797,6 +4810,7 @@ Return information on the service `id`. |
| 4797 | 4797 |
|
| 4798 | 4798 |
- **200** – no error |
| 4799 | 4799 |
- **404** – no such service |
| 4800 |
+- **406** - node is not part of a swarm |
|
| 4800 | 4801 |
- **500** – server error |
| 4801 | 4802 |
|
| 4802 | 4803 |
#### Update a service |
| ... | ... |
@@ -4930,6 +4944,7 @@ image](#create-an-image) section for more details. |
| 4930 | 4930 |
|
| 4931 | 4931 |
- **200** – no error |
| 4932 | 4932 |
- **404** – no such service |
| 4933 |
+- **406** - node is not part of a swarm |
|
| 4933 | 4934 |
- **500** – server error |
| 4934 | 4935 |
|
| 4935 | 4936 |
### 3.10 Tasks |
| ... | ... |
@@ -5131,15 +5146,16 @@ List tasks |
| 5131 | 5131 |
|
| 5132 | 5132 |
**Status codes**: |
| 5133 | 5133 |
|
| 5134 |
-- **200** – no error |
|
| 5135 |
-- **500** – server error |
|
| 5134 |
+- **200** – no error |
|
| 5135 |
+- **406** - node is not part of a swarm |
|
| 5136 |
+- **500** – server error |
|
| 5136 | 5137 |
|
| 5137 | 5138 |
#### Inspect a task |
| 5138 | 5139 |
|
| 5139 | 5140 |
|
| 5140 |
-`GET /tasks/(task id)` |
|
| 5141 |
+`GET /tasks/(id)` |
|
| 5141 | 5142 |
|
| 5142 |
-Get details on a task |
|
| 5143 |
+Get details on the task `id` |
|
| 5143 | 5144 |
|
| 5144 | 5145 |
**Example request**: |
| 5145 | 5146 |
|
| ... | ... |
@@ -5233,9 +5249,10 @@ Get details on a task |
| 5233 | 5233 |
|
| 5234 | 5234 |
**Status codes**: |
| 5235 | 5235 |
|
| 5236 |
-- **200** – no error |
|
| 5237 |
-- **404** – unknown task |
|
| 5238 |
-- **500** – server error |
|
| 5236 |
+- **200** – no error |
|
| 5237 |
+- **404** – unknown task |
|
| 5238 |
+- **406** - node is not part of a swarm |
|
| 5239 |
+- **500** – server error |
|
| 5239 | 5240 |
|
| 5240 | 5241 |
## 4. Going further |
| 5241 | 5242 |
|
| ... | ... |
@@ -34,7 +34,7 @@ The `docker logs` command batch-retrieves logs present at the time of execution. |
| 34 | 34 |
> **Note**: this command is only functional for containers that are started with |
| 35 | 35 |
> the `json-file` or `journald` logging driver. |
| 36 | 36 |
|
| 37 |
-For more information about selecting and configuring login-drivers, refer to |
|
| 37 |
+For more information about selecting and configuring logging drivers, refer to |
|
| 38 | 38 |
[Configure logging drivers](https://docs.docker.com/engine/admin/logging/overview/). |
| 39 | 39 |
|
| 40 | 40 |
The `docker logs --follow` command will continue streaming the new output from |
| ... | ... |
@@ -36,8 +36,8 @@ Example output: |
| 36 | 36 |
```bash |
| 37 | 37 |
$ docker plugin ls |
| 38 | 38 |
|
| 39 |
-NAME TAG DESCRIPTION ENABLED |
|
| 40 |
-tiborvass/no-remove latest A test plugin for Docker true |
|
| 39 |
+ID NAME TAG DESCRIPTION ENABLED |
|
| 40 |
+69553ca1d123 tiborvass/no-remove latest A test plugin for Docker true |
|
| 41 | 41 |
``` |
| 42 | 42 |
|
| 43 | 43 |
## Related information |
| ... | ... |
@@ -35,7 +35,7 @@ The `docker service logs` command batch-retrieves logs present at the time of ex |
| 35 | 35 |
> **Note**: this command is only functional for services that are started with |
| 36 | 36 |
> the `json-file` or `journald` logging driver. |
| 37 | 37 |
|
| 38 |
-For more information about selecting and configuring login-drivers, refer to |
|
| 38 |
+For more information about selecting and configuring logging drivers, refer to |
|
| 39 | 39 |
[Configure logging drivers](https://docs.docker.com/engine/admin/logging/overview/). |
| 40 | 40 |
|
| 41 | 41 |
The `docker service logs --follow` command will continue streaming the new output from |
| 42 | 42 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,401 @@ |
| 0 |
+<# |
|
| 1 |
+.NOTES |
|
| 2 |
+ Author: @jhowardmsft |
|
| 3 |
+ |
|
| 4 |
+ Summary: Windows native build script. This is similar to functionality provided |
|
| 5 |
+ by hack\make.sh, but uses native Windows PowerShell semantics. It does |
|
| 6 |
+ not support the full set of options provided by the Linux counterpart. |
|
| 7 |
+ For example: |
|
| 8 |
+ |
|
| 9 |
+ - You can't cross-build Linux docker binaries on Windows |
|
| 10 |
+ - Hashes aren't generated on binaries |
|
| 11 |
+ - 'Releasing' isn't supported. |
|
| 12 |
+ - Integration tests. This is because they currently cannot run inside a container, |
|
| 13 |
+ and require significant external setup. |
|
| 14 |
+ |
|
| 15 |
+ It does however provided the minimum necessary to support parts of local Windows |
|
| 16 |
+ development and Windows to Windows CI. |
|
| 17 |
+ |
|
| 18 |
+ Usage Examples (run from repo root): |
|
| 19 |
+ "hack\make.ps1 -Binary" to build the binaries |
|
| 20 |
+ "hack\make.ps1 -Client" to build just the client 64-bit binary |
|
| 21 |
+ "hack\make.ps1 -TestUnit" to run unit tests |
|
| 22 |
+ "hack\make.ps1 -Binary -TestUnit" to build the binaries and run unit tests |
|
| 23 |
+ "hack\make.ps1 -All" to run everything this script knows about |
|
| 24 |
+ |
|
| 25 |
+.PARAMETER Client |
|
| 26 |
+ Builds the client binaries. |
|
| 27 |
+ |
|
| 28 |
+.PARAMETER Daemon |
|
| 29 |
+ Builds the daemon binary. |
|
| 30 |
+ |
|
| 31 |
+.PARAMETER Binary |
|
| 32 |
+ Builds the client binaries and the daemon binary. A convenient shortcut to `make.ps1 -Client -Daemon`. |
|
| 33 |
+ |
|
| 34 |
+.PARAMETER Race |
|
| 35 |
+ Use -race in go build and go test. |
|
| 36 |
+ |
|
| 37 |
+.PARAMETER Noisy |
|
| 38 |
+ Use -v in go build. |
|
| 39 |
+ |
|
| 40 |
+.PARAMETER ForceBuildAll |
|
| 41 |
+ Use -a in go build. |
|
| 42 |
+ |
|
| 43 |
+.PARAMETER NoOpt |
|
| 44 |
+ Use -gcflags -N -l in go build to disable optimisation (can aide debugging). |
|
| 45 |
+ |
|
| 46 |
+.PARAMETER CommitSuffix |
|
| 47 |
+ Adds a custom string to be appended to the commit ID (spaces are stripped). |
|
| 48 |
+ |
|
| 49 |
+.PARAMETER DCO |
|
| 50 |
+ Runs the DCO (Developer Certificate Of Origin) test. |
|
| 51 |
+ |
|
| 52 |
+.PARAMETER PkgImports |
|
| 53 |
+ Runs the pkg\ directory imports test. |
|
| 54 |
+ |
|
| 55 |
+.PARAMETER GoFormat |
|
| 56 |
+ Runs the Go formatting test. |
|
| 57 |
+ |
|
| 58 |
+.PARAMETER TestUnit |
|
| 59 |
+ Runs unit tests. |
|
| 60 |
+ |
|
| 61 |
+.PARAMETER All |
|
| 62 |
+ Runs everything this script knows about. |
|
| 63 |
+ |
|
| 64 |
+ |
|
| 65 |
+TODO |
|
| 66 |
+- Unify the head commit |
|
| 67 |
+- Sort out the GITCOMMIT environment variable in the absense of a .git (longer term) |
|
| 68 |
+- Add golint and other checks (swagger maybe?) |
|
| 69 |
+ |
|
| 70 |
+#> |
|
| 71 |
+ |
|
| 72 |
+ |
|
| 73 |
+param( |
|
| 74 |
+ [Parameter(Mandatory=$False)][switch]$Client, |
|
| 75 |
+ [Parameter(Mandatory=$False)][switch]$Daemon, |
|
| 76 |
+ [Parameter(Mandatory=$False)][switch]$Binary, |
|
| 77 |
+ [Parameter(Mandatory=$False)][switch]$Race, |
|
| 78 |
+ [Parameter(Mandatory=$False)][switch]$Noisy, |
|
| 79 |
+ [Parameter(Mandatory=$False)][switch]$ForceBuildAll, |
|
| 80 |
+ [Parameter(Mandatory=$False)][switch]$NoOpt, |
|
| 81 |
+ [Parameter(Mandatory=$False)][string]$CommitSuffix="", |
|
| 82 |
+ [Parameter(Mandatory=$False)][switch]$DCO, |
|
| 83 |
+ [Parameter(Mandatory=$False)][switch]$PkgImports, |
|
| 84 |
+ [Parameter(Mandatory=$False)][switch]$GoFormat, |
|
| 85 |
+ [Parameter(Mandatory=$False)][switch]$TestUnit, |
|
| 86 |
+ [Parameter(Mandatory=$False)][switch]$All |
|
| 87 |
+) |
|
| 88 |
+ |
|
| 89 |
+$ErrorActionPreference = "Stop" |
|
| 90 |
+$pushed=$False # To restore the directory if we have temporarily pushed to one. |
|
| 91 |
+ |
|
| 92 |
+# Utility function to get the commit ID of the repository |
|
| 93 |
+Function Get-GitCommit() {
|
|
| 94 |
+ if (-not (Test-Path ".\.git")) {
|
|
| 95 |
+ # If we don't have a .git directory, but we do have the environment |
|
| 96 |
+ # variable DOCKER_GITCOMMIT set, that can override it. |
|
| 97 |
+ if ($env:DOCKER_GITCOMMIT.Length -eq 0) {
|
|
| 98 |
+ Throw ".git directory missing and DOCKER_GITCOMMIT environment variable not specified." |
|
| 99 |
+ } |
|
| 100 |
+ Write-Host "INFO: Git commit assumed from DOCKER_GITCOMMIT environment variable" |
|
| 101 |
+ return $env:DOCKER_GITCOMMIT |
|
| 102 |
+ } |
|
| 103 |
+ $gitCommit=$(git rev-parse --short HEAD) |
|
| 104 |
+ if ($(git status --porcelain --untracked-files=no).Length -ne 0) {
|
|
| 105 |
+ $gitCommit="$gitCommit-unsupported" |
|
| 106 |
+ Write-Host "" |
|
| 107 |
+ Write-Warning "This version is unsupported because there are uncommitted file(s)." |
|
| 108 |
+ Write-Warning "Either commit these changes, or add them to .gitignore." |
|
| 109 |
+ git status --porcelain --untracked-files=no | Write-Warning |
|
| 110 |
+ Write-Host "" |
|
| 111 |
+ } |
|
| 112 |
+ return $gitCommit |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+# Utility function to get get the current build version of docker |
|
| 116 |
+Function Get-DockerVersion() {
|
|
| 117 |
+ if (-not (Test-Path ".\VERSION")) { Throw "VERSION file not found. Is this running from the root of a docker repository?" }
|
|
| 118 |
+ return $(Get-Content ".\VERSION" -raw).ToString().Replace("`n","").Trim()
|
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+# Utility function to determine if we are running in a container or not. |
|
| 122 |
+# In Windows, we get this through an environment variable set in `Dockerfile.Windows` |
|
| 123 |
+Function Check-InContainer() {
|
|
| 124 |
+ if ($env:FROM_DOCKERFILE.Length -eq 0) {
|
|
| 125 |
+ Write-Host "" |
|
| 126 |
+ Write-Warning "Not running in a container. The result might be an incorrect build." |
|
| 127 |
+ Write-Host "" |
|
| 128 |
+ } |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+# Utility function to get the commit for HEAD |
|
| 132 |
+Function Get-HeadCommit() {
|
|
| 133 |
+ $head = Invoke-Expression "git rev-parse --verify HEAD" |
|
| 134 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed getting HEAD commit" }
|
|
| 135 |
+ |
|
| 136 |
+ return $head |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+# Utility function to get the commit for upstream |
|
| 140 |
+Function Get-UpstreamCommit() {
|
|
| 141 |
+ Invoke-Expression "git fetch -q https://github.com/docker/docker.git refs/heads/master" |
|
| 142 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed fetching" }
|
|
| 143 |
+ |
|
| 144 |
+ $upstream = Invoke-Expression "git rev-parse --verify FETCH_HEAD" |
|
| 145 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed getting upstream commit" }
|
|
| 146 |
+ |
|
| 147 |
+ return $upstream |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+# Build a binary (client or daemon) |
|
| 151 |
+Function Execute-Build($type, $additionalBuildTags, $directory) {
|
|
| 152 |
+ # Generate the build flags |
|
| 153 |
+ $buildTags = "autogen" |
|
| 154 |
+ if ($Noisy) { $verboseParm=" -v" }
|
|
| 155 |
+ if ($Race) { Write-Warning "Using race detector"; $raceParm=" -race"}
|
|
| 156 |
+ if ($ForceBuildAll) { $allParm=" -a" }
|
|
| 157 |
+ if ($NoOpt) { $optParm=" -gcflags "+""""+"-N -l"+"""" }
|
|
| 158 |
+ if ($addtionalBuildTags -ne "") { $buildTags += $(" " + $additionalBuildTags) }
|
|
| 159 |
+ |
|
| 160 |
+ # Do the go build in the appropriate directory |
|
| 161 |
+ # Note -linkmode=internal is required to be able to debug on Windows. |
|
| 162 |
+ # https://github.com/golang/go/issues/14319#issuecomment-189576638 |
|
| 163 |
+ Write-Host "INFO: Building $type..." |
|
| 164 |
+ Push-Location $root\cmd\$directory; $global:pushed=$True |
|
| 165 |
+ $buildCommand = "go build" + ` |
|
| 166 |
+ $raceParm + ` |
|
| 167 |
+ $verboseParm + ` |
|
| 168 |
+ $allParm + ` |
|
| 169 |
+ $optParm + ` |
|
| 170 |
+ " -tags """ + $buildTags + """" + ` |
|
| 171 |
+ " -ldflags """ + "-linkmode=internal" + """" + ` |
|
| 172 |
+ " -o $root\bundles\"+$directory+".exe" |
|
| 173 |
+ Invoke-Expression $buildCommand |
|
| 174 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed to compile $type" }
|
|
| 175 |
+ Pop-Location; $global:pushed=$False |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+# Validates the DCO marker is present on each commit |
|
| 179 |
+Function Validate-DCO($headCommit, $upstreamCommit) {
|
|
| 180 |
+ Write-Host "INFO: Validating Developer Certificate of Origin..." |
|
| 181 |
+ # Username may only contain alphanumeric characters or dashes and cannot begin with a dash |
|
| 182 |
+ $usernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+' |
|
| 183 |
+ |
|
| 184 |
+ $dcoPrefix="Signed-off-by:" |
|
| 185 |
+ $dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \\(github: ($githubUsernameRegex)\\))?$" |
|
| 186 |
+ |
|
| 187 |
+ $counts = Invoke-Expression "git diff --numstat $upstreamCommit...$headCommit" |
|
| 188 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed git diff --numstat" }
|
|
| 189 |
+ |
|
| 190 |
+ # Counts of adds and deletes after removing multiple white spaces. AWK anyone? :( |
|
| 191 |
+ $adds=0; $dels=0; $($counts -replace '\s+', ' ') | %{ $a=$_.Split(" "); $adds+=[int]$a[0]; $dels+=[int]$a[1] }
|
|
| 192 |
+ if (($adds -eq 0) -and ($dels -eq 0)) {
|
|
| 193 |
+ Write-Warning "DCO validation - nothing to validate!" |
|
| 194 |
+ return |
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ $commits = Invoke-Expression "git log $upstreamCommit..$headCommit --format=format:%H%n" |
|
| 198 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed git log --format" }
|
|
| 199 |
+ $commits = $($commits -split '\s+' -match '\S') |
|
| 200 |
+ $badCommits=@() |
|
| 201 |
+ $commits | %{
|
|
| 202 |
+ # Skip commits with no content such as merge commits etc |
|
| 203 |
+ if ($(git log -1 --format=format: --name-status $_).Length -gt 0) {
|
|
| 204 |
+ # Ignore exit code on next call - always process regardless |
|
| 205 |
+ $commitMessage = Invoke-Expression "git log -1 --format=format:%B --name-status $_" |
|
| 206 |
+ if (($commitMessage -match $dcoRegex).Length -eq 0) { $badCommits+=$_ }
|
|
| 207 |
+ } |
|
| 208 |
+ } |
|
| 209 |
+ if ($badCommits.Length -eq 0) {
|
|
| 210 |
+ Write-Host "Congratulations! All commits are properly signed with the DCO!" |
|
| 211 |
+ } else {
|
|
| 212 |
+ $e = "`nThese commits do not have a proper '$dcoPrefix' marker:`n" |
|
| 213 |
+ $badCommits | %{ $e+=" - $_`n"}
|
|
| 214 |
+ $e += "`nPlease amend each commit to include a properly formatted DCO marker.`n`n" |
|
| 215 |
+ $e += "Visit the following URL for information about the Docker DCO:`n" |
|
| 216 |
+ $e += "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work`n" |
|
| 217 |
+ Throw $e |
|
| 218 |
+ } |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+# Validates that .\pkg\... is safely isolated from internal code |
|
| 222 |
+Function Validate-PkgImports($headCommit, $upstreamCommit) {
|
|
| 223 |
+ Write-Host "INFO: Validating pkg import isolation..." |
|
| 224 |
+ |
|
| 225 |
+ # Get a list of go source-code files which have changed under pkg\. Ignore exit code on next call - always process regardless |
|
| 226 |
+ $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'pkg\*.go`'" |
|
| 227 |
+ $badFiles=@(); $files | %{
|
|
| 228 |
+ $file=$_ |
|
| 229 |
+ # For the current changed file, get its list of dependencies, sorted and uniqued. |
|
| 230 |
+ $imports = Invoke-Expression "go list -e -f `'{{ .Deps }}`' $file"
|
|
| 231 |
+ if ($LASTEXITCODE -ne 0) { Throw "Failed go list for dependencies on $file" }
|
|
| 232 |
+ $imports = $imports -Replace "\[" -Replace "\]", "" -Split(" ") | Sort-Object | Get-Unique
|
|
| 233 |
+ # Filter out what we are looking for |
|
| 234 |
+ $imports = $imports -NotMatch "^github.com/docker/docker/pkg/" ` |
|
| 235 |
+ -NotMatch "^github.com/docker/docker/vendor" ` |
|
| 236 |
+ -Match "^github.com/docker/docker" ` |
|
| 237 |
+ -Replace "`n", "" |
|
| 238 |
+ $imports | % { $badFiles+="$file imports $_`n" }
|
|
| 239 |
+ } |
|
| 240 |
+ if ($badFiles.Length -eq 0) {
|
|
| 241 |
+ Write-Host 'Congratulations! ".\pkg\*.go" is safely isolated from internal code.' |
|
| 242 |
+ } else {
|
|
| 243 |
+ $e = "`nThese files import internal code: (either directly or indirectly)`n" |
|
| 244 |
+ $badFiles | %{ $e+=" - $_"}
|
|
| 245 |
+ Throw $e |
|
| 246 |
+ } |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+# Validates that changed files are correctly go-formatted |
|
| 250 |
+Function Validate-GoFormat($headCommit, $upstreamCommit) {
|
|
| 251 |
+ Write-Host "INFO: Validating go formatting on changed files..." |
|
| 252 |
+ |
|
| 253 |
+ # Verify gofmt is installed |
|
| 254 |
+ if ($(Get-Command gofmt -ErrorAction SilentlyContinue) -eq $nil) { Throw "gofmt does not appear to be installed" }
|
|
| 255 |
+ |
|
| 256 |
+ # Get a list of all go source-code files which have changed. Ignore exit code on next call - always process regardless |
|
| 257 |
+ $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'*.go`'" |
|
| 258 |
+ $files = $files | Select-String -NotMatch "^vendor/" |
|
| 259 |
+ $badFiles=@(); $files | %{
|
|
| 260 |
+ # Deliberately ignore error on next line - treat as failed |
|
| 261 |
+ $content=Invoke-Expression "git show $headCommit`:$_" |
|
| 262 |
+ |
|
| 263 |
+ # Next set of hoops are to ensure we have LF not CRLF semantics as otherwise gofmt on Windows will not succeed. |
|
| 264 |
+ # Also note that gofmt on Windows does not appear to support stdin piping correctly. Hence go through a temporary file. |
|
| 265 |
+ $content=$content -join "`n" |
|
| 266 |
+ $content+="`n" |
|
| 267 |
+ $outputFile=[System.IO.Path]::GetTempFileName() |
|
| 268 |
+ if (Test-Path $outputFile) { Remove-Item $outputFile }
|
|
| 269 |
+ [System.IO.File]::WriteAllText($outputFile, $content, (New-Object System.Text.UTF8Encoding($False))) |
|
| 270 |
+ $valid=Invoke-Expression "gofmt -s -l $outputFile" |
|
| 271 |
+ Write-Host "Checking $outputFile" |
|
| 272 |
+ if ($valid.Length -ne 0) { $badFiles+=$_ }
|
|
| 273 |
+ if (Test-Path $outputFile) { Remove-Item $outputFile }
|
|
| 274 |
+ } |
|
| 275 |
+ if ($badFiles.Length -eq 0) {
|
|
| 276 |
+ Write-Host 'Congratulations! All Go source files are properly formatted.' |
|
| 277 |
+ } else {
|
|
| 278 |
+ $e = "`nThese files are not properly gofmt`'d:`n" |
|
| 279 |
+ $badFiles | %{ $e+=" - $_`n"}
|
|
| 280 |
+ $e+= "`nPlease reformat the above files using `"gofmt -s -w`" and commit the result." |
|
| 281 |
+ Throw $e |
|
| 282 |
+ } |
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+# Run the unit tests |
|
| 286 |
+Function Run-UnitTests() {
|
|
| 287 |
+ Write-Host "INFO: Running unit tests..." |
|
| 288 |
+ $testPath="./..." |
|
| 289 |
+ $goListCommand = "go list -e -f '{{if ne .Name """ + '\"github.com/docker/docker\"' + """}}{{.ImportPath}}{{end}}' $testPath"
|
|
| 290 |
+ $pkgList = $(Invoke-Expression $goListCommand) |
|
| 291 |
+ if ($LASTEXITCODE -ne 0) { Throw "go list for unit tests failed" }
|
|
| 292 |
+ $pkgList = $pkgList | Select-String -Pattern "github.com/docker/docker" |
|
| 293 |
+ $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/vendor" |
|
| 294 |
+ $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man" |
|
| 295 |
+ $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration-cli" |
|
| 296 |
+ $pkgList = $pkgList -replace "`r`n", " " |
|
| 297 |
+ $goTestCommand = "go test" + $raceParm + " -cover -ldflags -w -tags """ + "autogen daemon" + """ -a """ + "-test.timeout=10m" + """ $pkgList" |
|
| 298 |
+ Invoke-Expression $goTestCommand |
|
| 299 |
+ if ($LASTEXITCODE -ne 0) { Throw "Unit tests failed" }
|
|
| 300 |
+} |
|
| 301 |
+ |
|
| 302 |
+# Start of main code. |
|
| 303 |
+Try {
|
|
| 304 |
+ Write-Host -ForegroundColor Cyan "INFO: make.ps1 starting at $(Get-Date)" |
|
| 305 |
+ $root=$(pwd) |
|
| 306 |
+ |
|
| 307 |
+ # Handle the "-All" shortcut to turn on all things we can handle. |
|
| 308 |
+ if ($All) { $Client=$True; $Daemon=$True; $DCO=$True; $PkgImports=$True; $GoFormat=$True; $TestUnit=$True }
|
|
| 309 |
+ |
|
| 310 |
+ # Handle the "-Binary" shortcut to build both client and daemon. |
|
| 311 |
+ if ($Binary) { $Client = $True; $Daemon = $True }
|
|
| 312 |
+ |
|
| 313 |
+ # Make sure we have something to do |
|
| 314 |
+ if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit)) { Throw 'Nothing to do. Try adding "-All" for everything I can do' }
|
|
| 315 |
+ |
|
| 316 |
+ # Verify git is installed |
|
| 317 |
+ if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" }
|
|
| 318 |
+ |
|
| 319 |
+ # Verify go is installed |
|
| 320 |
+ if ($(Get-Command go -ErrorAction SilentlyContinue) -eq $nil) { Throw "GoLang does not appear to be installed" }
|
|
| 321 |
+ |
|
| 322 |
+ # Get the git commit. This will also verify if we are in a repo or not. Then add a custom string if supplied. |
|
| 323 |
+ $gitCommit=Get-GitCommit |
|
| 324 |
+ if ($CommitSuffix -ne "") { $gitCommit += "-"+$CommitSuffix -Replace ' ', '' }
|
|
| 325 |
+ |
|
| 326 |
+ # Get the version of docker (eg 1.14.0-dev) |
|
| 327 |
+ $dockerVersion=Get-DockerVersion |
|
| 328 |
+ |
|
| 329 |
+ # Give a warning if we are not running in a container and are building binaries or running unit tests. |
|
| 330 |
+ # Not relevant for validation tests as these are fine to run outside of a container. |
|
| 331 |
+ if ($Client -or $Daemon -or $TestUnit) { Check-InContainer }
|
|
| 332 |
+ |
|
| 333 |
+ # Verify GOPATH is set |
|
| 334 |
+ if ($env:GOPATH.Length -eq 0) { Throw "Missing GOPATH environment variable. See https://golang.org/doc/code.html#GOPATH" }
|
|
| 335 |
+ |
|
| 336 |
+ # Run autogen if building binaries or running unit tests. |
|
| 337 |
+ if ($Client -or $Daemon -or $TestUnit) {
|
|
| 338 |
+ Write-Host "INFO: Invoking autogen..." |
|
| 339 |
+ Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion }
|
|
| 340 |
+ Catch [Exception] { Throw $_ }
|
|
| 341 |
+ } |
|
| 342 |
+ |
|
| 343 |
+ # DCO, Package import and Go formatting tests. |
|
| 344 |
+ if ($DCO -or $PkgImports -or $GoFormat) {
|
|
| 345 |
+ # We need the head and upstream commits for these |
|
| 346 |
+ $headCommit=Get-HeadCommit |
|
| 347 |
+ $upstreamCommit=Get-UpstreamCommit |
|
| 348 |
+ |
|
| 349 |
+ # Run DCO validation |
|
| 350 |
+ if ($DCO) { Validate-DCO $headCommit $upstreamCommit }
|
|
| 351 |
+ |
|
| 352 |
+ # Run `gofmt` validation |
|
| 353 |
+ if ($GoFormat) { Validate-GoFormat $headCommit $upstreamCommit }
|
|
| 354 |
+ |
|
| 355 |
+ # Run pkg isolation validation |
|
| 356 |
+ if ($PkgImports) { Validate-PkgImports $headCommit $upstreamCommit }
|
|
| 357 |
+ } |
|
| 358 |
+ |
|
| 359 |
+ # Build the binaries |
|
| 360 |
+ if ($Client -or $Daemon) {
|
|
| 361 |
+ # Create the bundles directory if it doesn't exist |
|
| 362 |
+ if (-not (Test-Path ".\bundles")) { New-Item ".\bundles" -ItemType Directory | Out-Null }
|
|
| 363 |
+ |
|
| 364 |
+ # Perform the actual build |
|
| 365 |
+ if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" }
|
|
| 366 |
+ if ($Client) { Execute-Build "client" "" "docker" }
|
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ # Run unit tests |
|
| 370 |
+ if ($TestUnit) { Run-UnitTests }
|
|
| 371 |
+ |
|
| 372 |
+ # Gratuitous ASCII art. |
|
| 373 |
+ if ($Daemon -or $Client) {
|
|
| 374 |
+ Write-Host |
|
| 375 |
+ Write-Host -ForegroundColor Green " ________ ____ __." |
|
| 376 |
+ Write-Host -ForegroundColor Green " \_____ \ `| `|/ _`|" |
|
| 377 |
+ Write-Host -ForegroundColor Green " / `| \`| `<" |
|
| 378 |
+ Write-Host -ForegroundColor Green " / `| \ `| \" |
|
| 379 |
+ Write-Host -ForegroundColor Green " \_______ /____`|__ \" |
|
| 380 |
+ Write-Host -ForegroundColor Green " \/ \/" |
|
| 381 |
+ Write-Host |
|
| 382 |
+ } |
|
| 383 |
+} |
|
| 384 |
+Catch [Exception] {
|
|
| 385 |
+ Write-Host -ForegroundColor Red ("`nERROR: make.ps1 failed:`n$_")
|
|
| 386 |
+ |
|
| 387 |
+ # More gratuitous ASCII art. |
|
| 388 |
+ Write-Host |
|
| 389 |
+ Write-Host -ForegroundColor Red "___________ .__.__ .___" |
|
| 390 |
+ Write-Host -ForegroundColor Red "\_ _____/____ `|__`| `| ____ __`| _/" |
|
| 391 |
+ Write-Host -ForegroundColor Red " `| __) \__ \ `| `| `| _/ __ \ / __ `| " |
|
| 392 |
+ Write-Host -ForegroundColor Red " `| \ / __ \`| `| `|_\ ___// /_/ `| " |
|
| 393 |
+ Write-Host -ForegroundColor Red " \___ / (____ /__`|____/\___ `>____ `| " |
|
| 394 |
+ Write-Host -ForegroundColor Red " \/ \/ \/ \/ " |
|
| 395 |
+ Write-Host |
|
| 396 |
+} |
|
| 397 |
+Finally {
|
|
| 398 |
+ if ($global:pushed) { Pop-Location }
|
|
| 399 |
+ Write-Host -ForegroundColor Cyan "INFO: make.ps1 ended at $(Get-Date)" |
|
| 400 |
+} |
| ... | ... |
@@ -10,16 +10,11 @@ |
| 10 | 10 |
|
| 11 | 11 |
.PARAMETER DockerVersion |
| 12 | 12 |
The version such as 1.14.0-dev. This is calculated externally to this script. |
| 13 |
- |
|
| 14 |
-.PARAMETER StaticSQLite |
|
| 15 |
- A string indicating if the daemon binary is compiled with the SQLite |
|
| 16 |
- sources compiled in. This is calculated externally to this script. |
|
| 17 | 13 |
#> |
| 18 | 14 |
|
| 19 | 15 |
param( |
| 20 | 16 |
[Parameter(Mandatory=$true)][string]$CommitString, |
| 21 |
- [Parameter(Mandatory=$true)][string]$DockerVersion, |
|
| 22 |
- [Parameter(Mandatory=$true)][string]$StaticSQLiteString |
|
| 17 |
+ [Parameter(Mandatory=$true)][string]$DockerVersion |
|
| 23 | 18 |
) |
| 24 | 19 |
|
| 25 | 20 |
$ErrorActionPreference = "Stop" |
| ... | ... |
@@ -48,7 +43,6 @@ const ( |
| 48 | 48 |
GitCommit string = "'+$CommitString+'" |
| 49 | 49 |
Version string = "'+$DockerVersion+'" |
| 50 | 50 |
BuildTime string = "'+$buildDateTime+'" |
| 51 |
- StaticSQLite string = "'+$StaticSQLiteString+'" |
|
| 52 | 51 |
) |
| 53 | 52 |
|
| 54 | 53 |
// AUTOGENERATED FILE; see hack\make\.go-autogen.ps1 |
| ... | ... |
@@ -3610,8 +3610,8 @@ RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/do |
| 3610 | 3610 |
|
| 3611 | 3611 |
# Switch back to root and double check that worked exactly as we might expect it to |
| 3612 | 3612 |
USER root |
| 3613 |
-# Add a "supplementary" group for our dockerio user |
|
| 3614 | 3613 |
RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0 10:root wheel' ] && \ |
| 3614 |
+ # Add a "supplementary" group for our dockerio user |
|
| 3615 | 3615 |
echo 'supplementary:x:1002:dockerio' >> /etc/group |
| 3616 | 3616 |
|
| 3617 | 3617 |
# ... and then go verify that we get it like we expect |
| ... | ... |
@@ -7141,41 +7141,6 @@ func (s *DockerSuite) TestBuildNetContainer(c *check.C) {
|
| 7141 | 7141 |
c.Assert(strings.TrimSpace(host), check.Equals, "foobar") |
| 7142 | 7142 |
} |
| 7143 | 7143 |
|
| 7144 |
-// Test case for #24693 |
|
| 7145 |
-func (s *DockerSuite) TestBuildRunEmptyLineAfterEscape(c *check.C) {
|
|
| 7146 |
- name := "testbuildemptylineafterescape" |
|
| 7147 |
- _, out, err := buildImageWithOut(name, |
|
| 7148 |
- ` |
|
| 7149 |
-FROM busybox |
|
| 7150 |
-RUN echo x \ |
|
| 7151 |
- |
|
| 7152 |
-RUN echo y |
|
| 7153 |
-RUN echo z |
|
| 7154 |
-# Comment requires the '#' to start from position 1 |
|
| 7155 |
-# RUN echo w |
|
| 7156 |
-`, true) |
|
| 7157 |
- c.Assert(err, checker.IsNil) |
|
| 7158 |
- c.Assert(out, checker.Contains, "Step 1/4 : FROM busybox") |
|
| 7159 |
- c.Assert(out, checker.Contains, "Step 2/4 : RUN echo x") |
|
| 7160 |
- c.Assert(out, checker.Contains, "Step 3/4 : RUN echo y") |
|
| 7161 |
- c.Assert(out, checker.Contains, "Step 4/4 : RUN echo z") |
|
| 7162 |
- |
|
| 7163 |
- // With comment, see #24693 |
|
| 7164 |
- name = "testbuildcommentandemptylineafterescape" |
|
| 7165 |
- _, out, err = buildImageWithOut(name, |
|
| 7166 |
- ` |
|
| 7167 |
-FROM busybox |
|
| 7168 |
-RUN echo grafana && \ |
|
| 7169 |
- echo raintank \ |
|
| 7170 |
-#echo env-load |
|
| 7171 |
-RUN echo vegeta |
|
| 7172 |
-`, true) |
|
| 7173 |
- c.Assert(err, checker.IsNil) |
|
| 7174 |
- c.Assert(out, checker.Contains, "Step 1/3 : FROM busybox") |
|
| 7175 |
- c.Assert(out, checker.Contains, "Step 2/3 : RUN echo grafana && echo raintank") |
|
| 7176 |
- c.Assert(out, checker.Contains, "Step 3/3 : RUN echo vegeta") |
|
| 7177 |
-} |
|
| 7178 |
- |
|
| 7179 | 7144 |
func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
|
| 7180 | 7145 |
testRequires(c, ExperimentalDaemon) |
| 7181 | 7146 |
dockerFile := ` |
| ... | ... |
@@ -568,8 +568,36 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverOutOfBandDelete(c *c |
| 568 | 568 |
// simulate out of band volume deletion on plugin level |
| 569 | 569 |
delete(p.vols, "test") |
| 570 | 570 |
|
| 571 |
+ // test re-create with same driver |
|
| 572 |
+ out, err = s.d.Cmd("volume", "create", "-d", driverName, "--opt", "foo=bar", "--name", "test")
|
|
| 573 |
+ c.Assert(err, checker.IsNil, check.Commentf(out)) |
|
| 574 |
+ out, err = s.d.Cmd("volume", "inspect", "test")
|
|
| 575 |
+ c.Assert(err, checker.IsNil, check.Commentf(out)) |
|
| 576 |
+ |
|
| 577 |
+ var vs []types.Volume |
|
| 578 |
+ err = json.Unmarshal([]byte(out), &vs) |
|
| 579 |
+ c.Assert(err, checker.IsNil) |
|
| 580 |
+ c.Assert(vs, checker.HasLen, 1) |
|
| 581 |
+ c.Assert(vs[0].Driver, checker.Equals, driverName) |
|
| 582 |
+ c.Assert(vs[0].Options, checker.NotNil) |
|
| 583 |
+ c.Assert(vs[0].Options["foo"], checker.Equals, "bar") |
|
| 584 |
+ c.Assert(vs[0].Driver, checker.Equals, driverName) |
|
| 585 |
+ |
|
| 586 |
+ // simulate out of band volume deletion on plugin level |
|
| 587 |
+ delete(p.vols, "test") |
|
| 588 |
+ |
|
| 589 |
+ // test create with different driver |
|
| 571 | 590 |
out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
|
| 572 | 591 |
c.Assert(err, checker.IsNil, check.Commentf(out)) |
| 592 |
+ |
|
| 593 |
+ out, err = s.d.Cmd("volume", "inspect", "test")
|
|
| 594 |
+ c.Assert(err, checker.IsNil, check.Commentf(out)) |
|
| 595 |
+ vs = nil |
|
| 596 |
+ err = json.Unmarshal([]byte(out), &vs) |
|
| 597 |
+ c.Assert(err, checker.IsNil) |
|
| 598 |
+ c.Assert(vs, checker.HasLen, 1) |
|
| 599 |
+ c.Assert(vs[0].Options, checker.HasLen, 0) |
|
| 600 |
+ c.Assert(vs[0].Driver, checker.Equals, "local") |
|
| 573 | 601 |
} |
| 574 | 602 |
|
| 575 | 603 |
func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *check.C) {
|
| ... | ... |
@@ -417,3 +417,33 @@ func (s *DockerSuite) TestInspectAmpersand(c *check.C) {
|
| 417 | 417 |
out, _ = dockerCmd(c, "inspect", name) |
| 418 | 418 |
c.Assert(out, checker.Contains, `soanni&rtr`) |
| 419 | 419 |
} |
| 420 |
+ |
|
| 421 |
+func (s *DockerSuite) TestInspectPlugin(c *check.C) {
|
|
| 422 |
+ testRequires(c, DaemonIsLinux, Network) |
|
| 423 |
+ _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
|
|
| 424 |
+ c.Assert(err, checker.IsNil) |
|
| 425 |
+ |
|
| 426 |
+ out, _, err := dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
|
|
| 427 |
+ c.Assert(err, checker.IsNil) |
|
| 428 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 429 |
+ |
|
| 430 |
+ out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
|
|
| 431 |
+ c.Assert(err, checker.IsNil) |
|
| 432 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 433 |
+ |
|
| 434 |
+ // Even without tag the inspect still work |
|
| 435 |
+ out, _, err = dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pName)
|
|
| 436 |
+ c.Assert(err, checker.IsNil) |
|
| 437 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 438 |
+ |
|
| 439 |
+ out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pName)
|
|
| 440 |
+ c.Assert(err, checker.IsNil) |
|
| 441 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 442 |
+ |
|
| 443 |
+ _, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
|
|
| 444 |
+ c.Assert(err, checker.IsNil) |
|
| 445 |
+ |
|
| 446 |
+ out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
|
|
| 447 |
+ c.Assert(err, checker.IsNil) |
|
| 448 |
+ c.Assert(out, checker.Contains, pNameWithTag) |
|
| 449 |
+} |
| ... | ... |
@@ -62,10 +62,10 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
|
| 62 | 62 |
name := inspectField(c, newName, "Name") |
| 63 | 63 |
c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
|
| 64 | 64 |
|
| 65 |
- result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
|
|
| 65 |
+ result := dockerCmdWithResult("inspect", "-f={{.Name}}", "--type=container", "first_name")
|
|
| 66 | 66 |
c.Assert(result, icmd.Matches, icmd.Expected{
|
| 67 | 67 |
ExitCode: 1, |
| 68 |
- Err: "No such object: first_name", |
|
| 68 |
+ Err: "No such container: first_name", |
|
| 69 | 69 |
}) |
| 70 | 70 |
} |
| 71 | 71 |
|
| ... | ... |
@@ -4372,42 +4372,6 @@ func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) {
|
| 4372 | 4372 |
c.Assert(err, checker.NotNil, check.Commentf(out)) |
| 4373 | 4373 |
} |
| 4374 | 4374 |
|
| 4375 |
-func (s *DockerSuite) TestRunTooLongHostname(c *check.C) {
|
|
| 4376 |
- // Test case in #21445 |
|
| 4377 |
- hostname1 := "this-is-a-way-too-long-hostname-but-it-should-give-a-nice-error.local" |
|
| 4378 |
- out, _, err := dockerCmdWithError("run", "--hostname", hostname1, "busybox", "echo", "test")
|
|
| 4379 |
- c.Assert(err, checker.NotNil, check.Commentf("Expected docker run to fail!"))
|
|
| 4380 |
- c.Assert(out, checker.Contains, "invalid hostname format:", check.Commentf("Expected to have 'invalid hostname format:' in the output, get: %s!", out))
|
|
| 4381 |
- |
|
| 4382 |
- // Additional test cases |
|
| 4383 |
- validHostnames := map[string]string{
|
|
| 4384 |
- "hostname": "hostname", |
|
| 4385 |
- "host-name": "host-name", |
|
| 4386 |
- "hostname123": "hostname123", |
|
| 4387 |
- "123hostname": "123hostname", |
|
| 4388 |
- "hostname-of-63-bytes-long-should-be-valid-and-without-any-error": "hostname-of-63-bytes-long-should-be-valid-and-without-any-error", |
|
| 4389 |
- } |
|
| 4390 |
- for hostname := range validHostnames {
|
|
| 4391 |
- dockerCmd(c, "run", "--hostname", hostname, "busybox", "echo", "test") |
|
| 4392 |
- } |
|
| 4393 |
- |
|
| 4394 |
- invalidHostnames := map[string]string{
|
|
| 4395 |
- "^hostname": "invalid hostname format: ^hostname", |
|
| 4396 |
- "hostname%": "invalid hostname format: hostname%", |
|
| 4397 |
- "host&name": "invalid hostname format: host&name", |
|
| 4398 |
- "-hostname": "invalid hostname format: -hostname", |
|
| 4399 |
- "host_name": "invalid hostname format: host_name", |
|
| 4400 |
- "hostname-of-64-bytes-long-should-be-invalid-and-be-with-an-error": "invalid hostname format: hostname-of-64-bytes-long-should-be-invalid-and-be-with-an-error", |
|
| 4401 |
- } |
|
| 4402 |
- |
|
| 4403 |
- for hostname, expectedError := range invalidHostnames {
|
|
| 4404 |
- out, _, err = dockerCmdWithError("run", "--hostname", hostname, "busybox", "echo", "test")
|
|
| 4405 |
- c.Assert(err, checker.NotNil, check.Commentf("Expected docker run to fail!"))
|
|
| 4406 |
- c.Assert(out, checker.Contains, expectedError, check.Commentf("Expected to have '%s' in the output, get: %s!", expectedError, out))
|
|
| 4407 |
- |
|
| 4408 |
- } |
|
| 4409 |
-} |
|
| 4410 |
- |
|
| 4411 | 4375 |
// Test case for #21976 |
| 4412 | 4376 |
func (s *DockerSuite) TestRunDNSInHostMode(c *check.C) {
|
| 4413 | 4377 |
testRequires(c, DaemonIsLinux, NotUserNamespace) |
| ... | ... |
@@ -84,7 +84,7 @@ func (pm *Manager) Inspect(refOrID string) (tp types.Plugin, err error) {
|
| 84 | 84 |
return tp, err |
| 85 | 85 |
} |
| 86 | 86 |
|
| 87 |
- return tp, fmt.Errorf("no plugin name or ID associated with %q", refOrID)
|
|
| 87 |
+ return tp, fmt.Errorf("no such plugin name or ID associated with %q", refOrID)
|
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
func (pm *Manager) pull(ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig, pluginID string) (types.PluginPrivileges, error) {
|
| ... | ... |
@@ -284,56 +284,64 @@ func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]st |
| 284 | 284 |
func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, error) {
|
| 285 | 285 |
// check the local cache |
| 286 | 286 |
v, _ := s.getNamed(name) |
| 287 |
- if v != nil {
|
|
| 288 |
- vDriverName := v.DriverName() |
|
| 289 |
- if driverName != "" && vDriverName != driverName {
|
|
| 290 |
- // we have what looks like a conflict |
|
| 291 |
- // let's see if there are existing refs to this volume, if so we don't need |
|
| 292 |
- // to go any further since we can assume the volume is legit. |
|
| 293 |
- if len(s.getRefs(name)) > 0 {
|
|
| 294 |
- return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name) |
|
| 295 |
- } |
|
| 287 |
+ if v == nil {
|
|
| 288 |
+ return nil, nil |
|
| 289 |
+ } |
|
| 296 | 290 |
|
| 297 |
- // looks like there is a conflict, but nothing is referencing it... |
|
| 298 |
- // let's check if the found volume ref |
|
| 299 |
- // is stale by checking with the driver if it still exists |
|
| 300 |
- vd, err := volumedrivers.GetDriver(vDriverName) |
|
| 301 |
- if err != nil {
|
|
| 302 |
- // play it safe and return the error |
|
| 303 |
- // TODO(cpuguy83): maybe when when v2 plugins are ubiquitous, we should |
|
| 304 |
- // just purge this from the cache |
|
| 305 |
- return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err) |
|
| 306 |
- } |
|
| 291 |
+ vDriverName := v.DriverName() |
|
| 292 |
+ var conflict bool |
|
| 293 |
+ if driverName != "" && vDriverName != driverName {
|
|
| 294 |
+ conflict = true |
|
| 295 |
+ } |
|
| 307 | 296 |
|
| 308 |
- // now check if it still exists in the driver |
|
| 309 |
- v2, err := vd.Get(name) |
|
| 310 |
- err = errors.Cause(err) |
|
| 311 |
- if err != nil {
|
|
| 312 |
- if _, ok := err.(net.Error); ok {
|
|
| 313 |
- // got some error related to the driver connectivity |
|
| 314 |
- // play it safe and return the error |
|
| 315 |
- // TODO(cpuguy83): When when v2 plugins are ubiquitous, maybe we should |
|
| 316 |
- // just purge this from the cache |
|
| 317 |
- return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err) |
|
| 318 |
- } |
|
| 319 |
- |
|
| 320 |
- // a driver can return whatever it wants, so let's make sure this is nil |
|
| 321 |
- if v2 == nil {
|
|
| 322 |
- // purge this reference from the cache |
|
| 323 |
- s.Purge(name) |
|
| 324 |
- return nil, nil |
|
| 325 |
- } |
|
| 326 |
- } |
|
| 327 |
- if v2 != nil {
|
|
| 328 |
- return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name) |
|
| 329 |
- } |
|
| 297 |
+ // let's check if the found volume ref |
|
| 298 |
+ // is stale by checking with the driver if it still exists |
|
| 299 |
+ exists, err := volumeExists(v) |
|
| 300 |
+ if err != nil {
|
|
| 301 |
+ return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err) |
|
| 302 |
+ } |
|
| 303 |
+ |
|
| 304 |
+ if exists {
|
|
| 305 |
+ if conflict {
|
|
| 306 |
+ return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name) |
|
| 330 | 307 |
} |
| 331 | 308 |
return v, nil |
| 332 | 309 |
} |
| 333 | 310 |
|
| 311 |
+ if len(s.getRefs(v.Name())) > 0 {
|
|
| 312 |
+ // Containers are referencing this volume but it doesn't seem to exist anywhere. |
|
| 313 |
+ // Return a conflict error here, the user can fix this with `docker volume rm -f` |
|
| 314 |
+ return nil, errors.Wrapf(errNameConflict, "found references to volume '%s' in driver '%s' but the volume was not found in the driver -- you may need to remove containers referencing this volume or force remove the volume to re-create it", name, vDriverName) |
|
| 315 |
+ } |
|
| 316 |
+ |
|
| 317 |
+ // doesn't exist, so purge it from the cache |
|
| 318 |
+ s.Purge(name) |
|
| 334 | 319 |
return nil, nil |
| 335 | 320 |
} |
| 336 | 321 |
|
| 322 |
+// volumeExists returns if the volume is still present in the driver. |
|
| 323 |
+// An error is returned if there was an issue communicating with the driver. |
|
| 324 |
+func volumeExists(v volume.Volume) (bool, error) {
|
|
| 325 |
+ vd, err := volumedrivers.GetDriver(v.DriverName()) |
|
| 326 |
+ if err != nil {
|
|
| 327 |
+ return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName()) |
|
| 328 |
+ } |
|
| 329 |
+ exists, err := vd.Get(v.Name()) |
|
| 330 |
+ if err != nil {
|
|
| 331 |
+ err = errors.Cause(err) |
|
| 332 |
+ if _, ok := err.(net.Error); ok {
|
|
| 333 |
+ return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName()) |
|
| 334 |
+ } |
|
| 335 |
+ |
|
| 336 |
+ // At this point, the error could be anything from the driver, such as "no such volume" |
|
| 337 |
+ // Let's not check an error here, and instead check if the driver returned a volume |
|
| 338 |
+ } |
|
| 339 |
+ if exists == nil {
|
|
| 340 |
+ return false, nil |
|
| 341 |
+ } |
|
| 342 |
+ return true, nil |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 337 | 345 |
// create asks the given driver to create a volume with the name/opts. |
| 338 | 346 |
// If a volume with the name is already known, it will ask the stored driver for the volume. |
| 339 | 347 |
// If the passed in driver name does not match the driver name which is stored |
| ... | ... |
@@ -354,6 +362,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st |
| 354 | 354 |
if err != nil {
|
| 355 | 355 |
return nil, err |
| 356 | 356 |
} |
| 357 |
+ |
|
| 357 | 358 |
if v != nil {
|
| 358 | 359 |
return v, nil |
| 359 | 360 |
} |