Browse code

Merge pull request #29094 from vieux/1.13.0-rc3-cherrypicks

1.13.0-rc3 cherry-picks: part3

Victor Vieux authored on 2016/12/05 19:29:24
Showing 39 changed files
... ...
@@ -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.* \
... ...
@@ -27,5 +27,8 @@ bye\
27 27
 frog
28 28
 
29 29
 RUN echo hello \
30
-# this is a comment that breaks escape continuation
31
-RUN echo this is some more useful stuff
30
+# this is a comment
31
+
32
+# this is a comment with a blank line surrounding it
33
+
34
+this is some more useful stuff
... ...
@@ -6,5 +6,4 @@
6 6
 (run "echo hi   world  goodnight")
7 7
 (run "echo goodbyefrog")
8 8
 (run "echo goodbyefrog")
9
-(run "echo hello")
10
-(run "echo this is some more useful stuff")
9
+(run "echo hello this is some more useful stuff")
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
16 1
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-(from "busybox")
2
-(run "foo")
3
-(bar "")
... ...
@@ -6,7 +6,9 @@ RUN apt-get \update && \
6 6
 ADD \conf\\" /.znc
7 7
 
8 8
 RUN foo \
9
+
9 10
 bar \
11
+
10 12
 baz
11 13
 
12 14
 CMD [ "\/usr\\\"/bin/znc", "-f", "-r" ]
... ...
@@ -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
 	}