| 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 buildbot | 5 package buildbot |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "encoding/json" | 8 "encoding/json" |
| 9 "errors" | 9 "errors" |
| 10 "fmt" | 10 "fmt" |
| 11 "io/ioutil" | 11 "io/ioutil" |
| 12 "math" |
| 12 "path/filepath" | 13 "path/filepath" |
| 13 "regexp" | 14 "regexp" |
| 14 "sort" | 15 "sort" |
| 15 "strings" | 16 "strings" |
| 16 "time" | 17 "time" |
| 17 | 18 |
| 18 "golang.org/x/net/context" | 19 "golang.org/x/net/context" |
| 19 | 20 |
| 20 "github.com/luci/gae/service/datastore" | 21 "github.com/luci/gae/service/datastore" |
| 21 "github.com/luci/luci-go/common/data/stringset" | 22 "github.com/luci/luci-go/common/data/stringset" |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 306 } | 307 } |
| 307 | 308 |
| 308 // Figure out the times. | 309 // Figure out the times. |
| 309 bc.Started, bc.Finished, bc.Duration = parseTimes(endingTime, st
ep.Times) | 310 bc.Started, bc.Finished, bc.Duration = parseTimes(endingTime, st
ep.Times) |
| 310 | 311 |
| 311 result = append(result, bc) | 312 result = append(result, bc) |
| 312 } | 313 } |
| 313 return | 314 return |
| 314 } | 315 } |
| 315 | 316 |
| 316 // parseProp returns a representation of v based off k, and a boolean to | 317 // parseProp returns a string representation of v. |
| 317 // specify whether or not to hide it altogether. | 318 func parseProp(v interface{}) string { |
| 318 func parseProp(prop map[string]Prop, k string, v interface{}) (string, bool) { | 319 » // if v is a whole number, force it into an int. json.Marshal() would t
urn |
| 319 » switch k { | 320 » // it into what looks like a float instead. We want this to remain and |
| 320 » case "requestedAt": | 321 » // int instead of a number. |
| 321 » » if vf, ok := v.(float64); ok { | 322 » if vf, ok := v.(float64); ok { |
| 322 » » » return time.Unix(int64(vf), 0).UTC().Format(time.RFC3339
), true | 323 » » if math.Floor(vf) == vf { |
| 323 » » } | 324 » » » return fmt.Sprintf("%d", int64(vf)) |
| 324 » case "buildbucket": | |
| 325 » » var b map[string]interface{} | |
| 326 » » json.Unmarshal([]byte(v.(string)), &b) | |
| 327 » » if b["build"] == nil { | |
| 328 » » » return "", false | |
| 329 » » } | |
| 330 » » build := b["build"].(map[string]interface{}) | |
| 331 » » id := build["id"] | |
| 332 » » if id == nil { | |
| 333 » » » return "", false | |
| 334 » » } | |
| 335 » » return fmt.Sprintf("https://cr-buildbucket.appspot.com/b/%s", id
.(string)), true | |
| 336 » case "issue": | |
| 337 » » if rv, ok := prop["rietveld"]; ok { | |
| 338 » » » rietveld := rv.Value.(string) | |
| 339 » » » // Issue could be a float, int, or string. | |
| 340 » » » switch v := v.(type) { | |
| 341 » » » case float64: | |
| 342 » » » » return fmt.Sprintf("%s/%d", rietveld, int(v)), t
rue | |
| 343 » » » default: // Probably int or string | |
| 344 » » » » return fmt.Sprintf("%s/%v", rietveld, v), true | |
| 345 » » » } | |
| 346 » » } | |
| 347 » » return fmt.Sprintf("%d", int(v.(float64))), true | |
| 348 » case "rietveld": | |
| 349 » » return "", false | |
| 350 » default: | |
| 351 » » if vs, ok := v.(string); ok && vs == "" { | |
| 352 » » » return "", false // Value is empty, don't show it. | |
| 353 » » } | |
| 354 » » if v == nil { | |
| 355 » » » return "", false | |
| 356 } | 325 } |
| 357 } | 326 } |
| 358 » return fmt.Sprintf("%v", v), true | 327 » // return the json representation of the value. |
| 328 » b, err := json.Marshal(v) |
| 329 » if err == nil { |
| 330 » » return string(b) |
| 331 » } |
| 332 » return fmt.Sprintf("%v", v) |
| 359 } | 333 } |
| 360 | 334 |
| 361 // Prop is a struct used to store a value and group so that we can make a map | 335 // Prop is a struct used to store a value and group so that we can make a map |
| 362 // of key:Prop to pass into parseProp() for the purpose of cross referencing | 336 // of key:Prop to pass into parseProp() for the purpose of cross referencing |
| 363 // one prop while working on another. | 337 // one prop while working on another. |
| 364 type Prop struct { | 338 type Prop struct { |
| 365 Value interface{} | 339 Value interface{} |
| 366 Group string | 340 Group string |
| 367 } | 341 } |
| 368 | 342 |
| 369 // properties extracts all properties from buildbot builds and groups them into | 343 // properties extracts all properties from buildbot builds and groups them into |
| 370 // property groups. | 344 // property groups. |
| 371 func properties(b *buildbotBuild) (result []*resp.PropertyGroup) { | 345 func properties(b *buildbotBuild) (result []*resp.PropertyGroup) { |
| 372 groups := map[string]*resp.PropertyGroup{} | 346 groups := map[string]*resp.PropertyGroup{} |
| 373 allProps := map[string]Prop{} | 347 allProps := map[string]Prop{} |
| 374 for _, prop := range b.Properties { | 348 for _, prop := range b.Properties { |
| 375 allProps[prop.Name] = Prop{ | 349 allProps[prop.Name] = Prop{ |
| 376 Value: prop.Value, | 350 Value: prop.Value, |
| 377 Group: prop.Source, | 351 Group: prop.Source, |
| 378 } | 352 } |
| 379 } | 353 } |
| 380 for key, prop := range allProps { | 354 for key, prop := range allProps { |
| 381 value := prop.Value | 355 value := prop.Value |
| 382 groupName := prop.Group | 356 groupName := prop.Group |
| 383 if _, ok := groups[groupName]; !ok { | 357 if _, ok := groups[groupName]; !ok { |
| 384 groups[groupName] = &resp.PropertyGroup{GroupName: group
Name} | 358 groups[groupName] = &resp.PropertyGroup{GroupName: group
Name} |
| 385 } | 359 } |
| 386 » » vs, ok := parseProp(allProps, key, value) | 360 » » vs := parseProp(value) |
| 387 » » if !ok { | |
| 388 » » » continue | |
| 389 » » } | |
| 390 groups[groupName].Property = append(groups[groupName].Property,
&resp.Property{ | 361 groups[groupName].Property = append(groups[groupName].Property,
&resp.Property{ |
| 391 Key: key, | 362 Key: key, |
| 392 Value: vs, | 363 Value: vs, |
| 393 }) | 364 }) |
| 394 } | 365 } |
| 395 // Insert the groups into a list in alphabetical order. | 366 // Insert the groups into a list in alphabetical order. |
| 396 // You have to make a separate sorting data structure because Go doesn't
like | 367 // You have to make a separate sorting data structure because Go doesn't
like |
| 397 // sorting things for you. | 368 // sorting things for you. |
| 398 groupNames := []string{} | 369 groupNames := []string{} |
| 399 for n := range groups { | 370 for n := range groups { |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 659 var newAliases map[string][]*buildbotLinkAlias | 630 var newAliases map[string][]*buildbotLinkAlias |
| 660 if l := remainingAliases.Len(); l > 0 { | 631 if l := remainingAliases.Len(); l > 0 { |
| 661 newAliases = make(map[string][]*buildbotLinkAlias, l) | 632 newAliases = make(map[string][]*buildbotLinkAlias, l) |
| 662 remainingAliases.Iter(func(v string) bool { | 633 remainingAliases.Iter(func(v string) bool { |
| 663 newAliases[v] = s.Aliases[v] | 634 newAliases[v] = s.Aliases[v] |
| 664 return true | 635 return true |
| 665 }) | 636 }) |
| 666 } | 637 } |
| 667 s.Aliases = newAliases | 638 s.Aliases = newAliases |
| 668 } | 639 } |
| OLD | NEW |