| 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 "flag" | 8 "flag" |
| 9 "fmt" | 9 "fmt" |
| 10 "io" | 10 "io" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 "github.com/luci/luci-go/common/environ" | 31 "github.com/luci/luci-go/common/environ" |
| 32 "github.com/luci/luci-go/common/errors" | 32 "github.com/luci/luci-go/common/errors" |
| 33 "github.com/luci/luci-go/common/logdog/types" | 33 "github.com/luci/luci-go/common/logdog/types" |
| 34 log "github.com/luci/luci-go/common/logging" | 34 log "github.com/luci/luci-go/common/logging" |
| 35 | 35 |
| 36 "github.com/golang/protobuf/proto" | 36 "github.com/golang/protobuf/proto" |
| 37 "golang.org/x/net/context" | 37 "golang.org/x/net/context" |
| 38 ) | 38 ) |
| 39 | 39 |
| 40 type cookLogDogParams struct { | 40 type cookLogDogParams struct { |
| 41 » host string | 41 » host string |
| 42 » project string | 42 » project string |
| 43 » prefix types.StreamName | 43 » prefix types.StreamName |
| 44 » annotee bool | 44 » annotee bool |
| 45 » tee bool | 45 » tee bool |
| 46 » filePath string | 46 |
| 47 » filePath string |
| 48 » serviceAccountJSONPath string |
| 47 } | 49 } |
| 48 | 50 |
| 49 func (p *cookLogDogParams) addFlags(fs *flag.FlagSet) { | 51 func (p *cookLogDogParams) addFlags(fs *flag.FlagSet) { |
| 50 fs.StringVar( | 52 fs.StringVar( |
| 51 &p.host, | 53 &p.host, |
| 52 "logdog-host", | 54 "logdog-host", |
| 53 "", | 55 "", |
| 54 "The name of the LogDog host.") | 56 "The name of the LogDog host.") |
| 55 fs.StringVar( | 57 fs.StringVar( |
| 56 &p.project, | 58 &p.project, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 71 fs.BoolVar( | 73 fs.BoolVar( |
| 72 &p.tee, | 74 &p.tee, |
| 73 "logdog-tee", | 75 "logdog-tee", |
| 74 true, | 76 true, |
| 75 "Tee bootstrapped STDOUT and STDERR through Kitchen. If false, t
hese will only be sent as LogDog streams") | 77 "Tee bootstrapped STDOUT and STDERR through Kitchen. If false, t
hese will only be sent as LogDog streams") |
| 76 fs.StringVar( | 78 fs.StringVar( |
| 77 &p.filePath, | 79 &p.filePath, |
| 78 "logdog-debug-out-file", | 80 "logdog-debug-out-file", |
| 79 "", | 81 "", |
| 80 "If specified, write all generated logs to this path instead of
sending them.") | 82 "If specified, write all generated logs to this path instead of
sending them.") |
| 83 fs.StringVar( |
| 84 &p.serviceAccountJSONPath, |
| 85 "logdog-service-account-json-path", |
| 86 "", |
| 87 "If specified, use the service account JSON file at this path. O
therwise, autodetect.") |
| 81 } | 88 } |
| 82 | 89 |
| 83 func (p *cookLogDogParams) active() bool { | 90 func (p *cookLogDogParams) active() bool { |
| 84 return p.host != "" || p.project != "" || p.prefix != "" || p.tee | 91 return p.host != "" || p.project != "" || p.prefix != "" || p.tee |
| 85 } | 92 } |
| 86 | 93 |
| 87 func (p *cookLogDogParams) validate() error { | 94 func (p *cookLogDogParams) validate() error { |
| 88 if p.project == "" { | 95 if p.project == "" { |
| 89 return fmt.Errorf("a LogDog project must be supplied (-logdog-pr
oject)") | 96 return fmt.Errorf("a LogDog project must be supplied (-logdog-pr
oject)") |
| 90 } | 97 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 return 0, errors.Annotate(err).Reason("failed to get LogDog pref
ix").Err() | 153 return 0, errors.Annotate(err).Reason("failed to get LogDog pref
ix").Err() |
| 147 } | 154 } |
| 148 log.Fields{ | 155 log.Fields{ |
| 149 "prefix": prefix, | 156 "prefix": prefix, |
| 150 }.Infof(ctx, "Using LogDog prefix: %q", prefix) | 157 }.Infof(ctx, "Using LogDog prefix: %q", prefix) |
| 151 | 158 |
| 152 // Set up authentication. | 159 // Set up authentication. |
| 153 authOpts := auth.Options{ | 160 authOpts := auth.Options{ |
| 154 Scopes: out.Scopes(), | 161 Scopes: out.Scopes(), |
| 155 } | 162 } |
| 156 » if !infraenv.OnGCE() { | 163 » switch { |
| 157 » » // If we're not on GCE, we will need to explicitly supply the Lo
gDog | 164 » case c.logdog.serviceAccountJSONPath != "": |
| 158 » » // credentials path. | 165 » » authOpts.ServiceAccountJSONPath = c.logdog.serviceAccountJSONPat
h |
| 166 |
| 167 » case infraenv.OnGCE(): |
| 168 » » // Do nothing, auth will automatically use GCE metadata. |
| 169 » » break |
| 170 |
| 171 » default: |
| 172 » » // No service account specified, so load the LogDog credentials
from the |
| 173 » » // local bot deployment. |
| 159 credPath, err := infraenv.GetLogDogServiceAccountJSON() | 174 credPath, err := infraenv.GetLogDogServiceAccountJSON() |
| 160 if err != nil { | 175 if err != nil { |
| 161 return 0, errors.Annotate(err).Reason("failed to get Log
Dog service account JSON path").Err() | 176 return 0, errors.Annotate(err).Reason("failed to get Log
Dog service account JSON path").Err() |
| 162 } | 177 } |
| 163 authOpts.ServiceAccountJSONPath = credPath | 178 authOpts.ServiceAccountJSONPath = credPath |
| 164 } | 179 } |
| 165 authenticator := auth.NewAuthenticator(ctx, auth.SilentLogin, authOpts) | 180 authenticator := auth.NewAuthenticator(ctx, auth.SilentLogin, authOpts) |
| 166 | 181 |
| 167 // Register and instantiate our LogDog Output. | 182 // Register and instantiate our LogDog Output. |
| 168 var o output.Output | 183 var o output.Output |
| (...skipping 16 matching lines...) Expand all Loading... |
| 185 } else { | 200 } else { |
| 186 // Debug: Use a file output. | 201 // Debug: Use a file output. |
| 187 ocfg := fileOut.Options{ | 202 ocfg := fileOut.Options{ |
| 188 Path: c.logdog.filePath, | 203 Path: c.logdog.filePath, |
| 189 } | 204 } |
| 190 o = ocfg.New(ctx) | 205 o = ocfg.New(ctx) |
| 191 } | 206 } |
| 192 defer o.Close() | 207 defer o.Close() |
| 193 | 208 |
| 194 butlerCfg := butler.Config{ | 209 butlerCfg := butler.Config{ |
| 195 » » Output: o, | 210 » » Output: o, |
| 196 » » Project: config.ProjectName(c.logdog.project), | 211 » » Project: config.ProjectName(c.logdog.project), |
| 197 » » Prefix: c.logdog.prefix, | 212 » » Prefix: prefix, |
| 198 » » BufferLogs: true, | 213 » » BufferLogs: true, |
| 214 » » MaxBufferAge: butler.DefaultMaxBufferAge, |
| 199 } | 215 } |
| 200 | 216 |
| 201 // If we're teeing and we're not using Annotee, tee our subprocess' STDO
UT | 217 // If we're teeing and we're not using Annotee, tee our subprocess' STDO
UT |
| 202 // and STDERR through Kitchen's STDOUT/STDERR. | 218 // and STDERR through Kitchen's STDOUT/STDERR. |
| 203 // | 219 // |
| 204 // If we're using Annotee, we will configure this in the Annotee setup | 220 // If we're using Annotee, we will configure this in the Annotee setup |
| 205 // directly. | 221 // directly. |
| 206 if c.logdog.tee && !c.logdog.annotee { | 222 if c.logdog.tee && !c.logdog.annotee { |
| 207 butlerCfg.TeeStdout = os.Stdout | 223 butlerCfg.TeeStdout = os.Stdout |
| 208 butlerCfg.TeeStderr = os.Stderr | 224 butlerCfg.TeeStderr = os.Stderr |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 Offline: false, | 313 Offline: false, |
| 298 CloseSteps: true, | 314 CloseSteps: true, |
| 299 }) | 315 }) |
| 300 defer func() { | 316 defer func() { |
| 301 as := annoteeProcessor.Finish() | 317 as := annoteeProcessor.Finish() |
| 302 log.Infof(ctx, "Annotations finished:\n%s", proto.Marsha
lTextString(as.RootStep().Proto())) | 318 log.Infof(ctx, "Annotations finished:\n%s", proto.Marsha
lTextString(as.RootStep().Proto())) |
| 303 }() | 319 }() |
| 304 | 320 |
| 305 // Run STDOUT/STDERR streams through the processor. This will bl
ock until | 321 // Run STDOUT/STDERR streams through the processor. This will bl
ock until |
| 306 // both streams are closed. | 322 // both streams are closed. |
| 323 // |
| 324 // If we're teeing, we will tee the full stream, including annot
ations. |
| 307 streams := []*annotee.Stream{ | 325 streams := []*annotee.Stream{ |
| 308 { | 326 { |
| 309 Reader: stdout, | 327 Reader: stdout, |
| 310 Name: annotee.STDOUT, | 328 Name: annotee.STDOUT, |
| 311 Annotate: true, | 329 Annotate: true, |
| 312 » » » » StripAnnotations: true, | 330 » » » » StripAnnotations: !c.logdog.tee, |
| 313 }, | 331 }, |
| 314 { | 332 { |
| 315 Reader: stderr, | 333 Reader: stderr, |
| 316 Name: annotee.STDERR, | 334 Name: annotee.STDERR, |
| 317 Annotate: true, | 335 Annotate: true, |
| 318 » » » » StripAnnotations: true, | 336 » » » » StripAnnotations: !c.logdog.tee, |
| 319 }, | 337 }, |
| 320 } | 338 } |
| 321 if c.logdog.tee { | 339 if c.logdog.tee { |
| 322 streams[0].Tee = os.Stdout | 340 streams[0].Tee = os.Stdout |
| 323 streams[1].Tee = os.Stderr | 341 streams[1].Tee = os.Stderr |
| 324 } | 342 } |
| 325 | 343 |
| 326 // Run the process' output streams through Annotee. This will bl
ock until | 344 // Run the process' output streams through Annotee. This will bl
ock until |
| 327 // they are all consumed. | 345 // they are all consumed. |
| 328 if err = annoteeProcessor.RunStreams(streams); err != nil { | 346 if err = annoteeProcessor.RunStreams(streams); err != nil { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 // callbackReadCloser invokes a callback method when closed. | 405 // callbackReadCloser invokes a callback method when closed. |
| 388 type callbackReadCloser struct { | 406 type callbackReadCloser struct { |
| 389 io.ReadCloser | 407 io.ReadCloser |
| 390 callback func() | 408 callback func() |
| 391 } | 409 } |
| 392 | 410 |
| 393 func (c *callbackReadCloser) Close() error { | 411 func (c *callbackReadCloser) Close() error { |
| 394 defer c.callback() | 412 defer c.callback() |
| 395 return c.ReadCloser.Close() | 413 return c.ReadCloser.Close() |
| 396 } | 414 } |
| OLD | NEW |