39
39
// DockerClient is an interface to the Docker client that contains
40
40
// the methods used by the common builder
41
41
type DockerClient interface {
42
+ AttachToContainerNonBlocking (opts docker.AttachToContainerOptions ) (docker.CloseWaiter , error )
42
43
BuildImage (opts docker.BuildImageOptions ) error
43
44
PushImage (opts docker.PushImageOptions , auth docker.AuthConfiguration ) error
44
45
RemoveImage (name string ) error
@@ -169,7 +170,7 @@ func tagImage(dockerClient DockerClient, image, name string) error {
169
170
// dockerRun mimics the 'docker run --rm' CLI command. It uses the Docker Remote
170
171
// API to create and start a container and stream its logs. The container is
171
172
// removed after it terminates.
172
- func dockerRun (client DockerClient , createOpts docker.CreateContainerOptions , logsOpts docker.LogsOptions ) error {
173
+ func dockerRun (client DockerClient , createOpts docker.CreateContainerOptions , attachOpts docker.AttachToContainerOptions ) error {
173
174
// Create a new container.
174
175
glog .V (4 ).Infof ("Creating container with options {Name:%q Config:%+v HostConfig:%+v} ..." , createOpts .Name , createOpts .Config , createOpts .HostConfig )
175
176
c , err := client .CreateContainer (createOpts )
@@ -188,17 +189,32 @@ func dockerRun(client DockerClient, createOpts docker.CreateContainerOptions, lo
188
189
}
189
190
}
190
191
startWaitContainer := func () error {
192
+ // Changed to use attach call instead of logs call to stream stdout/stderr
193
+ // during execution to avoid race condition
194
+ // https://github.com/docker/docker/issues/31323 .
195
+ // Using attach call is also racy in docker versions which don't carry
196
+ // https://github.com/docker/docker/pull/30446 .
197
+ // In RHEL, docker >= 1.12.6-10.el7.x86_64 should be OK.
198
+
199
+ // Attach to the container.
200
+ attachOpts .Container = c .ID
201
+ glog .V (4 ).Infof ("Attaching to container %q with options %+v ..." , containerName , attachOpts )
202
+ wc , err := client .AttachToContainerNonBlocking (attachOpts )
203
+ if err != nil {
204
+ return fmt .Errorf ("attach container %q: %v" , containerName , err )
205
+ }
206
+ defer wc .Close ()
207
+
191
208
// Start the container.
192
209
glog .V (4 ).Infof ("Starting container %q ..." , containerName )
193
210
if err := client .StartContainer (c .ID , nil ); err != nil {
194
211
return fmt .Errorf ("start container %q: %v" , containerName , err )
195
212
}
196
213
197
- // Stream container logs.
198
- logsOpts .Container = c .ID
199
- glog .V (4 ).Infof ("Streaming logs of container %q with options %+v ..." , containerName , logsOpts )
200
- if err := client .Logs (logsOpts ); err != nil {
201
- return fmt .Errorf ("streaming logs of %q: %v" , containerName , err )
214
+ // Wait for streaming to finish.
215
+ glog .V (4 ).Infof ("Waiting for streaming to finish ..." )
216
+ if err := wc .Wait (); err != nil {
217
+ return fmt .Errorf ("container %q streaming: %v" , containerName , err )
202
218
}
203
219
204
220
// Return an error if the exit code of the container is non-zero.
0 commit comments