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

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

Issue 2271453002: Milo: Internal buildbot masters support (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: Small changes Created 4 years, 3 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
« no previous file with comments | « no previous file | milo/appengine/buildbot/build_test.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" 8 "encoding/json"
9 "fmt" 9 "fmt"
10 "io/ioutil" 10 "io/ioutil"
11 "net/http" 11 "net/http"
12 "os"
13 "path/filepath" 12 "path/filepath"
14 "regexp" 13 "regexp"
15 "sort" 14 "sort"
16 "strconv" 15 "strconv"
17 "strings" 16 "strings"
18 "time" 17 "time"
19 18
20 "github.com/luci/gae/service/datastore" 19 "github.com/luci/gae/service/datastore"
21 » "github.com/luci/gae/service/memcache" 20 » "github.com/luci/luci-go/common/logging"
22 » log "github.com/luci/luci-go/common/logging"
23 "github.com/luci/luci-go/milo/api/resp" 21 "github.com/luci/luci-go/milo/api/resp"
24 » "github.com/luci/luci-go/server/auth" 22 » "github.com/luci/luci-go/milo/appengine/settings"
23 » "github.com/luci/luci-go/milo/common/miloerror"
25 "golang.org/x/net/context" 24 "golang.org/x/net/context"
26 ) 25 )
27 26
28 func getURL(c context.Context, URL string) ([]byte, error) { 27 var errBuildNotFound = miloerror.Error{
29 » fmt.Fprintf(os.Stderr, "Fetching %s\n", URL) 28 » Message: "Build not found",
30 » tr, err := auth.GetRPCTransport(c, auth.NoAuth) 29 » Code: http.StatusNotFound,
31 » if err != nil {
32 » » return nil, err
33 » }
34 » client := http.Client{Transport: tr}
35 » resp, err := client.Get(URL)
36 » if err != nil {
37 » » return nil, err
38 » }
39 » if resp.StatusCode != 200 {
40 » » return nil, fmt.Errorf("Failed to fetch %s, status code %d", URL , resp.StatusCode)
41 » }
42 » defer resp.Body.Close()
43 » return ioutil.ReadAll(resp.Body)
44 } 30 }
45 31
46 // getBuild fetches a build from BuildBot. 32 // getBuild fetches a buildbot build from the datastore and checks ACLs.
47 func getBuild(c context.Context, master, builder, buildNum string) (*buildbotBui ld, error) { 33 func getBuild(c context.Context, master, builder, buildNum string) (*buildbotBui ld, error) {
48 result := &buildbotBuild{ 34 result := &buildbotBuild{
49 Master: master, 35 Master: master,
50 Buildername: builder, 36 Buildername: builder,
51 } 37 }
52 if num, err := strconv.Atoi(buildNum); err != nil { 38 if num, err := strconv.Atoi(buildNum); err != nil {
53 » » panic(fmt.Errorf("%s does not look like a number", buildNum)) 39 » » return nil, miloerror.Error{
40 » » » Message: fmt.Sprintf("%s does not look like a number", b uildNum),
41 » » » Code: http.StatusBadRequest,
42 » » }
54 } else { 43 } else {
55 result.Number = num 44 result.Number = num
56 } 45 }
57 // Check memcache first.
58 mc := memcache.Get(c)
59 if item, err := mc.Get(result.getID()); err == nil {
60 log.Debugf(c, "Found in Memcache!")
61 json.Unmarshal(item.Value(), result)
62 return result, nil
63 }
64 // Then check local datastore.
65 ds := datastore.Get(c) 46 ds := datastore.Get(c)
66 err := ds.Get(result) 47 err := ds.Get(result)
67 » if err == nil { 48 » switch err {
Vadim Sh. 2016/08/23 22:35:46 nit: I usually do switch { case err == datastor
Ryan Tseng 2016/08/24 21:07:24 Done.
68 » » log.Debugf(c, "Found build in local cache!") 49 » case nil:
69 » » if result.Times[1] != nil { 50 » case datastore.ErrNoSuchEntity:
70 » » » bs, ierr := json.Marshal(result) 51 » » return nil, errBuildNotFound
71 » » » if ierr == nil { 52 » default:
72 » » » » item := mc.NewItem(result.getID()).SetValue(bs)
73 » » » » mc.Set(item)
74 » » » }
75 » » }
76 » » return result, nil
77 » }
78 » log.Debugf(c, "Could not find log in local cache: %s", err.Error())
79 » // Now check CBE.
80 » cbeURL := fmt.Sprintf(
81 » » "https://chrome-build-extract.appspot.com/p/%s/builders/%s/build s/%s?json=1",
82 » » master, builder, buildNum)
83 » if buf, err := getURL(c, cbeURL); err == nil {
84 » » return result, json.Unmarshal(buf, result)
85 » }
86 » // TODO(hinoka): Cache finished builds.
87 » url := fmt.Sprintf(
88 » » "https://build.chromium.org/p/%s/json/builders/%s/builds/%s", ma ster, builder, buildNum)
89 » buf, err := getURL(c, url)
90 » if err != nil {
91 return nil, err 53 return nil, err
92 } 54 }
55 if result.Internal {
56 allowed, err := settings.IsAllowedInternal(c)
57 if err != nil {
58 return nil, err
59 }
60 if !allowed {
61 return nil, errBuildNotFound
62 }
63 }
93 64
94 » return result, json.Unmarshal(buf, result) 65 » return result, nil
95 } 66 }
96 67
97 // result2Status translates a buildbot result integer into a resp.Status. 68 // result2Status translates a buildbot result integer into a resp.Status.
98 func result2Status(s *int) (status resp.Status) { 69 func result2Status(s *int) (status resp.Status) {
99 if s == nil { 70 if s == nil {
100 return resp.Running 71 return resp.Running
101 } 72 }
102 switch *s { 73 switch *s {
103 case 0: 74 case 0:
104 status = resp.Success 75 status = resp.Success
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 // getBanner fetches the banner information about the bot that the build 116 // getBanner fetches the banner information about the bot that the build
146 // ran on. This is best effort and: 117 // ran on. This is best effort and:
147 // * Will return an empty list of banners if the master is not found. 118 // * Will return an empty list of banners if the master is not found.
148 // * May return incorrect data if the platform of the slave of the same name 119 // * May return incorrect data if the platform of the slave of the same name
149 // was changed recently, and this build was older than before the slave 120 // was changed recently, and this build was older than before the slave
150 // changed platforms. 121 // changed platforms.
151 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner { 122 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner {
152 logos := &resp.LogoBanner{} 123 logos := &resp.LogoBanner{}
153 // Fetch the master info from datastore if not provided. 124 // Fetch the master info from datastore if not provided.
154 if m == nil { 125 if m == nil {
155 » » m1, i, _, err := getMasterJSON(c, b.Master) 126 » » m1, _, _, err := getMasterJSON(c, b.Master)
156 m = m1 127 m = m1
157 if i {
158 // TODO(hinoka): Support internal masters.
159 return nil
160 }
161 if err != nil { 128 if err != nil {
162 » » » log.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master) 129 » » » logging.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master)
163 return nil 130 return nil
164 } 131 }
165 } 132 }
166 133
167 s, ok := m.Slaves[b.Slave] 134 s, ok := m.Slaves[b.Slave]
168 if !ok { 135 if !ok {
169 » » log.Warningf(c, "Could not find slave %s in master %s", b.Slave, b.Master) 136 » » logging.Warningf(c, "Could not find slave %s in master %s", b.Sl ave, b.Master)
170 return nil 137 return nil
171 } 138 }
172 hostInfo := map[string]string{} 139 hostInfo := map[string]string{}
173 for _, v := range strings.Split(s.Host, "\n") { 140 for _, v := range strings.Split(s.Host, "\n") {
174 if info := strings.SplitN(v, ":", 2); len(info) == 2 { 141 if info := strings.SplitN(v, ":", 2); len(info) == 2 {
175 hostInfo[info[0]] = strings.TrimSpace(info[1]) 142 hostInfo[info[0]] = strings.TrimSpace(info[1])
176 } 143 }
177 } 144 }
178 145
179 // Extract OS and OS Family 146 // Extract OS and OS Family
(...skipping 15 matching lines...) Expand all
195 result.LogoBase = resp.Ubuntu 162 result.LogoBase = resp.Ubuntu
196 default: 163 default:
197 return nil 164 return nil
198 } 165 }
199 result.Subtitle = version 166 result.Subtitle = version
200 return result 167 return result
201 }() 168 }()
202 if osLogo != nil { 169 if osLogo != nil {
203 logos.OS = append(logos.OS, *osLogo) 170 logos.OS = append(logos.OS, *osLogo)
204 } else { 171 } else {
205 » » log.Warningf(c, "No OS info found. Host: %s", s.Host) 172 » » logging.Warningf(c, "No OS info found. Host: %s", s.Host)
206 } 173 }
207 » log.Infof(c, "OS: %s/%s", os, version) 174 » logging.Infof(c, "OS: %s/%s", os, version)
208 return logos 175 return logos
209 } 176 }
210 177
211 // summary Extract the top level summary from a buildbot build as a 178 // summary Extract the top level summary from a buildbot build as a
212 // BuildComponent 179 // BuildComponent
213 func summary(c context.Context, b *buildbotBuild) resp.BuildComponent { 180 func summary(c context.Context, b *buildbotBuild) resp.BuildComponent {
214 // TODO(hinoka): use b.toStatus() 181 // TODO(hinoka): use b.toStatus()
215 // Status 182 // Status
216 var status resp.Status 183 var status resp.Status
217 if b.Currentstep != nil { 184 if b.Currentstep != nil {
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 bc.Status = resp.Running 250 bc.Status = resp.Running
284 } else { 251 } else {
285 if len(step.Results) > 0 { 252 if len(step.Results) > 0 {
286 status := int(step.Results[0].(float64)) 253 status := int(step.Results[0].(float64))
287 bc.Status = result2Status(&status) 254 bc.Status = result2Status(&status)
288 } else { 255 } else {
289 bc.Status = resp.Success 256 bc.Status = resp.Success
290 } 257 }
291 } 258 }
292 259
293 » » // Now, link to the logs. 260 » » // Now, link to the loggings.
294 » » for _, log := range step.Logs { 261 » » for _, l := range step.Logs {
295 link := &resp.Link{ 262 link := &resp.Link{
296 » » » » Label: log[0], 263 » » » » Label: l[0],
297 » » » » URL: log[1], 264 » » » » URL: l[1],
298 } 265 }
299 if link.Label == "stdio" { 266 if link.Label == "stdio" {
300 bc.MainLink = link 267 bc.MainLink = link
301 } else { 268 } else {
302 bc.SubLink = append(bc.SubLink, link) 269 bc.SubLink = append(bc.SubLink, link)
303 } 270 }
304 } 271 }
305 272
306 // Figure out the times 273 // Figure out the times
307 bc.Started, bc.Finished, bc.Duration = parseTimes(step.Times) 274 bc.Started, bc.Finished, bc.Duration = parseTimes(step.Times)
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 issue := int64(-1) 409 issue := int64(-1)
443 // TODO(hinoka): Gerrit URLs. 410 // TODO(hinoka): Gerrit URLs.
444 for _, prop := range b.Properties { 411 for _, prop := range b.Properties {
445 key := prop[0].(string) 412 key := prop[0].(string)
446 value := prop[1] 413 value := prop[1]
447 switch key { 414 switch key {
448 case "rietveld": 415 case "rietveld":
449 if v, ok := value.(string); ok { 416 if v, ok := value.(string); ok {
450 rietveld = v 417 rietveld = v
451 } else { 418 } else {
452 » » » » log.Warningf(c, "Field rietveld is not a string: %#v", value) 419 » » » » logging.Warningf(c, "Field rietveld is not a str ing: %#v", value)
453 } 420 }
454 case "issue": 421 case "issue":
455 if v, ok := value.(float64); ok { 422 if v, ok := value.(float64); ok {
456 issue = int64(v) 423 issue = int64(v)
457 } else { 424 } else {
458 » » » » log.Warningf(c, "Field issue is not a float: %#v ", value) 425 » » » » logging.Warningf(c, "Field issue is not a float: %#v", value)
459 } 426 }
460 427
461 case "got_revision": 428 case "got_revision":
462 if v, ok := value.(string); ok { 429 if v, ok := value.(string); ok {
463 ss.Revision = v 430 ss.Revision = v
464 } else { 431 } else {
465 » » » » log.Warningf(c, "Field got_revision is not a str ing: %#v", value) 432 » » » » logging.Warningf(c, "Field got_revision is not a string: %#v", value)
466 } 433 }
467 434
468 } 435 }
469 } 436 }
470 if issue != -1 { 437 if issue != -1 {
471 if rietveld != "" { 438 if rietveld != "" {
472 rietveld = strings.TrimRight(rietveld, "/") 439 rietveld = strings.TrimRight(rietveld, "/")
473 ss.Changelist = &resp.Link{ 440 ss.Changelist = &resp.Link{
474 Label: fmt.Sprintf("Issue %d", issue), 441 Label: fmt.Sprintf("Issue %d", issue),
475 URL: fmt.Sprintf("%s/%d", rietveld, issue), 442 URL: fmt.Sprintf("%s/%d", rietveld, issue),
476 } 443 }
477 } else { 444 } else {
478 » » » log.Warningf(c, "Found issue but not rietveld property." ) 445 » » » logging.Warningf(c, "Found issue but not rietveld proper ty.")
479 } 446 }
480 } 447 }
481 return ss 448 return ss
482 } 449 }
483 450
484 func getDebugBuild(c context.Context, builder, buildNum string) (*buildbotBuild, error) { 451 func getDebugBuild(c context.Context, builder, buildNum string) (*buildbotBuild, error) {
485 fname := fmt.Sprintf("%s.%s.json", builder, buildNum) 452 fname := fmt.Sprintf("%s.%s.json", builder, buildNum)
486 // ../buildbot below assumes that 453 // ../buildbot below assumes that
487 // - this code is not executed by tests outside of this dir 454 // - this code is not executed by tests outside of this dir
488 // - this dir is a sibling of frontend dir 455 // - this dir is a sibling of frontend dir
(...skipping 21 matching lines...) Expand all
510 477
511 // TODO(hinoka): Do all fields concurrently. 478 // TODO(hinoka): Do all fields concurrently.
512 return &resp.MiloBuild{ 479 return &resp.MiloBuild{
513 SourceStamp: sourcestamp(c, b), 480 SourceStamp: sourcestamp(c, b),
514 Summary: summary(c, b), 481 Summary: summary(c, b),
515 Components: components(b), 482 Components: components(b),
516 PropertyGroup: properties(b), 483 PropertyGroup: properties(b),
517 Blame: blame(b), 484 Blame: blame(b),
518 }, nil 485 }, nil
519 } 486 }
OLDNEW
« no previous file with comments | « no previous file | milo/appengine/buildbot/build_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698