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

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: nit fix 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 {
68 » » log.Debugf(c, "Found build in local cache!") 49 » case err == datastore.ErrNoSuchEntity:
69 » » if result.Times[1] != nil { 50 » » return nil, errBuildNotFound
70 » » » bs, ierr := json.Marshal(result) 51 » case err != nil:
71 » » » if ierr == nil {
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 52 return nil, err
92 } 53 }
54 if result.Internal {
55 allowed, err := settings.IsAllowedInternal(c)
56 if err != nil {
57 return nil, err
58 }
59 if !allowed {
60 return nil, errBuildNotFound
61 }
62 }
93 63
94 » return result, json.Unmarshal(buf, result) 64 » return result, nil
95 } 65 }
96 66
97 // result2Status translates a buildbot result integer into a resp.Status. 67 // result2Status translates a buildbot result integer into a resp.Status.
98 func result2Status(s *int) (status resp.Status) { 68 func result2Status(s *int) (status resp.Status) {
99 if s == nil { 69 if s == nil {
100 return resp.Running 70 return resp.Running
101 } 71 }
102 switch *s { 72 switch *s {
103 case 0: 73 case 0:
104 status = resp.Success 74 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 115 // getBanner fetches the banner information about the bot that the build
146 // ran on. This is best effort and: 116 // ran on. This is best effort and:
147 // * Will return an empty list of banners if the master is not found. 117 // * 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 118 // * 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 119 // was changed recently, and this build was older than before the slave
150 // changed platforms. 120 // changed platforms.
151 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner { 121 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner {
152 logos := &resp.LogoBanner{} 122 logos := &resp.LogoBanner{}
153 // Fetch the master info from datastore if not provided. 123 // Fetch the master info from datastore if not provided.
154 if m == nil { 124 if m == nil {
155 » » m1, i, _, err := getMasterJSON(c, b.Master) 125 » » m1, _, _, err := getMasterJSON(c, b.Master)
156 m = m1 126 m = m1
157 if i {
158 // TODO(hinoka): Support internal masters.
159 return nil
160 }
161 if err != nil { 127 if err != nil {
162 » » » log.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master) 128 » » » logging.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master)
163 return nil 129 return nil
164 } 130 }
165 } 131 }
166 132
167 s, ok := m.Slaves[b.Slave] 133 s, ok := m.Slaves[b.Slave]
168 if !ok { 134 if !ok {
169 » » log.Warningf(c, "Could not find slave %s in master %s", b.Slave, b.Master) 135 » » logging.Warningf(c, "Could not find slave %s in master %s", b.Sl ave, b.Master)
170 return nil 136 return nil
171 } 137 }
172 hostInfo := map[string]string{} 138 hostInfo := map[string]string{}
173 for _, v := range strings.Split(s.Host, "\n") { 139 for _, v := range strings.Split(s.Host, "\n") {
174 if info := strings.SplitN(v, ":", 2); len(info) == 2 { 140 if info := strings.SplitN(v, ":", 2); len(info) == 2 {
175 hostInfo[info[0]] = strings.TrimSpace(info[1]) 141 hostInfo[info[0]] = strings.TrimSpace(info[1])
176 } 142 }
177 } 143 }
178 144
179 // Extract OS and OS Family 145 // Extract OS and OS Family
(...skipping 15 matching lines...) Expand all
195 result.LogoBase = resp.Ubuntu 161 result.LogoBase = resp.Ubuntu
196 default: 162 default:
197 return nil 163 return nil
198 } 164 }
199 result.Subtitle = version 165 result.Subtitle = version
200 return result 166 return result
201 }() 167 }()
202 if osLogo != nil { 168 if osLogo != nil {
203 logos.OS = append(logos.OS, *osLogo) 169 logos.OS = append(logos.OS, *osLogo)
204 } else { 170 } else {
205 » » log.Warningf(c, "No OS info found. Host: %s", s.Host) 171 » » logging.Warningf(c, "No OS info found. Host: %s", s.Host)
206 } 172 }
207 » log.Infof(c, "OS: %s/%s", os, version) 173 » logging.Infof(c, "OS: %s/%s", os, version)
208 return logos 174 return logos
209 } 175 }
210 176
211 // summary Extract the top level summary from a buildbot build as a 177 // summary Extract the top level summary from a buildbot build as a
212 // BuildComponent 178 // BuildComponent
213 func summary(c context.Context, b *buildbotBuild) resp.BuildComponent { 179 func summary(c context.Context, b *buildbotBuild) resp.BuildComponent {
214 // TODO(hinoka): use b.toStatus() 180 // TODO(hinoka): use b.toStatus()
215 // Status 181 // Status
216 var status resp.Status 182 var status resp.Status
217 if b.Currentstep != nil { 183 if b.Currentstep != nil {
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 bc.Status = resp.Running 249 bc.Status = resp.Running
284 } else { 250 } else {
285 if len(step.Results) > 0 { 251 if len(step.Results) > 0 {
286 status := int(step.Results[0].(float64)) 252 status := int(step.Results[0].(float64))
287 bc.Status = result2Status(&status) 253 bc.Status = result2Status(&status)
288 } else { 254 } else {
289 bc.Status = resp.Success 255 bc.Status = resp.Success
290 } 256 }
291 } 257 }
292 258
293 » » // Now, link to the logs. 259 » » // Now, link to the loggings.
294 » » for _, log := range step.Logs { 260 » » for _, l := range step.Logs {
295 link := &resp.Link{ 261 link := &resp.Link{
296 » » » » Label: log[0], 262 » » » » Label: l[0],
297 » » » » URL: log[1], 263 » » » » URL: l[1],
298 } 264 }
299 if link.Label == "stdio" { 265 if link.Label == "stdio" {
300 bc.MainLink = link 266 bc.MainLink = link
301 } else { 267 } else {
302 bc.SubLink = append(bc.SubLink, link) 268 bc.SubLink = append(bc.SubLink, link)
303 } 269 }
304 } 270 }
305 271
306 // Figure out the times 272 // Figure out the times
307 bc.Started, bc.Finished, bc.Duration = parseTimes(step.Times) 273 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) 408 issue := int64(-1)
443 // TODO(hinoka): Gerrit URLs. 409 // TODO(hinoka): Gerrit URLs.
444 for _, prop := range b.Properties { 410 for _, prop := range b.Properties {
445 key := prop[0].(string) 411 key := prop[0].(string)
446 value := prop[1] 412 value := prop[1]
447 switch key { 413 switch key {
448 case "rietveld": 414 case "rietveld":
449 if v, ok := value.(string); ok { 415 if v, ok := value.(string); ok {
450 rietveld = v 416 rietveld = v
451 } else { 417 } else {
452 » » » » log.Warningf(c, "Field rietveld is not a string: %#v", value) 418 » » » » logging.Warningf(c, "Field rietveld is not a str ing: %#v", value)
453 } 419 }
454 case "issue": 420 case "issue":
455 if v, ok := value.(float64); ok { 421 if v, ok := value.(float64); ok {
456 issue = int64(v) 422 issue = int64(v)
457 } else { 423 } else {
458 » » » » log.Warningf(c, "Field issue is not a float: %#v ", value) 424 » » » » logging.Warningf(c, "Field issue is not a float: %#v", value)
459 } 425 }
460 426
461 case "got_revision": 427 case "got_revision":
462 if v, ok := value.(string); ok { 428 if v, ok := value.(string); ok {
463 ss.Revision = v 429 ss.Revision = v
464 } else { 430 } else {
465 » » » » log.Warningf(c, "Field got_revision is not a str ing: %#v", value) 431 » » » » logging.Warningf(c, "Field got_revision is not a string: %#v", value)
466 } 432 }
467 433
468 } 434 }
469 } 435 }
470 if issue != -1 { 436 if issue != -1 {
471 if rietveld != "" { 437 if rietveld != "" {
472 rietveld = strings.TrimRight(rietveld, "/") 438 rietveld = strings.TrimRight(rietveld, "/")
473 ss.Changelist = &resp.Link{ 439 ss.Changelist = &resp.Link{
474 Label: fmt.Sprintf("Issue %d", issue), 440 Label: fmt.Sprintf("Issue %d", issue),
475 URL: fmt.Sprintf("%s/%d", rietveld, issue), 441 URL: fmt.Sprintf("%s/%d", rietveld, issue),
476 } 442 }
477 } else { 443 } else {
478 » » » log.Warningf(c, "Found issue but not rietveld property." ) 444 » » » logging.Warningf(c, "Found issue but not rietveld proper ty.")
479 } 445 }
480 } 446 }
481 return ss 447 return ss
482 } 448 }
483 449
484 func getDebugBuild(c context.Context, builder, buildNum string) (*buildbotBuild, error) { 450 func getDebugBuild(c context.Context, builder, buildNum string) (*buildbotBuild, error) {
485 fname := fmt.Sprintf("%s.%s.json", builder, buildNum) 451 fname := fmt.Sprintf("%s.%s.json", builder, buildNum)
486 // ../buildbot below assumes that 452 // ../buildbot below assumes that
487 // - this code is not executed by tests outside of this dir 453 // - this code is not executed by tests outside of this dir
488 // - this dir is a sibling of frontend dir 454 // - this dir is a sibling of frontend dir
(...skipping 21 matching lines...) Expand all
510 476
511 // TODO(hinoka): Do all fields concurrently. 477 // TODO(hinoka): Do all fields concurrently.
512 return &resp.MiloBuild{ 478 return &resp.MiloBuild{
513 SourceStamp: sourcestamp(c, b), 479 SourceStamp: sourcestamp(c, b),
514 Summary: summary(c, b), 480 Summary: summary(c, b),
515 Components: components(b), 481 Components: components(b),
516 PropertyGroup: properties(b), 482 PropertyGroup: properties(b),
517 Blame: blame(b), 483 Blame: blame(b),
518 }, nil 484 }, nil
519 } 485 }
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