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

Side by Side Diff: milo/appengine/buildbot/builder.go

Issue 2321243002: Milo: Add current builds to buildbot builder view (Closed)
Patch Set: Rebase Created 4 years, 1 month 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
« no previous file with comments | « no previous file | milo/appengine/buildbot/master.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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"
9 "fmt" 8 "fmt"
9 "net/http"
10 "sort" 10 "sort"
11 "strings" 11 "strings"
12 "time" 12 "time"
13 13
14 ds "github.com/luci/gae/service/datastore" 14 ds "github.com/luci/gae/service/datastore"
15 15
16 "github.com/luci/luci-go/common/clock" 16 "github.com/luci/luci-go/common/clock"
17 "github.com/luci/luci-go/common/logging" 17 "github.com/luci/luci-go/common/logging"
18 "github.com/luci/luci-go/milo/api/resp" 18 "github.com/luci/luci-go/milo/api/resp"
19 "github.com/luci/luci-go/milo/common/miloerror"
19 "golang.org/x/net/context" 20 "golang.org/x/net/context"
20 ) 21 )
21 22
22 // builderRef is used for keying specific builds in a master json. 23 // builderRef is used for keying specific builds in a master json.
23 type builderRef struct { 24 type builderRef struct {
24 builder string 25 builder string
25 buildNum int 26 buildNum int
26 } 27 }
27 28
28 // buildMap contains all of the current build within a master json. We use this 29 // buildMap contains all of the current build within a master json. We use this
29 // because buildbot returns all current builds as within the slaves portion, whe reas 30 // because buildbot returns all current builds as within the slaves portion, whe reas
30 // it's eaiser to map thenm by builders instead. 31 // it's eaiser to map thenm by builders instead.
31 type buildMap map[builderRef]*buildbotBuild 32 type buildMap map[builderRef]*buildbotBuild
32 33
33 // createRunningBuildMap extracts all of the running builds in a master json
34 // from the various slaves and dumps it into a map for easy reference.
35 func createRunningBuildMap(master *buildbotMaster) buildMap {
36 result := buildMap{}
37 for _, slave := range master.Slaves {
38 for _, build := range slave.Runningbuilds {
39 result[builderRef{build.Buildername, build.Number}] = bu ild
40 }
41 }
42 return result
43 }
44
45 func getBuildSummary(b *buildbotBuild) *resp.BuildSummary { 34 func getBuildSummary(b *buildbotBuild) *resp.BuildSummary {
46 started, finished, duration := parseTimes(b.Times) 35 started, finished, duration := parseTimes(b.Times)
47 return &resp.BuildSummary{ 36 return &resp.BuildSummary{
48 Link: &resp.Link{ 37 Link: &resp.Link{
49 URL: fmt.Sprintf("%d", b.Number), 38 URL: fmt.Sprintf("%d", b.Number),
50 Label: fmt.Sprintf("#%d", b.Number), 39 Label: fmt.Sprintf("#%d", b.Number),
51 }, 40 },
52 Status: b.toStatus(), 41 Status: b.toStatus(),
53 ExecutionTime: resp.Interval{ 42 ExecutionTime: resp.Interval{
54 Started: started, 43 Started: started,
(...skipping 11 matching lines...) Expand all
66 func getBuilds( 55 func getBuilds(
67 c context.Context, masterName, builderName string, finished bool, limit int) ( 56 c context.Context, masterName, builderName string, finished bool, limit int) (
68 []*resp.BuildSummary, error) { 57 []*resp.BuildSummary, error) {
69 58
70 // TODO(hinoka): Builder specific structs. 59 // TODO(hinoka): Builder specific structs.
71 result := []*resp.BuildSummary{} 60 result := []*resp.BuildSummary{}
72 q := ds.NewQuery("buildbotBuild") 61 q := ds.NewQuery("buildbotBuild")
73 q = q.Eq("finished", finished) 62 q = q.Eq("finished", finished)
74 q = q.Eq("master", masterName) 63 q = q.Eq("master", masterName)
75 q = q.Eq("builder", builderName) 64 q = q.Eq("builder", builderName)
76 » q = q.Limit(int32(limit)) 65 » if limit != 0 {
66 » » q = q.Limit(int32(limit))
67 » }
77 q = q.Order("-number") 68 q = q.Order("-number")
78 buildbots := []*buildbotBuild{} 69 buildbots := []*buildbotBuild{}
79 err := ds.GetAll(c, q, &buildbots) 70 err := ds.GetAll(c, q, &buildbots)
80 if err != nil { 71 if err != nil {
81 return nil, err 72 return nil, err
82 } 73 }
83 for _, b := range buildbots { 74 for _, b := range buildbots {
84 result = append(result, getBuildSummary(b)) 75 result = append(result, getBuildSummary(b))
85 } 76 }
86 return result, nil 77 return result, nil
87 } 78 }
88 79
89 // getCurrentBuild extracts a build from a map of current builds, and translates 80 var errMasterNotFound = miloerror.Error{
90 // it into the milo version of the build. 81 » Message: "Master not found",
91 func getCurrentBuild(c context.Context, bMap buildMap, builder string, buildNum int) *resp.BuildSummary { 82 » Code: http.StatusNotFound,
92 » b, ok := bMap[builderRef{builder, buildNum}]
93 » if !ok {
94 » » logging.Warningf(c, "Could not find %s/%d in builder map:\n %s", builder, buildNum, bMap)
95 » » return nil
96 » }
97 » return getBuildSummary(b)
98 }
99
100 // getCurrentBuilds extracts the list of all the current builds from a master js on
101 // from the slaves' runningBuilds portion.
102 func getCurrentBuilds(c context.Context, master *buildbotMaster, builderName str ing) []*resp.BuildSummary {
103 » b := master.Builders[builderName]
104 » results := []*resp.BuildSummary{}
105 » bMap := createRunningBuildMap(master)
106 » for _, bn := range b.CurrentBuilds {
107 » » cb := getCurrentBuild(c, bMap, builderName, bn)
108 » » if cb != nil {
109 » » » results = append(results, cb)
110 » » }
111 » }
112 » return results
113 } 83 }
114 84
115 // builderImpl is the implementation for getting a milo builder page from buildb ot. 85 // builderImpl is the implementation for getting a milo builder page from buildb ot.
116 // This gets: 86 // This gets:
117 // * Current Builds from querying the master json from the datastore. 87 // * Current Builds from querying the master json from the datastore.
118 // * Recent Builds from a cron job that backfills the recent builds. 88 // * Recent Builds from a cron job that backfills the recent builds.
119 func builderImpl(c context.Context, masterName, builderName string, limit int) ( *resp.Builder, error) { 89 func builderImpl(c context.Context, masterName, builderName string, limit int) ( *resp.Builder, error) {
120 result := &resp.Builder{ 90 result := &resp.Builder{
121 Name: builderName, 91 Name: builderName,
122 } 92 }
123 master, t, err := getMasterJSON(c, masterName) 93 master, t, err := getMasterJSON(c, masterName)
124 » switch { 94 » if err != nil {
125 » case err == ds.ErrNoSuchEntity:
126 » » return nil, errMasterNotFound
127 » case err != nil:
128 return nil, err 95 return nil, err
129 } 96 }
130 if clock.Now(c).Sub(t) > 2*time.Minute { 97 if clock.Now(c).Sub(t) > 2*time.Minute {
131 warning := fmt.Sprintf( 98 warning := fmt.Sprintf(
132 "WARNING: Master data is stale (last updated %s)", t) 99 "WARNING: Master data is stale (last updated %s)", t)
133 logging.Warningf(c, warning) 100 logging.Warningf(c, warning)
134 result.Warning = warning 101 result.Warning = warning
135 } 102 }
136 103
137 s, _ := json.Marshal(master)
138 logging.Debugf(c, "Master: %s", s)
139
140 p, ok := master.Builders[builderName] 104 p, ok := master.Builders[builderName]
141 if !ok { 105 if !ok {
142 // This long block is just to return a good error message when a n invalid 106 // This long block is just to return a good error message when a n invalid
143 // buildbot builder is specified. 107 // buildbot builder is specified.
144 keys := make([]string, 0, len(master.Builders)) 108 keys := make([]string, 0, len(master.Builders))
145 for k := range master.Builders { 109 for k := range master.Builders {
146 keys = append(keys, k) 110 keys = append(keys, k)
147 } 111 }
148 sort.Strings(keys) 112 sort.Strings(keys)
149 avail := strings.Join(keys, "\n") 113 avail := strings.Join(keys, "\n")
(...skipping 14 matching lines...) Expand all
164 } 128 }
165 result.PendingBuilds[i].Blame = make([]*resp.Commit, len(pb.Sour ce.Changes)) 129 result.PendingBuilds[i].Blame = make([]*resp.Commit, len(pb.Sour ce.Changes))
166 for j, cm := range pb.Source.Changes { 130 for j, cm := range pb.Source.Changes {
167 result.PendingBuilds[i].Blame[j] = &resp.Commit{ 131 result.PendingBuilds[i].Blame[j] = &resp.Commit{
168 AuthorEmail: cm.Who, 132 AuthorEmail: cm.Who,
169 CommitURL: cm.Revlink, 133 CommitURL: cm.Revlink,
170 } 134 }
171 } 135 }
172 } 136 }
173 137
174 » recentBuilds, err := getBuilds(c, masterName, builderName, true, limit) 138 » // This is CPU bound anyways, so there's no need to do this in parallel.
139 » finishedBuilds, err := getBuilds(c, masterName, builderName, true, limit )
175 if err != nil { 140 if err != nil {
176 return nil, err 141 return nil, err
177 } 142 }
178 currentBuilds, err := getBuilds(c, masterName, builderName, false, 0) 143 currentBuilds, err := getBuilds(c, masterName, builderName, false, 0)
179 if err != nil { 144 if err != nil {
180 return nil, err 145 return nil, err
181 } 146 }
182 » logging.Debugf(c, "Number of current builds: %d", len(currentBuilds)) 147 » result.CurrentBuilds = currentBuilds
183 » // TODO(hinoka): This works, but there's a lot of junk data from 148 » for _, fb := range finishedBuilds {
184 » // masters with unclean shutdown. Need to implement a cleanup
185 » // procedure of some sort. Once that is done, set:
186 » // result.CurrentBuilds = currentBuilds
187
188 » for _, fb := range recentBuilds {
189 » » // Yes recent builds is synonymous with finished builds.
190 » » // TODO(hinoka): Implement limits.
191 if fb != nil { 149 if fb != nil {
192 result.FinishedBuilds = append(result.FinishedBuilds, fb ) 150 result.FinishedBuilds = append(result.FinishedBuilds, fb )
193 } 151 }
194 } 152 }
195 return result, nil 153 return result, nil
196 } 154 }
OLDNEW
« no previous file with comments | « no previous file | milo/appengine/buildbot/master.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698