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

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

Issue 2774343003: Promote LogDog aliases for BuildBot JSON data. (Closed)
Patch Set: comments 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 // Modify the build for rendering.
497 updatePostProcessBuild(b)
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 // updatePostProcessBuild transforms a build from its raw JSON format into the
510 // format that should be presented to users.
511 //
512 // Post-processing includes:
513 // - If the build is LogDog-only, promotes aliases (LogDog links) to
514 // first-class links in the build.
515 func updatePostProcessBuild(b *buildbotBuild) {
516 // If this is a LogDog-only build, we want to promote the LogDog links.
517 if loc, ok := b.getPropertyValue("log_location").(string); ok && strings .HasPrefix(loc, "logdog://") {
518 for sidx := range b.Steps {
519 promoteLogDogLinks(&b.Steps[sidx], sidx == 0)
520 }
521 }
522 }
523
524 // promoteLogDogLinks updates the links in a BuildBot step to
525 // promote LogDog links.
526 //
527 // A build's links come in one of three forms:
528 // - Log Links, which link directly to BuildBot build logs.
529 // - URL Links, which are named links to arbitrary URLs.
530 // - Aliases, which attach to the label in one of the other types of links and
531 // augment it with additional named links.
532 //
533 // LogDog uses aliases exclusively to attach LogDog logs to other links. When
534 // the build is LogDog-only, though, the original links are actually junk. What
535 // we want to do is remove the original junk links and replace them with their
536 // alias counterparts, so that the "natural" BuildBot links are actually LogDog
537 // links.
538 func promoteLogDogLinks(s *buildbotStep, isInitialStep bool) {
539 type stepLog struct {
540 label string
541 url string
542 }
543
544 remainingAliases := stringset.New(len(s.Aliases))
545 for linkAnchor := range s.Aliases {
546 remainingAliases.Add(linkAnchor)
547 }
548
549 maybePromoteAliases := func(sl *stepLog, isLog bool) []*stepLog {
550 // As a special case, if this is the first step ("steps" in Buil dBot), we
551 // will refrain from promoting aliases for "stdio", since "stdio " represents
552 // the raw BuildBot logs.
553 if isLog && isInitialStep && sl.label == "stdio" {
554 // No aliases, don't modify this log.
555 return []*stepLog{sl}
556 }
557
558 // If there are no aliases, we should obviously not promote them . This will
559 // be the case for pre-LogDog steps such as build setup.
560 aliases := s.Aliases[sl.label]
561 if len(aliases) == 0 {
562 return []*stepLog{sl}
563 }
564
565 // We have chosen to promote the aliases. Therefore, we will not include
566 // them as aliases in the modified step.
567 remainingAliases.Del(sl.label)
568
569 result := make([]*stepLog, len(aliases))
570 for i, alias := range aliases {
571 aliasStepLog := stepLog{alias.Text, alias.URL}
572
573 // Any link named "logdog" (Annotee cosmetic implementat ion detail) will
574 // inherit the name of the original log.
575 if !isLog {
576 if aliasStepLog.label == "logdog" {
577 aliasStepLog.label = sl.label
578 }
579 }
580
581 result[i] = &aliasStepLog
582 }
583 return result
584 }
585
586 // Update step logs.
587 newLogs := make([][]string, 0, len(s.Logs))
588 for _, l := range s.Logs {
589 for _, res := range maybePromoteAliases(&stepLog{l[0], l[1]}, tr ue) {
590 newLogs = append(newLogs, []string{res.label, res.url})
591 }
592 }
593 s.Logs = newLogs
594
595 // Update step URLs.
596 newURLs := make(map[string]string, len(s.Urls))
597 for label, link := range s.Urls {
598 urlLinks := maybePromoteAliases(&stepLog{label, link}, false)
599 if len(urlLinks) > 0 {
600 // Use the last URL link, since our URL map can only tol erate one link.
601 // The expected case here is that len(urlLinks) == 1, th ough, but it's
602 // possible that multiple aliases can be included for a single URL, so
603 // we need to handle that.
604 newValue := urlLinks[len(urlLinks)-1]
605 newURLs[newValue.label] = newValue.url
606 } else {
607 newURLs[label] = link
608 }
609 }
610 s.Urls = newURLs
611
612 // Preserve any aliases that haven't been promoted.
613 var newAliases map[string][]*buildbotLinkAlias
614 if l := remainingAliases.Len(); l > 0 {
615 newAliases = make(map[string][]*buildbotLinkAlias, l)
616 remainingAliases.Iter(func(v string) bool {
617 newAliases[v] = s.Aliases[v]
618 return true
619 })
620 }
621 s.Aliases = newAliases
622 }
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