Chromium Code Reviews| OLD | NEW |
|---|---|
| 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" | |
| 22 log "github.com/luci/luci-go/common/logging" | 20 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 build from BuildBot. |
|
Vadim Sh.
2016/08/23 18:53:06
... and checks ACL.
Ryan Tseng
2016/08/23 22:00:15
Done.
| |
| 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 panic(fmt.Errorf("%s does not look like a number", buildNum)) |
| 54 } else { | 40 } else { |
| 55 result.Number = num | 41 result.Number = num |
| 56 } | 42 } |
| 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) | 43 ds := datastore.Get(c) |
| 66 err := ds.Get(result) | 44 err := ds.Get(result) |
| 67 » if err == nil { | 45 » if err != nil { |
| 68 » » log.Debugf(c, "Found build in local cache!") | 46 » » return nil, errBuildNotFound |
|
Vadim Sh.
2016/08/23 18:53:06
It is important to distinguish transient errors fr
Ryan Tseng
2016/08/23 22:00:14
Done.
| |
| 69 » » if result.Times[1] != nil { | 47 » } |
| 70 » » » bs, ierr := json.Marshal(result) | 48 » // If the build is internal, check to see if the user has access. We us e |
| 71 » » » if ierr == nil { | 49 » // the magic project name "buildbot-internal" for membership check. |
| 72 » » » » item := mc.NewItem(result.getID()).SetValue(bs) | 50 » if result.Internal { |
| 73 » » » » mc.Set(item) | 51 » » allowed, err := settings.IsAllowed(c, "buildbot-internal") |
|
Vadim Sh.
2016/08/23 18:53:06
since we are hardcoding magic name anyway, perhaps
Ryan Tseng
2016/08/23 22:00:14
Done.
| |
| 74 » » » } | 52 » » if err != nil { |
| 53 » » » log.WithError(err).Errorf(c, | |
| 54 » » » » "Encountered error while checking buildbot membe rship, returning 404") | |
| 55 » » » return nil, errBuildNotFound | |
|
Vadim Sh.
2016/08/23 18:53:06
same here, it should be HTTP 500, not HTTP 404.
Ryan Tseng
2016/08/23 22:00:15
Done.
| |
| 75 } | 56 } |
| 76 » » return result, nil | 57 » » if !allowed { |
| 77 » } | 58 » » » return nil, errBuildNotFound |
| 78 » log.Debugf(c, "Could not find log in local cache: %s", err.Error()) | 59 » » } |
| 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 | |
| 92 } | 60 } |
| 93 | 61 |
| 94 » return result, json.Unmarshal(buf, result) | 62 » return result, nil |
| 95 } | 63 } |
| 96 | 64 |
| 97 // result2Status translates a buildbot result integer into a resp.Status. | 65 // result2Status translates a buildbot result integer into a resp.Status. |
| 98 func result2Status(s *int) (status resp.Status) { | 66 func result2Status(s *int) (status resp.Status) { |
| 99 if s == nil { | 67 if s == nil { |
| 100 return resp.Running | 68 return resp.Running |
| 101 } | 69 } |
| 102 switch *s { | 70 switch *s { |
| 103 case 0: | 71 case 0: |
| 104 status = resp.Success | 72 status = resp.Success |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 145 // getBanner fetches the banner information about the bot that the build | 113 // getBanner fetches the banner information about the bot that the build |
| 146 // ran on. This is best effort and: | 114 // ran on. This is best effort and: |
| 147 // * Will return an empty list of banners if the master is not found. | 115 // * 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 | 116 // * 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 | 117 // was changed recently, and this build was older than before the slave |
| 150 // changed platforms. | 118 // changed platforms. |
| 151 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner { | 119 func getBanner(c context.Context, b *buildbotBuild, m *buildbotMaster) *resp.Log oBanner { |
| 152 logos := &resp.LogoBanner{} | 120 logos := &resp.LogoBanner{} |
| 153 // Fetch the master info from datastore if not provided. | 121 // Fetch the master info from datastore if not provided. |
| 154 if m == nil { | 122 if m == nil { |
| 155 » » m1, i, _, err := getMasterJSON(c, b.Master) | 123 » » m1, _, _, err := getMasterJSON(c, b.Master) |
| 156 m = m1 | 124 m = m1 |
| 157 if i { | |
| 158 // TODO(hinoka): Support internal masters. | |
| 159 return nil | |
| 160 } | |
| 161 if err != nil { | 125 if err != nil { |
| 162 log.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master) | 126 log.Warningf(c, "Failed to fetch master information for banners on master %s", b.Master) |
| 163 return nil | 127 return nil |
| 164 } | 128 } |
| 165 } | 129 } |
| 166 | 130 |
| 167 s, ok := m.Slaves[b.Slave] | 131 s, ok := m.Slaves[b.Slave] |
| 168 if !ok { | 132 if !ok { |
| 169 log.Warningf(c, "Could not find slave %s in master %s", b.Slave, b.Master) | 133 log.Warningf(c, "Could not find slave %s in master %s", b.Slave, b.Master) |
| 170 return nil | 134 return nil |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 510 | 474 |
| 511 // TODO(hinoka): Do all fields concurrently. | 475 // TODO(hinoka): Do all fields concurrently. |
| 512 return &resp.MiloBuild{ | 476 return &resp.MiloBuild{ |
| 513 SourceStamp: sourcestamp(c, b), | 477 SourceStamp: sourcestamp(c, b), |
| 514 Summary: summary(c, b), | 478 Summary: summary(c, b), |
| 515 Components: components(b), | 479 Components: components(b), |
| 516 PropertyGroup: properties(b), | 480 PropertyGroup: properties(b), |
| 517 Blame: blame(b), | 481 Blame: blame(b), |
| 518 }, nil | 482 }, nil |
| 519 } | 483 } |
| OLD | NEW |