| 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 "fmt" | 9 "fmt" |
| 10 "io/ioutil" | 10 "io/ioutil" |
| 11 "net/http" | 11 "net/http" |
| 12 "path/filepath" | 12 "path/filepath" |
| 13 "regexp" | 13 "regexp" |
| 14 "sort" | 14 "sort" |
| 15 "strings" | 15 "strings" |
| 16 "time" | 16 "time" |
| 17 | 17 |
| 18 ds "github.com/luci/gae/service/datastore" | |
| 19 "github.com/luci/luci-go/common/data/stringset" | 18 "github.com/luci/luci-go/common/data/stringset" |
| 20 "github.com/luci/luci-go/common/logging" | 19 "github.com/luci/luci-go/common/logging" |
| 21 "github.com/luci/luci-go/milo/api/resp" | 20 "github.com/luci/luci-go/milo/api/resp" |
| 22 "github.com/luci/luci-go/milo/common/miloerror" | 21 "github.com/luci/luci-go/milo/common/miloerror" |
| 22 |
| 23 ds "github.com/luci/gae/service/datastore" |
| 24 |
| 23 "golang.org/x/net/context" | 25 "golang.org/x/net/context" |
| 24 ) | 26 ) |
| 25 | 27 |
| 26 var errBuildNotFound = miloerror.Error{ | 28 var errBuildNotFound = miloerror.Error{ |
| 27 Message: "Build not found", | 29 Message: "Build not found", |
| 28 Code: http.StatusNotFound, | 30 Code: http.StatusNotFound, |
| 29 } | 31 } |
| 30 | 32 |
| 31 // getBuild fetches a buildbot build from the datastore and checks ACLs. | 33 // getBuild fetches a buildbot build from the datastore and checks ACLs. |
| 32 // The return code matches the master responses. | 34 // The return code matches the master responses. |
| (...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 Value interface{} | 343 Value interface{} |
| 342 Group string | 344 Group string |
| 343 } | 345 } |
| 344 | 346 |
| 345 // properties extracts all properties from buildbot builds and groups them into | 347 // properties extracts all properties from buildbot builds and groups them into |
| 346 // property groups. | 348 // property groups. |
| 347 func properties(b *buildbotBuild) (result []*resp.PropertyGroup) { | 349 func properties(b *buildbotBuild) (result []*resp.PropertyGroup) { |
| 348 groups := map[string]*resp.PropertyGroup{} | 350 groups := map[string]*resp.PropertyGroup{} |
| 349 allProps := map[string]Prop{} | 351 allProps := map[string]Prop{} |
| 350 for _, prop := range b.Properties { | 352 for _, prop := range b.Properties { |
| 351 » » allProps[prop[0].(string)] = Prop{ | 353 » » allProps[prop.Name] = Prop{ |
| 352 » » » Value: prop[1], | 354 » » » Value: prop.Value, |
| 353 » » » Group: prop[2].(string), | 355 » » » Group: prop.Source, |
| 354 } | 356 } |
| 355 } | 357 } |
| 356 for key, prop := range allProps { | 358 for key, prop := range allProps { |
| 357 value := prop.Value | 359 value := prop.Value |
| 358 groupName := prop.Group | 360 groupName := prop.Group |
| 359 if _, ok := groups[groupName]; !ok { | 361 if _, ok := groups[groupName]; !ok { |
| 360 groups[groupName] = &resp.PropertyGroup{GroupName: group
Name} | 362 groups[groupName] = &resp.PropertyGroup{GroupName: group
Name} |
| 361 } | 363 } |
| 362 vs, ok := parseProp(allProps, key, value) | 364 vs, ok := parseProp(allProps, key, value) |
| 363 if !ok { | 365 if !ok { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 413 } | 415 } |
| 414 | 416 |
| 415 // sourcestamp extracts the source stamp from various parts of a buildbot build, | 417 // sourcestamp extracts the source stamp from various parts of a buildbot build, |
| 416 // including the properties. | 418 // including the properties. |
| 417 func sourcestamp(c context.Context, b *buildbotBuild) *resp.SourceStamp { | 419 func sourcestamp(c context.Context, b *buildbotBuild) *resp.SourceStamp { |
| 418 ss := &resp.SourceStamp{} | 420 ss := &resp.SourceStamp{} |
| 419 rietveld := "" | 421 rietveld := "" |
| 420 issue := int64(-1) | 422 issue := int64(-1) |
| 421 // TODO(hinoka): Gerrit URLs. | 423 // TODO(hinoka): Gerrit URLs. |
| 422 for _, prop := range b.Properties { | 424 for _, prop := range b.Properties { |
| 423 » » key := prop[0].(string) | 425 » » switch prop.Name { |
| 424 » » value := prop[1] | |
| 425 » » switch key { | |
| 426 case "rietveld": | 426 case "rietveld": |
| 427 » » » if v, ok := value.(string); ok { | 427 » » » if v, ok := prop.Value.(string); ok { |
| 428 rietveld = v | 428 rietveld = v |
| 429 } else { | 429 } else { |
| 430 » » » » logging.Warningf(c, "Field rietveld is not a str
ing: %#v", value) | 430 » » » » logging.Warningf(c, "Field rietveld is not a str
ing: %#v", prop.Value) |
| 431 } | 431 } |
| 432 case "issue": | 432 case "issue": |
| 433 » » » if v, ok := value.(float64); ok { | 433 » » » if v, ok := prop.Value.(float64); ok { |
| 434 issue = int64(v) | 434 issue = int64(v) |
| 435 } else { | 435 } else { |
| 436 » » » » logging.Warningf(c, "Field issue is not a float:
%#v", value) | 436 » » » » logging.Warningf(c, "Field issue is not a float:
%#v", prop.Value) |
| 437 } | 437 } |
| 438 | 438 |
| 439 case "got_revision": | 439 case "got_revision": |
| 440 » » » if v, ok := value.(string); ok { | 440 » » » if v, ok := prop.Value.(string); ok { |
| 441 ss.Revision = v | 441 ss.Revision = v |
| 442 } else { | 442 } else { |
| 443 » » » » logging.Warningf(c, "Field got_revision is not a
string: %#v", value) | 443 » » » » logging.Warningf(c, "Field got_revision is not a
string: %#v", prop.Value) |
| 444 } | 444 } |
| 445 | 445 |
| 446 } | 446 } |
| 447 } | 447 } |
| 448 if issue != -1 { | 448 if issue != -1 { |
| 449 if rietveld != "" { | 449 if rietveld != "" { |
| 450 rietveld = strings.TrimRight(rietveld, "/") | 450 rietveld = strings.TrimRight(rietveld, "/") |
| 451 ss.Changelist = &resp.Link{ | 451 ss.Changelist = &resp.Link{ |
| 452 Label: fmt.Sprintf("Issue %d", issue), | 452 Label: fmt.Sprintf("Issue %d", issue), |
| 453 URL: fmt.Sprintf("%s/%d", rietveld, issue), | 453 URL: fmt.Sprintf("%s/%d", rietveld, issue), |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 | 488 |
| 489 // TODO(hinoka): Do all fields concurrently. | 489 // TODO(hinoka): Do all fields concurrently. |
| 490 return &resp.MiloBuild{ | 490 return &resp.MiloBuild{ |
| 491 SourceStamp: sourcestamp(c, b), | 491 SourceStamp: sourcestamp(c, b), |
| 492 Summary: summary(c, b), | 492 Summary: summary(c, b), |
| 493 Components: components(b), | 493 Components: components(b), |
| 494 PropertyGroup: properties(b), | 494 PropertyGroup: properties(b), |
| 495 Blame: blame(b), | 495 Blame: blame(b), |
| 496 }, nil | 496 }, nil |
| 497 } | 497 } |
| OLD | NEW |