OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package main | 5 package main |
6 | 6 |
7 import ( | 7 import ( |
8 "bufio" | 8 "bufio" |
9 "bytes" | 9 "bytes" |
10 "fmt" | 10 "fmt" |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
120 case x.shouldForwardOutput: | 120 case x.shouldForwardOutput: |
121 cmd.Stdout = &teeWriter{os.Stdout, &x.stdout} | 121 cmd.Stdout = &teeWriter{os.Stdout, &x.stdout} |
122 cmd.Stderr = &teeWriter{os.Stderr, &x.stderr} | 122 cmd.Stderr = &teeWriter{os.Stderr, &x.stderr} |
123 | 123 |
124 case log.IsLogging(c, x.outputLevel): | 124 case log.IsLogging(c, x.outputLevel): |
125 // Get our command pipes. Wrap each one in a "closeOnceReader" s
o that our | 125 // Get our command pipes. Wrap each one in a "closeOnceReader" s
o that our |
126 // reader goroutine can close on error and our outer loop can al
so close on | 126 // reader goroutine can close on error and our outer loop can al
so close on |
127 // error without conflicting. | 127 // error without conflicting. |
128 stdoutPipe, err := cmd.StdoutPipe() | 128 stdoutPipe, err := cmd.StdoutPipe() |
129 if err != nil { | 129 if err != nil { |
130 » » » return -1, errors.Annotate(err).Reason("failed to create
STDOUT pipe").Err() | 130 » » » return -1, errors.Annotate(err, "failed to create STDOUT
pipe").Err() |
131 } | 131 } |
132 stdoutPipe = &closeOnceReader{ReadCloser: stdoutPipe} | 132 stdoutPipe = &closeOnceReader{ReadCloser: stdoutPipe} |
133 defer stdoutPipe.Close() | 133 defer stdoutPipe.Close() |
134 | 134 |
135 stderrPipe, err := cmd.StderrPipe() | 135 stderrPipe, err := cmd.StderrPipe() |
136 if err != nil { | 136 if err != nil { |
137 » » » return -1, errors.Annotate(err).Reason("failed to create
STDERR pipe").Err() | 137 » » » return -1, errors.Annotate(err, "failed to create STDERR
pipe").Err() |
138 } | 138 } |
139 stderrPipe = &closeOnceReader{ReadCloser: stderrPipe} | 139 stderrPipe = &closeOnceReader{ReadCloser: stderrPipe} |
140 defer stderrPipe.Close() | 140 defer stderrPipe.Close() |
141 | 141 |
142 spawnMonitor := func(name string, in io.ReadCloser, tee io.Write
r) { | 142 spawnMonitor := func(name string, in io.ReadCloser, tee io.Write
r) { |
143 wg.Add(1) | 143 wg.Add(1) |
144 go func() { | 144 go func() { |
145 defer wg.Done() | 145 defer wg.Done() |
146 defer in.Close() | 146 defer in.Close() |
147 | 147 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 for i, k := range env { | 200 for i, k := range env { |
201 env[i] = fmt.Sprintf("%s=%s", k, x.envMap[k]) | 201 env[i] = fmt.Sprintf("%s=%s", k, x.envMap[k]) |
202 } | 202 } |
203 cmd.Env = env | 203 cmd.Env = env |
204 } | 204 } |
205 | 205 |
206 log.Fields{ | 206 log.Fields{ |
207 "cwd": x.workdir, | 207 "cwd": x.workdir, |
208 }.Debugf(c, "Running command: %s %s.", x.command, x.args) | 208 }.Debugf(c, "Running command: %s %s.", x.command, x.args) |
209 if err := cmd.Start(); err != nil { | 209 if err := cmd.Start(); err != nil { |
210 » » return -1, errors.Annotate(err).Reason("failed to start command"
). | 210 » » return -1, errors.Annotate(err, "failed to start command"). |
211 » » » D("command", x.command).D("args", x.args).D("cwd", x.wor
kdir).Err() | 211 » » » InternalReason("command(%s)/args(%v)/cwd(%s)", x.command
, x.args, x.workdir).Err() |
212 } | 212 } |
213 | 213 |
214 // Wait for our stream processing to finish. | 214 // Wait for our stream processing to finish. |
215 wg.Wait() | 215 wg.Wait() |
216 | 216 |
217 if err := cmd.Wait(); err != nil { | 217 if err := cmd.Wait(); err != nil { |
218 if rc, ok := exitcode.Get(err); ok { | 218 if rc, ok := exitcode.Get(err); ok { |
219 log.Fields{ | 219 log.Fields{ |
220 "returnCode": rc, | 220 "returnCode": rc, |
221 }.Debugf(c, "Command completed with non-zero return code
: %s %s", x.command, x.args) | 221 }.Debugf(c, "Command completed with non-zero return code
: %s %s", x.command, x.args) |
222 return rc, nil | 222 return rc, nil |
223 } | 223 } |
224 | 224 |
225 » » return -1, errors.Annotate(err).Reason("failed to wait for comma
nd"). | 225 » » return -1, errors.Annotate(err, "failed to wait for command"). |
226 » » » D("command", x.command).D("args", x.args).D("cwd", x.wor
kdir).Err() | 226 » » » InternalReason("command(%s)/args(%v)/cwd(%s)", x.command
, x.args, x.workdir).Err() |
227 } | 227 } |
228 | 228 |
229 log.Debugf(c, "Command completed with zero return code: %s %s", x.comman
d, x.args) | 229 log.Debugf(c, "Command completed with zero return code: %s %s", x.comman
d, x.args) |
230 return 0, nil | 230 return 0, nil |
231 } | 231 } |
232 | 232 |
233 func (x *workExecutor) check(c context.Context) error { | 233 func (x *workExecutor) check(c context.Context) error { |
234 switch rc, err := x.run(c); { | 234 switch rc, err := x.run(c); { |
235 case err != nil: | 235 case err != nil: |
236 » » return errors.Annotate(err).Err() | 236 » » return errors.Annotate(err, "").Err() |
237 | 237 |
238 case rc != 0: | 238 case rc != 0: |
239 log.Fields{ | 239 log.Fields{ |
240 "returnCode": rc, | 240 "returnCode": rc, |
241 "command": x.command, | 241 "command": x.command, |
242 "args": x.args, | 242 "args": x.args, |
243 "cwd": x.workdir, | 243 "cwd": x.workdir, |
244 }.Errorf(c, "Command failed with error return code.\nSTDOUT:\n%s
\n\nSTDERR:\n%s", | 244 }.Errorf(c, "Command failed with error return code.\nSTDOUT:\n%s
\n\nSTDERR:\n%s", |
245 x.stdout.String(), x.stderr.String()) | 245 x.stdout.String(), x.stderr.String()) |
246 » » return errors.Reason("process exited with return code: %(returnC
ode)d"). | 246 » » return errors.Reason("process exited with return code: %d", rc). |
247 » » » D("command", x.command).D("args", x.args).D("cwd", x.wor
kdir).D("returnCode", rc).Err() | 247 » » » InternalReason("command(%s)/args(%v)/cwd(%s)", x.command
, x.args, x.workdir).Err() |
248 | 248 |
249 default: | 249 default: |
250 return nil | 250 return nil |
251 } | 251 } |
252 } | 252 } |
253 | 253 |
254 func runWork(c context.Context, workers int, tools *tools, f func(w *work) error
) error { | 254 func runWork(c context.Context, workers int, tools *tools, f func(w *work) error
) error { |
255 return parallel.RunMulti(c, workers, func(mr parallel.MultiRunner) error
{ | 255 return parallel.RunMulti(c, workers, func(mr parallel.MultiRunner) error
{ |
256 return f(&work{ | 256 return f(&work{ |
257 Context: c, | 257 Context: c, |
(...skipping 24 matching lines...) Expand all Loading... |
282 // will panic. | 282 // will panic. |
283 type teeWriter struct { | 283 type teeWriter struct { |
284 base io.Writer | 284 base io.Writer |
285 tee io.Writer | 285 tee io.Writer |
286 } | 286 } |
287 | 287 |
288 func (w *teeWriter) Write(d []byte) (amt int, err error) { | 288 func (w *teeWriter) Write(d []byte) (amt int, err error) { |
289 amt, err = w.base.Write(d) | 289 amt, err = w.base.Write(d) |
290 if amt > 0 { | 290 if amt > 0 { |
291 if _, ierr := w.tee.Write(d[:amt]); ierr != nil { | 291 if _, ierr := w.tee.Write(d[:amt]); ierr != nil { |
292 » » » panic(errors.Annotate(ierr).Reason("failed to write to t
ee writer").Err()) | 292 » » » panic(errors.Annotate(ierr, "failed to write to tee writ
er").Err()) |
293 } | 293 } |
294 } | 294 } |
295 return | 295 return |
296 } | 296 } |
OLD | NEW |