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

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

Issue 2765383002: Milo: Move instance configuration to luci-config (Closed)
Patch Set: Review Created 3 years, 8 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 | « milo/appengine/buildbucket/buckets.go ('k') | milo/appengine/buildbucket/builder_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 buildbucket 5 package buildbucket
6 6
7 import ( 7 import (
8 "encoding/json" 8 "encoding/json"
9 "fmt" 9 "fmt"
10 "net/url" 10 "net/url"
11 "os" 11 "os"
12 "path" 12 "path"
13 "path/filepath" 13 "path/filepath"
14 "strconv" 14 "strconv"
15 "strings" 15 "strings"
16 "time" 16 "time"
17 17
18 "golang.org/x/net/context" 18 "golang.org/x/net/context"
19 "google.golang.org/api/googleapi" 19 "google.golang.org/api/googleapi"
20 20
21 "github.com/luci/luci-go/common/api/buildbucket/buildbucket/v1" 21 "github.com/luci/luci-go/common/api/buildbucket/buildbucket/v1"
22 "github.com/luci/luci-go/common/clock" 22 "github.com/luci/luci-go/common/clock"
23 "github.com/luci/luci-go/common/errors" 23 "github.com/luci/luci-go/common/errors"
24 » log "github.com/luci/luci-go/common/logging" 24 » "github.com/luci/luci-go/common/logging"
25 "github.com/luci/luci-go/common/retry" 25 "github.com/luci/luci-go/common/retry"
26 "github.com/luci/luci-go/common/sync/parallel" 26 "github.com/luci/luci-go/common/sync/parallel"
27 "github.com/luci/luci-go/milo/api/resp" 27 "github.com/luci/luci-go/milo/api/resp"
28 "github.com/luci/luci-go/milo/appengine/common"
28 ) 29 )
29 30
30 // search executes the search request with retries and exponential back-off. 31 // search executes the search request with retries and exponential back-off.
31 func search(c context.Context, client *buildbucket.Service, req *buildbucket.Sea rchCall) ( 32 func search(c context.Context, client *buildbucket.Service, req *buildbucket.Sea rchCall) (
32 *buildbucket.ApiSearchResponseMessage, error) { 33 *buildbucket.ApiSearchResponseMessage, error) {
33 34
34 var res *buildbucket.ApiSearchResponseMessage 35 var res *buildbucket.ApiSearchResponseMessage
35 err := retry.Retry( 36 err := retry.Retry(
36 c, 37 c,
37 retry.TransientOnly(retry.Default), 38 retry.TransientOnly(retry.Default),
38 func() error { 39 func() error {
39 var err error 40 var err error
40 res, err = req.Do() 41 res, err = req.Do()
41 if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Co de >= 500 { 42 if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Co de >= 500 {
42 err = errors.WrapTransient(apiErr) 43 err = errors.WrapTransient(apiErr)
43 } 44 }
44 return err 45 return err
45 }, 46 },
46 func(err error, wait time.Duration) { 47 func(err error, wait time.Duration) {
47 » » » log.WithError(err).Warningf(c, "buildbucket search reque st failed transiently, will retry in %s", wait) 48 » » » logging.WithError(err).Warningf(c, "buildbucket search r equest failed transiently, will retry in %s", wait)
48 }) 49 })
49 return res, err 50 return res, err
50 } 51 }
51 52
52 // fetchBuilds fetches builds given a criteria. 53 // fetchBuilds fetches builds given a criteria.
53 // The returned builds are sorted by build creation descending. 54 // The returned builds are sorted by build creation descending.
54 // count defines maximum number of builds to fetch; if <0, defaults to 100. 55 // count defines maximum number of builds to fetch; if <0, defaults to 100.
55 func fetchBuilds(c context.Context, client *buildbucket.Service, bucket, builder , 56 func fetchBuilds(c context.Context, client *buildbucket.Service, bucket, builder ,
56 status string, count int) ([]*buildbucket.ApiBuildMessage, error) { 57 status string, count int) ([]*buildbucket.ApiBuildMessage, error) {
57 58
(...skipping 19 matching lines...) Expand all
77 return fetched, fmt.Errorf(res.Error.Message) 78 return fetched, fmt.Errorf(res.Error.Message)
78 } 79 }
79 80
80 fetched = append(fetched, res.Builds...) 81 fetched = append(fetched, res.Builds...)
81 82
82 if len(res.Builds) == 0 || res.NextCursor == "" { 83 if len(res.Builds) == 0 || res.NextCursor == "" {
83 break 84 break
84 } 85 }
85 req.StartCursor(res.NextCursor) 86 req.StartCursor(res.NextCursor)
86 } 87 }
87 » log.Debugf(c, "Fetched %d %s builds in %s", len(fetched), status, clock. Since(c, start)) 88 » logging.Debugf(c, "Fetched %d %s builds in %s", len(fetched), status, cl ock.Since(c, start))
88 return fetched, nil 89 return fetched, nil
89 } 90 }
90 91
91 // toMiloBuild converts a buildbucket build to a milo build. 92 // toMiloBuild converts a buildbucket build to a milo build.
92 // In case of an error, returns a build with a description of the error 93 // In case of an error, returns a build with a description of the error
93 // and logs the error. 94 // and logs the error.
94 func toMiloBuild(c context.Context, build *buildbucket.ApiBuildMessage) *resp.Bu ildSummary { 95 func toMiloBuild(c context.Context, build *buildbucket.ApiBuildMessage) *resp.Bu ildSummary {
95 // Parsing of parameters and result details is best effort. 96 // Parsing of parameters and result details is best effort.
96 var params buildParameters 97 var params buildParameters
97 if err := json.NewDecoder(strings.NewReader(build.ParametersJson)).Decod e(&params); err != nil { 98 if err := json.NewDecoder(strings.NewReader(build.ParametersJson)).Decod e(&params); err != nil {
98 » » log.Errorf(c, "Could not parse parameters of build %d: %s", buil d.Id, err) 99 » » logging.Errorf(c, "Could not parse parameters of build %d: %s", build.Id, err)
99 } 100 }
100 var resultDetails resultDetails 101 var resultDetails resultDetails
101 if err := json.NewDecoder(strings.NewReader(build.ResultDetailsJson)).De code(&resultDetails); err != nil { 102 if err := json.NewDecoder(strings.NewReader(build.ResultDetailsJson)).De code(&resultDetails); err != nil {
102 » » log.Errorf(c, "Could not parse result details of build %d: %s", build.Id, err) 103 » » logging.Errorf(c, "Could not parse result details of build %d: % s", build.Id, err)
103 } 104 }
104 105
105 result := &resp.BuildSummary{ 106 result := &resp.BuildSummary{
106 Text: []string{fmt.Sprintf("buildbucket id %d", build.Id)}, 107 Text: []string{fmt.Sprintf("buildbucket id %d", build.Id)},
107 Revision: resultDetails.Properties.GotRevision, 108 Revision: resultDetails.Properties.GotRevision,
108 } 109 }
109 if result.Revision == "" { 110 if result.Revision == "" {
110 result.Revision = params.Properties.Revision 111 result.Revision = params.Properties.Revision
111 } 112 }
112 113
113 var err error 114 var err error
114 result.Status, err = parseStatus(build) 115 result.Status, err = parseStatus(build)
115 if err != nil { 116 if err != nil {
116 // almost never happens 117 // almost never happens
117 » » log.WithError(err).Errorf(c, "could not convert status of build %d", build.Id) 118 » » logging.WithError(err).Errorf(c, "could not convert status of bu ild %d", build.Id)
118 result.Status = resp.InfraFailure 119 result.Status = resp.InfraFailure
119 result.Text = append(result.Text, fmt.Sprintf("invalid build: %s ", err)) 120 result.Text = append(result.Text, fmt.Sprintf("invalid build: %s ", err))
120 } 121 }
121 122
122 result.PendingTime.Started = parseTimestamp(build.CreatedTs) 123 result.PendingTime.Started = parseTimestamp(build.CreatedTs)
123 switch build.Status { 124 switch build.Status {
124 case "SCHEDULED": 125 case "SCHEDULED":
125 result.PendingTime.Duration = clock.Since(c, result.PendingTime. Started) 126 result.PendingTime.Duration = clock.Since(c, result.PendingTime. Started)
126 127
127 case "STARTED": 128 case "STARTED":
(...skipping 14 matching lines...) Expand all
142 143
143 tags := ParseTags(build.Tags) 144 tags := ParseTags(build.Tags)
144 145
145 if build.Url != "" { 146 if build.Url != "" {
146 u := build.Url 147 u := build.Url
147 parsed, err := url.Parse(u) 148 parsed, err := url.Parse(u)
148 149
149 // map milo links to itself 150 // map milo links to itself
150 switch { 151 switch {
151 case err != nil: 152 case err != nil:
152 » » » log.Errorf(c, "invalid URL in build %d: %s", build.Id, e rr) 153 » » » logging.Errorf(c, "invalid URL in build %d: %s", build.I d, err)
153 case parsed.Host == "luci-milo.appspot.com": 154 case parsed.Host == "luci-milo.appspot.com":
154 parsed.Host = "" 155 parsed.Host = ""
155 parsed.Scheme = "" 156 parsed.Scheme = ""
156 u = parsed.String() 157 u = parsed.String()
157 } 158 }
158 159
159 result.Link = &resp.Link{ 160 result.Link = &resp.Link{
160 URL: u, 161 URL: u,
161 Label: strconv.FormatInt(build.Id, 10), 162 Label: strconv.FormatInt(build.Id, 10),
162 } 163 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 } 214 }
214 215
215 default: 216 default:
216 panic("impossible") 217 panic("impossible")
217 } 218 }
218 } 219 }
219 return nil 220 return nil
220 } 221 }
221 222
222 type builderQuery struct { 223 type builderQuery struct {
223 Server string
224 Bucket string 224 Bucket string
225 Builder string 225 Builder string
226 Limit int 226 Limit int
227 } 227 }
228 228
229 // builderImpl is the implementation for getting a milo builder page from buildb ucket. 229 // builderImpl is the implementation for getting a milo builder page from buildb ucket.
230 // if maxCompletedBuilds < 0, 25 is used. 230 // if maxCompletedBuilds < 0, 25 is used.
231 func builderImpl(c context.Context, q builderQuery) (*resp.Builder, error) { 231 func builderImpl(c context.Context, q builderQuery) (*resp.Builder, error) {
232 settings, err := common.GetSettings(c)
233 if err != nil {
234 logging.WithError(err).Errorf(c, "failed to get settings")
235 return nil, err
236 }
237 if settings.Buildbucket == nil || settings.Buildbucket.Host == "" {
238 logging.WithError(err).Errorf(c, "missing buildbucket settings")
239 return nil, errors.New("missing buildbucket settings")
240 }
241 host := settings.Buildbucket.Host
242
232 if q.Limit < 0 { 243 if q.Limit < 0 {
233 q.Limit = 20 244 q.Limit = 20
234 } 245 }
235 246
236 result := &resp.Builder{ 247 result := &resp.Builder{
237 Name: q.Builder, 248 Name: q.Builder,
238 } 249 }
239 » if q.Server == "debug" { 250 » if host == "debug" {
240 return result, getDebugBuilds(c, q.Bucket, q.Builder, q.Limit, r esult) 251 return result, getDebugBuilds(c, q.Bucket, q.Builder, q.Limit, r esult)
241 } 252 }
242 » client, err := newBuildbucketClient(c, q.Server) 253 » client, err := newBuildbucketClient(c, host)
243 if err != nil { 254 if err != nil {
244 return nil, err 255 return nil, err
245 } 256 }
246 257
247 fetch := func(target *[]*resp.BuildSummary, status string, count int) er ror { 258 fetch := func(target *[]*resp.BuildSummary, status string, count int) er ror {
248 builds, err := fetchBuilds(c, client, q.Bucket, q.Builder, statu s, count) 259 builds, err := fetchBuilds(c, client, q.Bucket, q.Builder, statu s, count)
249 if err != nil { 260 if err != nil {
250 » » » log.Errorf(c, "Could not fetch builds with status %s: %s ", status, err) 261 » » » logging.Errorf(c, "Could not fetch builds with status %s : %s", status, err)
251 return err 262 return err
252 } 263 }
253 *target = make([]*resp.BuildSummary, len(builds)) 264 *target = make([]*resp.BuildSummary, len(builds))
254 for i, bb := range builds { 265 for i, bb := range builds {
255 (*target)[i] = toMiloBuild(c, bb) 266 (*target)[i] = toMiloBuild(c, bb)
256 } 267 }
257 return nil 268 return nil
258 } 269 }
259 // fetch pending, current and finished builds concurrently. 270 // fetch pending, current and finished builds concurrently.
260 // Why not a single request? Because we need different build number 271 // Why not a single request? Because we need different build number
(...skipping 19 matching lines...) Expand all
280 return time.Unix(microseconds/1e6, microseconds%1e6*1000).UTC() 291 return time.Unix(microseconds/1e6, microseconds%1e6*1000).UTC()
281 } 292 }
282 293
283 type newBuildsFirst []*resp.BuildSummary 294 type newBuildsFirst []*resp.BuildSummary
284 295
285 func (a newBuildsFirst) Len() int { return len(a) } 296 func (a newBuildsFirst) Len() int { return len(a) }
286 func (a newBuildsFirst) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 297 func (a newBuildsFirst) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
287 func (a newBuildsFirst) Less(i, j int) bool { 298 func (a newBuildsFirst) Less(i, j int) bool {
288 return a[i].PendingTime.Started.After(a[j].PendingTime.Started) 299 return a[i].PendingTime.Started.After(a[j].PendingTime.Started)
289 } 300 }
OLDNEW
« no previous file with comments | « milo/appengine/buildbucket/buckets.go ('k') | milo/appengine/buildbucket/builder_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698