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

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

Issue 2774343003: Promote LogDog aliases for BuildBot JSON data. (Closed)
Patch Set: 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 | « no previous file | milo/appengine/buildbot/grpc.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 "errors" 9 "errors"
10 "fmt" 10 "fmt"
(...skipping 21 matching lines...) Expand all
32 Master: master, 32 Master: master,
33 Buildername: builder, 33 Buildername: builder,
34 Number: buildNum, 34 Number: buildNum,
35 } 35 }
36 36
37 err := datastore.Get(c, result) 37 err := datastore.Get(c, result)
38 err = checkAccess(c, err, result.Internal) 38 err = checkAccess(c, err, result.Internal)
39 if err == errMasterNotFound { 39 if err == errMasterNotFound {
40 err = errBuildNotFound 40 err = errBuildNotFound
41 } 41 }
42
42 return result, err 43 return result, err
43 } 44 }
44 45
45 // result2Status translates a buildbot result integer into a resp.Status. 46 // result2Status translates a buildbot result integer into a resp.Status.
46 func result2Status(s *int) (status resp.Status) { 47 func result2Status(s *int) (status resp.Status) {
47 if s == nil { 48 if s == nil {
48 return resp.Running 49 return resp.Running
49 } 50 }
50 switch *s { 51 switch *s {
51 case 0: 52 case 0:
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 } 175 }
175 176
176 return sum 177 return sum
177 } 178 }
178 179
179 var rLineBreak = regexp.MustCompile("<br */?>") 180 var rLineBreak = regexp.MustCompile("<br */?>")
180 181
181 // components takes a full buildbot build struct and extract step info from all 182 // components takes a full buildbot build struct and extract step info from all
182 // of the steps and returns it as a list of milo Build Components. 183 // of the steps and returns it as a list of milo Build Components.
183 func components(b *buildbotBuild) (result []*resp.BuildComponent) { 184 func components(b *buildbotBuild) (result []*resp.BuildComponent) {
184 » for i, step := range b.Steps { 185 » for _, step := range b.Steps {
185 if step.Hidden == true { 186 if step.Hidden == true {
186 continue 187 continue
187 } 188 }
188 bc := &resp.BuildComponent{ 189 bc := &resp.BuildComponent{
189 Label: step.Name, 190 Label: step.Name,
190 } 191 }
191 // Step text sometimes contains <br>, which we want to parse int o new lines. 192 // Step text sometimes contains <br>, which we want to parse int o new lines.
192 for _, t := range step.Text { 193 for _, t := range step.Text {
193 for _, line := range rLineBreak.Split(t, -1) { 194 for _, line := range rLineBreak.Split(t, -1) {
194 bc.Text = append(bc.Text, line) 195 bc.Text = append(bc.Text, line)
195 } 196 }
196 } 197 }
197 198
198 // Figure out the status. 199 // Figure out the status.
199 if !step.IsStarted { 200 if !step.IsStarted {
200 bc.Status = resp.NotRun 201 bc.Status = resp.NotRun
201 } else if !step.IsFinished { 202 } else if !step.IsFinished {
202 bc.Status = resp.Running 203 bc.Status = resp.Running
203 } else { 204 } else {
204 if len(step.Results) > 0 { 205 if len(step.Results) > 0 {
205 status := int(step.Results[0].(float64)) 206 status := int(step.Results[0].(float64))
206 bc.Status = result2Status(&status) 207 bc.Status = result2Status(&status)
207 } else { 208 } else {
208 bc.Status = resp.Success 209 bc.Status = resp.Success
209 } 210 }
210 } 211 }
211 212
212 // Now, link to the logs.
213 //
214 // Any given log may have an alias link. There may also be alise s that are
215 // not attached to logs (unusual, but allowed), so we need to tr ack which
216 // aliases are unreferenced and add them as immediate links.
217 //
218 // Additionally, if the build is LogDog-only ("log_location" is "logdog://")
219 // then we want to render *only* the alias for steps with aliase s.
220 isLogDogOnly := false
221 if loc, ok := b.getPropertyValue("log_location").(string); ok && strings.HasPrefix(loc, "logdog://") {
222 isLogDogOnly = true
223 }
224 remainingAliases := stringset.New(len(step.Aliases)) 213 remainingAliases := stringset.New(len(step.Aliases))
225 for linkAnchor := range step.Aliases { 214 for linkAnchor := range step.Aliases {
226 remainingAliases.Add(linkAnchor) 215 remainingAliases.Add(linkAnchor)
227 } 216 }
228 217
229 getLinksWithAliases := func(logLink *resp.Link, isLog bool) resp .LinkSet { 218 getLinksWithAliases := func(logLink *resp.Link, isLog bool) resp .LinkSet {
230 // Generate alias links. 219 // Generate alias links.
231 var aliases resp.LinkSet 220 var aliases resp.LinkSet
232 if remainingAliases.Del(logLink.Label) { 221 if remainingAliases.Del(logLink.Label) {
233 stepAliases := step.Aliases[logLink.Label] 222 stepAliases := step.Aliases[logLink.Label]
234 aliases = make(resp.LinkSet, len(stepAliases)) 223 aliases = make(resp.LinkSet, len(stepAliases))
235 for i, alias := range stepAliases { 224 for i, alias := range stepAliases {
236 aliases[i] = alias.toLink() 225 aliases[i] = alias.toLink()
237 } 226 }
238 } 227 }
239 228
240 var links resp.LinkSet
241 if isLogDogOnly && len(aliases) > 0 && !(isLog && i == 0 && logLink.Label == "stdio") {
242 // LogDog only and there are viable aliases. Fea ture them instead of the
243 // log link.
244 //
245 // We exclude the top-level "stdio" link here be cause there isn't a
246 // LogDog equivalent of the raw step output.
247 //
248 // Any link named "logdog" (Annotee cosmetic imp lementation detail) will
249 // inherit the name of the build link.
250 for _, a := range aliases {
251 if a.Label == "logdog" {
252 a.Label = logLink.Label
253 }
254 }
255 return aliases
256 }
257
258 // Step log link takes primary, with aliases as secondar y. 229 // Step log link takes primary, with aliases as secondar y.
259 » » » links = make(resp.LinkSet, 1, 1+len(aliases)) 230 » » » links := make(resp.LinkSet, 1, 1+len(aliases))
260 links[0] = logLink 231 links[0] = logLink
261 232
262 for _, a := range aliases { 233 for _, a := range aliases {
263 a.Alias = true 234 a.Alias = true
264 } 235 }
265 return append(links, aliases...) 236 return append(links, aliases...)
266 } 237 }
267 238
268 for _, l := range step.Logs { 239 for _, l := range step.Logs {
269 logLink := resp.Link{ 240 logLink := resp.Link{
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
515 var err error 486 var err error
516 if master == "debug" { 487 if master == "debug" {
517 b, err = getDebugBuild(c, builder, buildNum) 488 b, err = getDebugBuild(c, builder, buildNum)
518 } else { 489 } else {
519 b, err = getBuild(c, master, builder, buildNum) 490 b, err = getBuild(c, master, builder, buildNum)
520 } 491 }
521 if err != nil { 492 if err != nil {
522 return nil, err 493 return nil, err
523 } 494 }
524 495
496 // Prepare the build for rendering.
497 postProcessBuild(b)
hinoka 2017/03/27 20:14:35 I like b = postProcessBuild(b) to make it crystal
dnj 2017/03/27 21:31:33 Talked offline, making "updatePostProcessBuild".
498
525 // TODO(hinoka): Do all fields concurrently. 499 // TODO(hinoka): Do all fields concurrently.
526 return &resp.MiloBuild{ 500 return &resp.MiloBuild{
527 SourceStamp: sourcestamp(c, b), 501 SourceStamp: sourcestamp(c, b),
528 Summary: summary(c, b), 502 Summary: summary(c, b),
529 Components: components(b), 503 Components: components(b),
530 PropertyGroup: properties(b), 504 PropertyGroup: properties(b),
531 Blame: blame(b), 505 Blame: blame(b),
532 }, nil 506 }, nil
533 } 507 }
508
509 // postProcessBuild transforms a build from its raw JSON format into the format
510 // that should be presented to users.
hinoka 2017/03/27 20:14:35 Add to comment: // This does the following: // 1.
dnj 2017/03/27 21:31:33 Done.
511 func postProcessBuild(b *buildbotBuild) {
512 // If this is a LogDog-only build, we want to promote the LogDog links, which
513 // come in the form of aliases, to first-class build log and links.
514 if loc, ok := b.getPropertyValue("log_location").(string); ok && strings .HasPrefix(loc, "logdog://") {
515 for sidx := range b.Steps {
hinoka 2017/03/27 20:14:35 nit: just "i" is fine.
dnj 2017/03/27 21:31:33 I want to use the same variable name as in "promot
hinoka 2017/03/27 22:04:26 Acknowledged.
516 promoteLogDogOnlyLinksForStep(b, sidx, &b.Steps[sidx])
hinoka 2017/03/27 20:14:35 I think: b.Steps[i] = logDogOnlyStep(...) is a cle
dnj 2017/03/27 21:31:33 That notation implies that a correct implementatio
517 }
518 }
519 }
520
521 // postProcessBuildPromoteLogDogOnly updates the links in a BuildBot build to
522 // promote LogDog links.
523 //
524 // A build's links come in one of three forms:
525 // - Log Links, which link directly to BuildBot build logs.
526 // - URL Links, which are named links to arbitrary URLs.
527 // - Aliases, which attach to the label in one of the other types of links and
528 // augment it with additional named links.
529 //
530 // LogDog uses aliases exclusively to attach LogDog logs to other links. When
531 // the build is LogDog-only, though, the original links are actually junk. What
532 // we want to do is remove the original junk links and replace them with their
533 // alias counterparts, so that the "natural" BuildBot links are actually LogDog
534 // links.
535 func promoteLogDogOnlyLinksForStep(b *buildbotBuild, sidx int, s *buildbotStep) {
hinoka 2017/03/27 20:14:35 how about logDogOnlyStep(...) Make sure comment f
dnj 2017/03/27 21:31:33 Chatted offline, made "promoteLogDogOnlyLinksForBu
536 type stepLog struct {
537 label string
538 link string
539 }
540
541 remainingAliases := stringset.New(len(s.Aliases))
542 for linkAnchor := range s.Aliases {
543 remainingAliases.Add(linkAnchor)
544 }
545
546 maybePromoteAliases := func(sl *stepLog, isLog bool) []*stepLog {
547 // As a special case, if this is the first step ("steps" in Buil dBot), we
548 // will refrain from promoting aliases for "stdio", since "stdio " represents
549 // the raw BuildBot logs.
550 if isLog && sidx == 0 && sl.label == "stdio" {
hinoka 2017/03/27 20:14:35 This looks like the only place sidx is used. Cons
dnj 2017/03/27 21:31:33 Replaced with "isInitialStep" boolean.
551 // No aliases, don't modify this log.
552 return []*stepLog{sl}
553 }
554
555 // If there are no aliases, we should obviously not promote them . This will
556 // be the case for pre-LogDog steps such as build setup.
557 aliases := s.Aliases[sl.label]
558 if len(aliases) == 0 {
559 return []*stepLog{sl}
560 }
561
562 // We have chosen to promote the aliases. Therefore, we will not include
563 // them as aliases in the modified step.
564 remainingAliases.Del(sl.label)
565
566 result := make([]*stepLog, len(aliases))
567 for i, alias := range aliases {
568 aliasStepLog := stepLog{alias.Text, alias.URL}
569
570 // Any link named "logdog" (Annotee cosmetic implementat ion detail) will
571 // inherit the name of the original log.
572 if !isLog {
573 if aliasStepLog.label == "logdog" {
574 aliasStepLog.label = sl.label
575 }
576 }
577
578 result[i] = &aliasStepLog
579 }
580 return result
581 }
582
583 // Update step logs.
584 newLogs := make([][]string, 0, len(s.Logs))
585 for _, l := range s.Logs {
586 for _, res := range maybePromoteAliases(&stepLog{l[0], l[1]}, tr ue) {
587 newLogs = append(newLogs, []string{res.label, res.link})
588 }
589 }
590 s.Logs = newLogs
591
592 // Update step URLs.
593 newURLs := make(map[string]string, len(s.Urls))
594 for label, link := range s.Urls {
595 urlLinks := maybePromoteAliases(&stepLog{label, link}, false)
596 if len(urlLinks) > 0 {
597 // Use the last URL link, since our URL map can only tol erate one link.
598 // The expected case here is that len(urlLinks) == 1, th ough, but it's
599 // possible that multiple aliases can be included for a single URL, so
600 // we need to handle that.
601 newValue := urlLinks[len(urlLinks)-1]
602 newURLs[newValue.label] = newValue.link
603 } else {
604 newURLs[label] = link
605 }
606 }
607 s.Urls = newURLs
608
609 // Preserve any aliases that haven't been promoted.
610 var newAliases map[string][]*buildbotLinkAlias
611 if l := remainingAliases.Len(); l > 0 {
612 newAliases = make(map[string][]*buildbotLinkAlias, l)
613 remainingAliases.Iter(func(v string) bool {
614 newAliases[v] = s.Aliases[v]
615 return true
616 })
617 }
618 s.Aliases = newAliases
619 }
OLDNEW
« no previous file with comments | « no previous file | milo/appengine/buildbot/grpc.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698