Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(599)

Side by Side Diff: appengine/cmd/milo/swarming/build.go

Issue 2078603002: milo: fix running steps (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@milo-pending
Patch Set: address comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 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 swarming 5 package swarming
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "encoding/json" 9 "encoding/json"
10 "fmt" 10 "fmt"
11 "io/ioutil" 11 "io/ioutil"
12 "path/filepath" 12 "path/filepath"
13 "strings" 13 "strings"
14 "sync" 14 "sync"
15 "time" 15 "time"
16 16
17 "github.com/luci/luci-go/appengine/cmd/milo/logdog" 17 "github.com/luci/luci-go/appengine/cmd/milo/logdog"
18 "github.com/luci/luci-go/appengine/cmd/milo/resp" 18 "github.com/luci/luci-go/appengine/cmd/milo/resp"
19 "github.com/luci/luci-go/appengine/gaeauth/client" 19 "github.com/luci/luci-go/appengine/gaeauth/client"
20 "github.com/luci/luci-go/client/logdog/annotee" 20 "github.com/luci/luci-go/client/logdog/annotee"
21 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1" 21 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1"
22 "github.com/luci/luci-go/common/clock"
22 "github.com/luci/luci-go/common/logdog/types" 23 "github.com/luci/luci-go/common/logdog/types"
23 "github.com/luci/luci-go/common/logging" 24 "github.com/luci/luci-go/common/logging"
24 miloProto "github.com/luci/luci-go/common/proto/milo" 25 miloProto "github.com/luci/luci-go/common/proto/milo"
25 "github.com/luci/luci-go/common/transport" 26 "github.com/luci/luci-go/common/transport"
26 "golang.org/x/net/context" 27 "golang.org/x/net/context"
27 ) 28 )
28 29
29 // Swarming task states.. 30 // Swarming task states..
30 const ( 31 const (
31 // TaskRunning means task is running. 32 // TaskRunning means task is running.
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 227
227 // Timeswamapts 228 // Timeswamapts
228 comp.Started = asc.Started.Time().Format(time.RFC3339) 229 comp.Started = asc.Started.Time().Format(time.RFC3339)
229 230
230 // This should be the exact same thing. 231 // This should be the exact same thing.
231 comp.Text = asc.Text 232 comp.Text = asc.Text
232 233
233 return comp 234 return comp
234 } 235 }
235 236
236 func swarmingProperties(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup { 237 func taskProperties(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup {
237 props := &resp.PropertyGroup{GroupName: "Swarming"} 238 props := &resp.PropertyGroup{GroupName: "Swarming"}
238 if len(sr.CostsUsd) == 1 { 239 if len(sr.CostsUsd) == 1 {
239 props.Property = append(props.Property, &resp.Property{ 240 props.Property = append(props.Property, &resp.Property{
240 Key: "Cost of job (USD)", 241 Key: "Cost of job (USD)",
241 Value: fmt.Sprintf("$%.2f", sr.CostsUsd[0]), 242 Value: fmt.Sprintf("$%.2f", sr.CostsUsd[0]),
242 }) 243 })
243 } 244 }
244 if sr.State == TaskCompleted || sr.State == TaskTimedOut { 245 if sr.State == TaskCompleted || sr.State == TaskTimedOut {
245 props.Property = append(props.Property, &resp.Property{ 246 props.Property = append(props.Property, &resp.Property{
246 Key: "Exit Code", 247 Key: "Exit Code",
247 Value: fmt.Sprintf("%d", sr.ExitCode), 248 Value: fmt.Sprintf("%d", sr.ExitCode),
248 }) 249 })
249 } 250 }
250 return props 251 return props
251 } 252 }
252 253
253 func swarmingTags(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup { 254 func tagsToProperties(tags []string) *resp.PropertyGroup {
254 props := &resp.PropertyGroup{GroupName: "Swarming Tags"} 255 props := &resp.PropertyGroup{GroupName: "Swarming Tags"}
255 » for _, s := range sr.Tags { 256 » for _, t := range tags {
256 » » sp := strings.SplitN(s, ":", 2) 257 » » if t == "" {
257 » » var k, v string 258 » » » continue
258 » » k = sp[0]
259 » » if len(sp) == 2 {
260 » » » v = sp[1]
261 } 259 }
262 » » props.Property = append(props.Property, &resp.Property{ 260 » » parts := strings.SplitN(t, ":", 2)
263 » » » Key: k, 261 » » p := &resp.Property{
264 » » » Value: v, 262 » » » Key: parts[0],
265 » » }) 263 » » }
264 » » if len(parts) == 2 {
265 » » » p.Value = parts[1]
266 » » }
267 » » props.Property = append(props.Property, p)
266 } 268 }
267 return props 269 return props
268 } 270 }
269 271
270 func addSwarmingToBuild( 272 func taskToBuild(c context.Context, sr *swarming.SwarmingRpcsTaskResult) (*resp. MiloBuild, error) {
271 » c context.Context, sr *swarming.SwarmingRpcsTaskResult, build *resp.Milo Build) { 273 » build := &resp.MiloBuild{}
272 » // Specify the result. 274 » switch sr.State {
273 » if sr.State == "RUNNING" { 275 » case TaskRunning:
274 build.Summary.Status = resp.Running 276 build.Summary.Status = resp.Running
275 » } else if sr.State == "PENDING" { 277
278 » case TaskPending:
276 build.Summary.Status = resp.NotRun 279 build.Summary.Status = resp.NotRun
277 » } else if sr.InternalFailure == true || sr.State == "BOT_DIED" || sr.Sta te == "EXPIRED" || sr.State == "TIMED_OUT" { 280
281 » case TaskExpired, TaskTimedOut, TaskBotDied:
278 build.Summary.Status = resp.InfraFailure 282 build.Summary.Status = resp.InfraFailure
279 » } else if sr.Failure == true || sr.State == "CANCELLED" { 283
284 » case TaskCanceled:
280 // Cancelled build is user action, so it is not an infra failure . 285 // Cancelled build is user action, so it is not an infra failure .
281 build.Summary.Status = resp.Failure 286 build.Summary.Status = resp.Failure
282 » } else { 287
283 » » build.Summary.Status = resp.Success 288 » case TaskCompleted:
289
290 » » switch {
291 » » case sr.InternalFailure:
292 » » » build.Summary.Status = resp.InfraFailure
293 » » case sr.Failure:
294 » » » build.Summary.Status = resp.Failure
295 » » default:
296 » » » build.Summary.Status = resp.Success
297 » » }
298
299 » default:
300 » » return nil, fmt.Errorf("unknown swarming task state %q", sr.Stat e)
284 } 301 }
285 302
286 // Extract more swarming specific information into the properties. 303 // Extract more swarming specific information into the properties.
287 » build.PropertyGroup = append(build.PropertyGroup, swarmingProperties(sr) ) 304 » if props := taskProperties(sr); len(props.Property) > 0 {
288 » build.PropertyGroup = append(build.PropertyGroup, swarmingTags(sr)) 305 » » build.PropertyGroup = append(build.PropertyGroup, props)
306 » }
307 » if props := tagsToProperties(sr.Tags); len(props.Property) > 0 {
308 » » build.PropertyGroup = append(build.PropertyGroup, props)
309 » }
289 310
290 » // Build times. Swarming timestamps are RFC3339Nano without the timezon e 311 » // Build times. Swarming timestamps are UTC RFC3339Nano, but without the
291 » // information, which is assumed to be UTC, so we fix it here. 312 » // timezone information. Make them valid RFC3339Nano.
292 » build.Summary.Started = fmt.Sprintf("%sZ", sr.StartedTs) 313 » build.Summary.Started = sr.StartedTs + "Z"
293 if sr.CompletedTs != "" { 314 if sr.CompletedTs != "" {
294 » » build.Summary.Finished = fmt.Sprintf("%sZ", sr.CompletedTs) 315 » » build.Summary.Finished = sr.CompletedTs + "Z"
295 } 316 }
296 » build.Summary.Duration = uint64(sr.Duration) 317 » if sr.Duration != 0 {
318 » » build.Summary.Duration = uint64(sr.Duration)
319 » } else if sr.State == TaskRunning {
320 » » started, err := time.Parse(time.RFC3339, build.Summary.Started)
321 » » if err != nil {
322 » » » return nil, fmt.Errorf("invalid task StartedTs: %s", err )
323 » » }
324 » » now := clock.Now(c)
325 » » if started.Before(now) {
326 » » » build.Summary.Duration = uint64(clock.Now(c).Sub(started ).Seconds())
327 » » }
328 » }
329
330 » return build, nil
297 } 331 }
298 332
299 // Takes in an annotated log and returns a fully populated set of logdog streams 333 // streamsFromAnnotatedLog takes in an annotated log and returns a fully
334 // populated set of logdog streams
300 func streamsFromAnnotatedLog(ctx context.Context, log []byte) (*logdog.Streams, error) { 335 func streamsFromAnnotatedLog(ctx context.Context, log []byte) (*logdog.Streams, error) {
301 c := &memoryClient{} 336 c := &memoryClient{}
302 p := annotee.New(ctx, annotee.Options{ 337 p := annotee.New(ctx, annotee.Options{
303 Client: c, 338 Client: c,
304 MetadataUpdateInterval: -1, // Neverrrrrr send incr updates. 339 MetadataUpdateInterval: -1, // Neverrrrrr send incr updates.
305 Offline: true, 340 Offline: true,
306 }) 341 })
307 defer p.Finish()
308 342
309 is := annotee.Stream{ 343 is := annotee.Stream{
310 Reader: bytes.NewBuffer(log), 344 Reader: bytes.NewBuffer(log),
311 Name: types.StreamName("stdout"), 345 Name: types.StreamName("stdout"),
312 Annotate: true, 346 Annotate: true,
313 StripAnnotations: true, 347 StripAnnotations: true,
314 } 348 }
315 // If this ever has more than one stream then memoryClient needs to beco me 349 // If this ever has more than one stream then memoryClient needs to beco me
316 // goroutine safe 350 // goroutine safe
317 if err := p.RunStreams([]*annotee.Stream{&is}); err != nil { 351 if err := p.RunStreams([]*annotee.Stream{&is}); err != nil {
(...skipping 14 matching lines...) Expand all
332 for _, t := range sr.Tags { 366 for _, t := range sr.Tags {
333 if t == "allow_milo:1" { 367 if t == "allow_milo:1" {
334 allowMilo = true 368 allowMilo = true
335 break 369 break
336 } 370 }
337 } 371 }
338 if !allowMilo { 372 if !allowMilo {
339 return nil, fmt.Errorf("Not A Milo Job") 373 return nil, fmt.Errorf("Not A Milo Job")
340 } 374 }
341 375
342 » build := &resp.MiloBuild{} 376 » build, err := taskToBuild(c, sr)
343 » addSwarmingToBuild(c, sr, build) 377 » if err != nil {
378 » » return nil, err
379 » }
344 380
345 // Decode the data using annotee. The logdog stream returned here is ass umed 381 // Decode the data using annotee. The logdog stream returned here is ass umed
346 // to be consistent, which is why the following block of code are not 382 // to be consistent, which is why the following block of code are not
347 // expected to ever err out. 383 // expected to ever err out.
348 lds, err := streamsFromAnnotatedLog(c, body) 384 lds, err := streamsFromAnnotatedLog(c, body)
349 if err != nil { 385 if err != nil {
350 build.Components = []*resp.BuildComponent{{ 386 build.Components = []*resp.BuildComponent{{
351 Type: resp.Summary, 387 Type: resp.Summary,
352 Label: "milo annotation parser", 388 Label: "milo annotation parser",
353 Text: []string{err.Error()}, 389 Text: []string{err.Error()},
354 Status: resp.InfraFailure, 390 Status: resp.InfraFailure,
355 SubLink: []*resp.Link{{ 391 SubLink: []*resp.Link{{
356 Label: "swarming task", 392 Label: "swarming task",
357 URL: taskPageURL(resolveServer(server), taskID ), 393 URL: taskPageURL(resolveServer(server), taskID ),
358 }}, 394 }},
359 }} 395 }}
360 } else { 396 } else {
361 logdog.AddLogDogToBuild(c, URL, lds, build) 397 logdog.AddLogDogToBuild(c, URL, lds, build)
362 } 398 }
363 399
364 return build, nil 400 return build, nil
365 } 401 }
366 402
367 // taskPageURL returns a URL to a human-consumable page of a swarming task. 403 // taskPageURL returns a URL to a human-consumable page of a swarming task.
368 func taskPageURL(swarmingHostname, taskID string) string { 404 func taskPageURL(swarmingHostname, taskID string) string {
369 return fmt.Sprintf("https://%s/user/task/%s", swarmingHostname, taskID) 405 return fmt.Sprintf("https://%s/user/task/%s", swarmingHostname, taskID)
370 } 406 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698