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

Side by Side Diff: appengine/cmd/milo/collectors/buildbot/collector.go

Issue 2058573003: Remove the milo/buildbot backfiller (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: Also the buildbot collector 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
(Empty)
1 // Copyright 2015 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 "encoding/json"
9 "fmt"
10 "net/http"
11 "strings"
12 "time"
13
14 "github.com/luci/gae/service/datastore"
15 "github.com/luci/luci-go/appengine/cmd/milo/model"
16 log "github.com/luci/luci-go/common/logging"
17 "github.com/luci/luci-go/common/parallel"
18 "golang.org/x/net/context"
19 )
20
21 // BuildExtractURL is the URL of Chrome Build extract, which caches buildbot
22 // json.
23 const BuildExtractURL = "https://chrome-build-extract.appspot.com"
24
25 const workPoolSizeMultiplier = 2
26
27 type changeJSON struct {
28 Repository string `json:"repository"`
29 Digest string `json:"revision"`
30 UnixTime int64 `json:"when"`
31 }
32
33 type sourceJSON struct {
34 Changes []changeJSON `json:"changes"`
35 }
36
37 type buildJSON struct {
38 // Times elements are a float64 unix time that the build was started. It 's
39 // an array because buildbot :)
40 Times []float64 `json:"times"`
41 Builder string `json:"builderName"`
42 Master string `json:"masterName"`
43 Error string `json:"error"`
44 Logs [][]string `json:"logs"`
45 SourceStamp sourceJSON `json:"sourceStamp"`
46 Number int `json:"number"`
47 Text []string `json:"text"`
48 }
49
50 func getRevForChange(c context.Context, change changeJSON) model.RevisionInfo {
51 repo := model.GetRepository(c, change.Repository)
52
53 generation := -1
54 if rev, err := repo.GetRevision(c, change.Digest); err == nil {
55 generation = rev.Generation
56 }
57
58 return model.RevisionInfo{
59 Repository: change.Repository,
60 Digest: change.Digest,
61 Generation: generation,
62 }
63 }
64
65 func (b *buildJSON) toDS(c context.Context) *model.Build {
66 perRepo := make(map[string]*changeJSON)
67 for _, change := range b.SourceStamp.Changes {
68 current := perRepo[change.Repository]
69 if current != nil {
70 if current.UnixTime < change.UnixTime {
71 perRepo[change.Repository] = &change
72 }
73 } else {
74 perRepo[change.Repository] = &change
75 }
76 }
77
78 revisions := make([]model.RevisionInfo, 0, len(perRepo))
79 for _, change := range perRepo {
80 revisions = append(revisions, getRevForChange(c, *change))
81 }
82
83 logKey := ""
84 if len(b.Logs) > 1 && len(b.Logs[1]) > 1 {
85 logKey = b.Logs[1][1]
86 } else {
87 log.Warningf(c, "build %d on %s master %s had weird logs\n", b.N umber, b.Builder, b.Master)
88 }
89
90 userStatus := "UNKNOWN"
91 if len(b.Text) < 2 {
92 log.Warningf(c, "build %d on %s master %s had strange text\n", b .Number, b.Builder, b.Master)
93 } else {
94 if b.Text[0] == "exception" {
95 userStatus = "EXCEPTION"
96 } else if b.Text[0] == "failed" {
97 userStatus = "FAILURE"
98 } else if b.Text[1] == "successful" {
99 userStatus = "SUCCESS"
100 }
101 if userStatus == "UNKNOWN" {
102 log.Warningf(c, "build %d on %s master %s had unknown te xt %v\n", b.Number, b.Builder, b.Master, b.Text)
103 }
104 }
105
106 return &model.Build{
107 ExecutionTime: model.TimeID{time.Unix(int64(b.Times[0]), 0).UTC( )},
108 BuildRoot: model.GetBuildRoot(c, b.Master, b.Builder).Key,
109 BuildLogKey: logKey,
110 Revisions: revisions,
111 UserStatus: userStatus,
112 }
113 }
114
115 type builderJSON struct {
116 Builds []int `json:"cachedBuilds"`
117 }
118
119 type masterJSON struct {
120 Builders map[string]builderJSON `json:"builders"`
121 }
122
123 func getBuild(master, builder string, build int, useCBE bool) (*buildJSON, error ) {
124 resp := (*http.Response)(nil)
125 err := (error)(nil)
126 if useCBE {
127 resp, err = http.Get(strings.Join(
128 []string{BuildExtractURL, "p", master, "builders", build er, "builds", fmt.Sprintf("%d", build)},
129 "/") + "?json=1")
130 } else {
131 resp, err = http.Get(strings.Join(
132 []string{"https://build.chromium.org", "p", master, "jso n", "builders", builder, "builds", fmt.Sprintf("%d", build)},
133 "/"))
134 }
135
136 if err != nil {
137 return nil, err
138 }
139 defer resp.Body.Close()
140
141 dec := json.NewDecoder(resp.Body)
142 doneBuild := &buildJSON{}
143 err = dec.Decode(doneBuild)
144 if err != nil {
145 return nil, err
146 }
147
148 if doneBuild.Error != "" {
149 return nil, fmt.Errorf("error while getting Build for master %s builder %s build number %d: %s", master, builder, build, doneBuild.Error)
150 }
151 return doneBuild, nil
152 }
153
154 // PopulateMaster puts the data for a master into the datastore.
155 //
156 // It hits CBE to grab the json, and then creates corresponding entities and
157 // puts them into the datastore. It assumes that a datastore impl exists in the
158 // supplied context.
159 func PopulateMaster(c context.Context, master string, dryRun, buildbotFallback b ool) error {
160 masterJSON := &masterJSON{}
161 resp, err := http.Get(strings.Join([]string{BuildExtractURL, "get_master ", master}, "/"))
162 if err != nil {
163 return err
164 }
165
166 defer resp.Body.Close()
167 dec := json.NewDecoder(resp.Body)
168 err = dec.Decode(masterJSON)
169 if err != nil {
170 return err
171 }
172
173 buildsToPut := make(map[string][]*model.Build)
174 log.Infof(c, "Getting builds for master %s\n", master)
175
176 errors := parallel.WorkPool(workPoolSizeMultiplier*len(masterJSON.Builde rs), func(ch chan<- func() error) {
177 for builderName, builder := range masterJSON.Builders {
178 if len(builder.Builds) > 0 {
179 buildsToPut[builderName] = make([]*model.Build, len(builder.Builds))
180
181 for ind, builderNum := range builder.Builds {
182 ind := ind
183 builderNum := builderNum
184 builderName := builderName
185
186 ch <- func() error {
187 res, err := getBuild(master, bui lderName, builderNum, true)
188 if err != nil {
189 if !buildbotFallback {
190 log.Warningf(c, "got %s, giving up", err)
191 return err
192 }
193
194 log.Warningf(c, "got %s, retrying without CBE", err)
195 res, err = getBuild(mast er, builderName, builderNum, false)
196
197 if err != nil {
198 log.Warningf(c, "got %s, giving up", err)
199 return err
200 }
201 }
202
203 conv := res.toDS(c)
204 buildsToPut[builderName][ind] = conv
205 return nil
206 }
207 }
208 log.Infof(c, "Queued builds for builder %s.", bu ilderName)
209 }
210 }
211 })
212
213 // We ignore these warnings because, for some reason, CBE sometimes retu rns
214 // 404s for some builds. So we ignore those, and just move on. They're
215 // printed to the user above, so if something is really wrong all those
216 // errors will show up.
217 log.Warningf(c, "Done fetching builds. errors: %s.\n", errors)
218
219 for name, builds := range buildsToPut {
220 num := 0
221 for _, b := range builds {
222 if b != nil {
223 num++
224 }
225 }
226
227 if num == 0 {
228 buildsToPut[name] = nil
229 continue
230 }
231
232 newBuilds := make([]*model.Build, num)
233 ind := 0
234 for _, b := range builds {
235 if b != nil {
236 newBuilds[ind] = b
237 ind++
238 }
239 }
240 buildsToPut[name] = newBuilds
241 }
242
243 ds := datastore.Get(c)
244 for builderName, toPut := range buildsToPut {
245 log.Infof(c, "%d to put for builder %s\n", len(toPut), builderNa me)
246 if toPut != nil {
247 if dryRun {
248 log.Infof(c, "dry run, not putting any modificat ions")
249 } else {
250 ds.Put(toPut)
251 }
252 }
253 }
254 return nil
255 }
OLDNEW
« appengine/cmd/milo/cmd/backfill/main.go ('K') | « appengine/cmd/milo/cmd/backfill/main.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698