Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package buildbot | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "strings" | |
| 10 "time" | |
| 11 | |
| 12 "github.com/luci/gae/service/datastore" | |
| 13 "github.com/luci/luci-go/appengine/cmd/milo/resp" | |
| 14 "github.com/luci/luci-go/common/clock" | |
| 15 log "github.com/luci/luci-go/common/logging" | |
| 16 "github.com/luci/luci-go/common/sync/parallel" | |
| 17 "golang.org/x/net/context" | |
| 18 ) | |
| 19 | |
| 20 // getBuilds fetches all of the recent builds from the datastore. | |
| 21 func getFullBuilds(c context.Context, masterName, builderName string, finished b ool) ([]*buildbotBuild, error) { | |
|
nodir
2016/08/03 20:26:16
why "Full"?
hinoka
2016/08/03 21:55:39
Not sure, I think it was arbitrary. I guess full
| |
| 22 // TODO(hinoka): Builder specific structs. | |
| 23 ds := datastore.Get(c) | |
| 24 q := datastore.NewQuery("buildbotBuild") | |
| 25 q = q.Eq("finished", finished) | |
| 26 q = q.Eq("master", masterName) | |
| 27 q = q.Eq("builder", builderName) | |
| 28 q = q.Limit(25) // TODO(hinoka): This should be adjustable | |
| 29 q = q.Order("-number") | |
| 30 q.Finalize() | |
| 31 buildbots := make([]*buildbotBuild, 0, 25) | |
| 32 err := ds.GetAll(q, &buildbots) | |
| 33 return buildbots, err | |
| 34 } | |
| 35 | |
| 36 func GetConsoleBuilds( | |
| 37 c context.Context, builders []resp.BuilderRef, commits []string) ( | |
| 38 [][]*resp.ConsoleBuild, error) { | |
| 39 | |
| 40 // First, fetch all the master entries referenced. | |
| 41 // TODO(hinoka): Parallel fetch. | |
| 42 masters := map[string]*buildbotMaster{} | |
| 43 for _, builder := range builders { | |
|
nodir
2016/08/03 20:26:17
this loop may take a lot of time because of master
hinoka
2016/08/03 21:55:39
Actually this entire loop isn't used... I forgot w
| |
| 44 f := strings.SplitN(builder.Name, "/", 2) | |
|
nodir
2016/08/03 20:26:17
validate that len(f) == 2, return error if not so
hinoka
2016/08/03 21:55:39
Deleted
| |
| 45 masterName := f[0] | |
| 46 if builder.Module != "buildbot" { | |
| 47 panic(fmt.Errorf("Module %s is not buildbot", builder.Mo dule)) | |
| 48 } | |
| 49 if _, ok := masters[masterName]; !ok { | |
| 50 master, internal, t, err := getMasterJSON(c, masterName) | |
| 51 if err != nil { | |
| 52 return nil, err | |
| 53 } | |
| 54 if t.Before(clock.Now(c).Add(-time.Minute * 5)) { | |
| 55 return nil, fmt.Errorf("Data for master is outda ted, last updated %s", t) | |
| 56 } | |
| 57 if internal { | |
| 58 return nil, fmt.Errorf("Internal masters not sup ported") | |
|
nodir
2016/08/03 20:26:17
if someone defines a config that references an int
hinoka
2016/08/03 21:55:39
Acknowledged. Probably should just treat the build
| |
| 59 } | |
| 60 masters[masterName] = master | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 results := make([][]*resp.ConsoleBuild, len(commits)) | |
| 65 for i := range results { | |
| 66 results[i] = make([]*resp.ConsoleBuild, len(builders)) | |
| 67 } | |
| 68 // Reuse the builder code. This is kind of a hack but it's okay for now. | |
| 69 err := parallel.FanOutIn(func(taskC chan<- func() error) { | |
| 70 for i, builder := range builders { | |
| 71 i := i // How do scopes werk | |
| 72 f := strings.SplitN(builder.Name, "/", 2) | |
| 73 master := f[0] | |
| 74 builderName := f[1] | |
|
nodir
2016/08/03 20:26:16
this will panic if builder.Name does not have a sl
hinoka
2016/08/03 21:55:39
Done.
| |
| 75 taskC <- func() error { | |
| 76 t1 := clock.Now(c) | |
| 77 builds, err := getFullBuilds(c, master, builderN ame, true) | |
|
nodir
2016/08/03 20:26:16
it won't show running builds? also does not show a
hinoka
2016/08/03 21:55:39
Yes that is true, but it hasn't been a problem for
nodir
2016/08/04 23:13:12
ok, makes sense now
| |
| 78 if err != nil { | |
| 79 return err | |
| 80 } | |
| 81 t2 := clock.Now(c) | |
| 82 var currentStatus *resp.Status | |
| 83 for j, commit := range commits { | |
| 84 for _, build := range builds { | |
| 85 if build.Sourcestamp.Revision == commit { | |
|
nodir
2016/08/03 20:26:17
this is O(n^2)
make a map commit->j before fanOutI
hinoka
2016/08/03 21:55:39
Logic for filling in-between builds on L97 would b
| |
| 86 results[j][i] = &resp.Co nsoleBuild{ | |
|
nodir
2016/08/03 20:26:17
this code is racy: it mutates results[j] in differ
hinoka
2016/08/03 21:55:39
Each goroutine fully owns a space in [i], so I don
nodir
2016/08/04 23:13:12
Acknowledged.
| |
| 87 Link: &resp.Link { | |
| 88 Label: s trings.Join(build.Text, " "), | |
| 89 URL: fmt .Sprintf( | |
| 90 "/buildbot/%s/%s/%d", master, builderName, build.Number), | |
| 91 }, | |
| 92 Status: build.to Status(), | |
| 93 } | |
| 94 currentStatus = &results [j][i].Status | |
| 95 } | |
| 96 } | |
| 97 if currentStatus != nil && results[j][i] == nil { | |
| 98 results[j][i] = &resp.ConsoleBui ld{Status: *currentStatus} | |
|
nodir
2016/08/03 20:26:17
I don't understand this logic. It might be simpler
hinoka
2016/08/03 21:55:39
Each goroutine processes a single builder. This j
nodir
2016/08/04 23:13:12
this does not sound correct. if there was no build
| |
| 99 } | |
| 100 } | |
| 101 log.Debugf(c, | |
| 102 "Builder %s took %s to query, %s to comp ute.", builderName, | |
| 103 t2.Sub(t1), clock.Since(c, t2)) | |
| 104 return nil | |
| 105 } | |
| 106 } | |
| 107 }) | |
| 108 | |
| 109 return results, err | |
| 110 | |
|
nodir
2016/08/03 20:26:17
remove blank line
hinoka
2016/08/03 21:55:39
Done.
| |
| 111 } | |
| OLD | NEW |