| 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 |