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

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

Issue 2944633003: [milo] Add BuildSummary and common models. (Closed)
Patch Set: add comments Created 3 years, 6 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/logdog/logDogBuild.go ('k') | milo/appengine/swarming/html_data.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 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 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 swarming 5 package swarming
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "fmt" 9 "fmt"
10 "net/http" 10 "net/http"
11 "net/url" 11 "net/url"
12 "strings" 12 "strings"
13 "time" 13 "time"
14 14
15 "golang.org/x/net/context" 15 "golang.org/x/net/context"
16 16
17 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1" 17 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1"
18 "github.com/luci/luci-go/common/errors" 18 "github.com/luci/luci-go/common/errors"
19 "github.com/luci/luci-go/common/logging" 19 "github.com/luci/luci-go/common/logging"
20 "github.com/luci/luci-go/common/proto/google" 20 "github.com/luci/luci-go/common/proto/google"
21 miloProto "github.com/luci/luci-go/common/proto/milo" 21 miloProto "github.com/luci/luci-go/common/proto/milo"
22 "github.com/luci/luci-go/common/sync/parallel" 22 "github.com/luci/luci-go/common/sync/parallel"
23 "github.com/luci/luci-go/logdog/client/annotee" 23 "github.com/luci/luci-go/logdog/client/annotee"
24 "github.com/luci/luci-go/logdog/client/coordinator" 24 "github.com/luci/luci-go/logdog/client/coordinator"
25 "github.com/luci/luci-go/logdog/common/types" 25 "github.com/luci/luci-go/logdog/common/types"
26 "github.com/luci/luci-go/milo/api/resp" 26 "github.com/luci/luci-go/milo/api/resp"
27 "github.com/luci/luci-go/milo/appengine/common" 27 "github.com/luci/luci-go/milo/appengine/common"
28 "github.com/luci/luci-go/milo/appengine/common/model"
28 "github.com/luci/luci-go/milo/appengine/logdog" 29 "github.com/luci/luci-go/milo/appengine/logdog"
29 "github.com/luci/luci-go/server/auth" 30 "github.com/luci/luci-go/server/auth"
30 ) 31 )
31 32
32 // errNotMiloJob is returned if a Swarming task is fetched that does not self- 33 // errNotMiloJob is returned if a Swarming task is fetched that does not self-
33 // identify as a Milo job. 34 // identify as a Milo job.
34 var errNotMiloJob = errors.New("Not a Milo Job or access denied") 35 var errNotMiloJob = errors.New("Not a Milo Job or access denied")
35 36
36 // SwarmingTimeLayout is time layout used by swarming. 37 // SwarmingTimeLayout is time layout used by swarming.
37 const SwarmingTimeLayout = "2006-01-02T15:04:05.999999999" 38 const SwarmingTimeLayout = "2006-01-02T15:04:05.999999999"
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 } 258 }
258 } 259 }
259 return result 260 return result
260 } 261 }
261 262
262 // addBuilderLink adds a link to the buildbucket builder view. 263 // addBuilderLink adds a link to the buildbucket builder view.
263 func addBuilderLink(c context.Context, build *resp.MiloBuild, tags map[string]st ring) { 264 func addBuilderLink(c context.Context, build *resp.MiloBuild, tags map[string]st ring) {
264 bucket := tags["buildbucket_bucket"] 265 bucket := tags["buildbucket_bucket"]
265 builder := tags["builder"] 266 builder := tags["builder"]
266 if bucket != "" && builder != "" { 267 if bucket != "" && builder != "" {
267 » » build.Summary.ParentLabel = &resp.Link{ 268 » » build.Summary.ParentLabel = resp.NewLink(
268 » » » Label: builder, 269 » » » builder, fmt.Sprintf("/buildbucket/%s/%s", bucket, build er))
269 » » » URL: fmt.Sprintf("/buildbucket/%s/%s", bucket, builder ),
270 » » }
271 } 270 }
272 } 271 }
273 272
274 // addBanner adds an OS banner derived from "os" swarming tag, if present. 273 // addBanner adds an OS banner derived from "os" swarming tag, if present.
275 func addBanner(build *resp.MiloBuild, tags map[string]string) { 274 func addBanner(build *resp.MiloBuild, tags map[string]string) {
276 os := tags["os"] 275 os := tags["os"]
277 var ver string 276 var ver string
278 parts := strings.SplitN(os, "-", 2) 277 parts := strings.SplitN(os, "-", 2)
279 if len(parts) == 2 { 278 if len(parts) == 2 {
280 os = parts[0] 279 os = parts[0]
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 if strings.HasPrefix(patchset, "gerrit/") { 400 if strings.HasPrefix(patchset, "gerrit/") {
402 gerritPatchset := strings.TrimLeft(patchset, "gerrit/") 401 gerritPatchset := strings.TrimLeft(patchset, "gerrit/")
403 parts := strings.Split(gerritPatchset, "/") 402 parts := strings.Split(gerritPatchset, "/")
404 if len(parts) != 3 { 403 if len(parts) != 3 {
405 // Not a well-formed gerrit patchset. 404 // Not a well-formed gerrit patchset.
406 return 405 return
407 } 406 }
408 if build.SourceStamp == nil { 407 if build.SourceStamp == nil {
409 build.SourceStamp = &resp.SourceStamp{} 408 build.SourceStamp = &resp.SourceStamp{}
410 } 409 }
411 » » build.SourceStamp.Changelist = &resp.Link{ 410 » » build.SourceStamp.Changelist = resp.NewLink(
412 » » » Label: "Gerrit CL", 411 » » » "Gerrit CL", fmt.Sprintf("https://%s/c/%s/%s", parts[0], parts[1], parts[2]))
413 » » » URL: fmt.Sprintf("https://%s/c/%s/%s", parts[0], parts [1], parts[2]),
414 » » }
415 412
416 } 413 }
417 } 414 }
418 415
419 func addRecipeLink(build *resp.MiloBuild, tags map[string]string) { 416 func addRecipeLink(build *resp.MiloBuild, tags map[string]string) {
420 name := tags["recipe_name"] 417 name := tags["recipe_name"]
421 repoURL := tags["recipe_repository"] 418 repoURL := tags["recipe_repository"]
422 revision := tags["recipe_revision"] 419 revision := tags["recipe_revision"]
423 if name != "" && repoURL != "" { 420 if name != "" && repoURL != "" {
424 if revision == "" { 421 if revision == "" {
425 revision = "master" 422 revision = "master"
426 } 423 }
427 // Link directly to the revision if it is a gerrit URL, otherwis e just 424 // Link directly to the revision if it is a gerrit URL, otherwis e just
428 // display it in the name. 425 // display it in the name.
429 if repoParse, err := url.Parse(repoURL); err == nil && strings.H asSuffix( 426 if repoParse, err := url.Parse(repoURL); err == nil && strings.H asSuffix(
430 repoParse.Host, ".googlesource.com") { 427 repoParse.Host, ".googlesource.com") {
431 repoURL += "/+/" + revision + "/" 428 repoURL += "/+/" + revision + "/"
432 } else { 429 } else {
433 if len(revision) > 8 { 430 if len(revision) > 8 {
434 revision = revision[:8] 431 revision = revision[:8]
435 } 432 }
436 name += " @ " + revision 433 name += " @ " + revision
437 } 434 }
438 » » build.Summary.Recipe = &resp.Link{ 435 » » build.Summary.Recipe = resp.NewLink(name, repoURL)
439 » » » Label: name,
440 » » » URL: repoURL,
441 » » }
442 } 436 }
443 } 437 }
444 438
445 func addTaskToBuild(c context.Context, server string, sr *swarming.SwarmingRpcsT askResult, build *resp.MiloBuild) error { 439 func addTaskToBuild(c context.Context, server string, sr *swarming.SwarmingRpcsT askResult, build *resp.MiloBuild) error {
446 build.Summary.Label = sr.TaskId 440 build.Summary.Label = sr.TaskId
447 build.Summary.Type = resp.Recipe 441 build.Summary.Type = resp.Recipe
448 » build.Summary.Source = &resp.Link{ 442 » build.Summary.Source = resp.NewLink("Task "+sr.TaskId, taskPageURL(serve r, sr.TaskId))
449 » » Label: "Task " + sr.TaskId,
450 » » URL: taskPageURL(server, sr.TaskId),
451 » }
452 443
453 // Extract more swarming specific information into the properties. 444 // Extract more swarming specific information into the properties.
454 if props := taskProperties(sr); len(props.Property) > 0 { 445 if props := taskProperties(sr); len(props.Property) > 0 {
455 build.PropertyGroup = append(build.PropertyGroup, props) 446 build.PropertyGroup = append(build.PropertyGroup, props)
456 } 447 }
457 tags := tagsToMap(sr.Tags) 448 tags := tagsToMap(sr.Tags)
458 449
459 addBuildsetInfo(build, tags) 450 addBuildsetInfo(build, tags)
460 addBanner(build, tags) 451 addBanner(build, tags)
461 addBuilderLink(c, build, tags) 452 addBuilderLink(c, build, tags)
462 addRecipeLink(build, tags) 453 addRecipeLink(build, tags)
463 454
464 // Add a link to the bot. 455 // Add a link to the bot.
465 if sr.BotId != "" { 456 if sr.BotId != "" {
466 » » build.Summary.Bot = &resp.Link{ 457 » » build.Summary.Bot = resp.NewLink(sr.BotId, botPageURL(server, sr .BotId))
467 » » » Label: sr.BotId,
468 » » » URL: botPageURL(server, sr.BotId),
469 » » }
470 } 458 }
471 459
472 return nil 460 return nil
473 } 461 }
474 462
475 // streamsFromAnnotatedLog takes in an annotated log and returns a fully 463 // streamsFromAnnotatedLog takes in an annotated log and returns a fully
476 // populated set of logdog streams 464 // populated set of logdog streams
477 func streamsFromAnnotatedLog(ctx context.Context, log string) (*logdog.Streams, error) { 465 func streamsFromAnnotatedLog(ctx context.Context, log string) (*logdog.Streams, error) {
478 c := &memoryClient{} 466 c := &memoryClient{}
479 p := annotee.New(ctx, annotee.Options{ 467 p := annotee.New(ctx, annotee.Options{
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 } 519 }
532 520
533 return &as, nil 521 return &as, nil
534 } 522 }
535 523
536 // failedToStart is called in the case where logdog-only mode is on but the 524 // failedToStart is called in the case where logdog-only mode is on but the
537 // stream doesn't exist and the swarming job is complete. It modifies the build 525 // stream doesn't exist and the swarming job is complete. It modifies the build
538 // to add information that would've otherwise been in the annotation stream. 526 // to add information that would've otherwise been in the annotation stream.
539 func failedToStart(c context.Context, build *resp.MiloBuild, res *swarming.Swarm ingRpcsTaskResult, host string) error { 527 func failedToStart(c context.Context, build *resp.MiloBuild, res *swarming.Swarm ingRpcsTaskResult, host string) error {
540 var err error 528 var err error
541 » build.Summary.Status = resp.InfraFailure 529 » build.Summary.Status = model.InfraFailure
542 build.Summary.Started, err = time.Parse(SwarmingTimeLayout, res.StartedT s) 530 build.Summary.Started, err = time.Parse(SwarmingTimeLayout, res.StartedT s)
543 if err != nil { 531 if err != nil {
544 return err 532 return err
545 } 533 }
546 build.Summary.Finished, err = time.Parse(SwarmingTimeLayout, res.Complet edTs) 534 build.Summary.Finished, err = time.Parse(SwarmingTimeLayout, res.Complet edTs)
547 if err != nil { 535 if err != nil {
548 return err 536 return err
549 } 537 }
550 build.Summary.Duration = build.Summary.Finished.Sub(build.Summary.Starte d) 538 build.Summary.Duration = build.Summary.Finished.Sub(build.Summary.Starte d)
551 » infoComp := infoComponent(resp.InfraFailure, 539 » infoComp := infoComponent(model.InfraFailure,
552 "LogDog stream not found", "Job likely failed to start.") 540 "LogDog stream not found", "Job likely failed to start.")
553 infoComp.Started = build.Summary.Started 541 infoComp.Started = build.Summary.Started
554 infoComp.Finished = build.Summary.Finished 542 infoComp.Finished = build.Summary.Finished
555 infoComp.Duration = build.Summary.Duration 543 infoComp.Duration = build.Summary.Duration
556 infoComp.Verbosity = resp.Interesting 544 infoComp.Verbosity = resp.Interesting
557 build.Components = append(build.Components, infoComp) 545 build.Components = append(build.Components, infoComp)
558 return addTaskToBuild(c, host, res, build) 546 return addTaskToBuild(c, host, res, build)
559 } 547 }
560 548
561 func (bl *buildLoader) swarmingBuildImpl(c context.Context, svc swarmingService, linkBase, taskID string) (*resp.MiloBuild, error) { 549 func (bl *buildLoader) swarmingBuildImpl(c context.Context, svc swarmingService, linkBase, taskID string) (*resp.MiloBuild, error) {
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
630 // The stream was not found. This could be due to one of two things: 618 // The stream was not found. This could be due to one of two things:
631 // 1. The step just started and we're just waiti ng for the logs 619 // 1. The step just started and we're just waiti ng for the logs
632 // to propogage to logdog. 620 // to propogage to logdog.
633 // 2. The bootsrap on the client failed, and nev er sent data to logdog. 621 // 2. The bootsrap on the client failed, and nev er sent data to logdog.
634 // This would be evident because the swarming re sult would be a failure. 622 // This would be evident because the swarming re sult would be a failure.
635 if fr.res.State == TaskCompleted { 623 if fr.res.State == TaskCompleted {
636 err = failedToStart(c, &build, fr.res, s vc.getHost()) 624 err = failedToStart(c, &build, fr.res, s vc.getHost())
637 return &build, err 625 return &build, err
638 } 626 }
639 logging.WithError(err).Errorf(c, "User cannot ac cess stream.") 627 logging.WithError(err).Errorf(c, "User cannot ac cess stream.")
640 » » » » build.Components = append(build.Components, info Component(resp.Running, 628 » » » » build.Components = append(build.Components, info Component(model.Running,
641 "Waiting...", "waiting for annotation st ream")) 629 "Waiting...", "waiting for annotation st ream"))
642 630
643 case coordinator.ErrNoAccess: 631 case coordinator.ErrNoAccess:
644 logging.WithError(err).Errorf(c, "User cannot ac cess stream.") 632 logging.WithError(err).Errorf(c, "User cannot ac cess stream.")
645 » » » » build.Components = append(build.Components, info Component(resp.Failure, 633 » » » » build.Components = append(build.Components, info Component(model.Failure,
646 "No Access", "no access to annotation st ream")) 634 "No Access", "no access to annotation st ream"))
647 635
648 default: 636 default:
649 logging.WithError(err).Errorf(c, "Failed to load LogDog annotation stream.") 637 logging.WithError(err).Errorf(c, "Failed to load LogDog annotation stream.")
650 » » » » build.Components = append(build.Components, info Component(resp.InfraFailure, 638 » » » » build.Components = append(build.Components, info Component(model.InfraFailure,
651 "Error", "failed to load annotation stre am")) 639 "Error", "failed to load annotation stre am"))
652 } 640 }
653 } 641 }
654 642
655 case fr.log != "": 643 case fr.log != "":
656 // Decode the data using annotee. The logdog stream returned her e is assumed 644 // Decode the data using annotee. The logdog stream returned her e is assumed
657 // to be consistent, which is why the following block of code ar e not 645 // to be consistent, which is why the following block of code ar e not
658 // expected to ever err out. 646 // expected to ever err out.
659 var err error 647 var err error
660 lds, err = streamsFromAnnotatedLog(c, fr.log) 648 lds, err = streamsFromAnnotatedLog(c, fr.log)
661 if err != nil { 649 if err != nil {
662 » » » comp := infoComponent(resp.InfraFailure, "Milo annotatio n parser", err.Error()) 650 » » » comp := infoComponent(model.InfraFailure, "Milo annotati on parser", err.Error())
663 » » » comp.SubLink = append(comp.SubLink, resp.LinkSet{&resp.L ink{ 651 » » » comp.SubLink = append(comp.SubLink, resp.LinkSet{
664 » » » » Label: "swarming task", 652 » » » » resp.NewLink("swarming task", taskPageURL(svc.ge tHost(), taskID)),
665 » » » » URL: taskPageURL(svc.getHost(), taskID), 653 » » » })
666 » » » }})
667 build.Components = append(build.Components, comp) 654 build.Components = append(build.Components, comp)
668 } 655 }
669 656
670 if lds != nil && lds.MainStream != nil && lds.MainStream.Data != nil { 657 if lds != nil && lds.MainStream != nil && lds.MainStream.Data != nil {
671 s = lds.MainStream.Data 658 s = lds.MainStream.Data
672 } 659 }
673 ub = swarmingURLBuilder(linkBase) 660 ub = swarmingURLBuilder(linkBase)
674 661
675 default: 662 default:
676 s = &miloProto.Step{} 663 s = &miloProto.Step{}
677 ub = swarmingURLBuilder(linkBase) 664 ub = swarmingURLBuilder(linkBase)
678 } 665 }
679 666
680 if s != nil { 667 if s != nil {
681 if err := addTaskToMiloStep(c, svc.getHost(), fr.res, s); err != nil { 668 if err := addTaskToMiloStep(c, svc.getHost(), fr.res, s); err != nil {
682 return nil, err 669 return nil, err
683 } 670 }
684 logdog.AddLogDogToBuild(c, ub, s, &build) 671 logdog.AddLogDogToBuild(c, ub, s, &build)
685 } 672 }
686 673
687 if err := addTaskToBuild(c, svc.getHost(), fr.res, &build); err != nil { 674 if err := addTaskToBuild(c, svc.getHost(), fr.res, &build); err != nil {
688 return nil, err 675 return nil, err
689 } 676 }
690 677
691 return &build, nil 678 return &build, nil
692 } 679 }
693 680
694 func infoComponent(st resp.Status, label, text string) *resp.BuildComponent { 681 func infoComponent(st model.Status, label, text string) *resp.BuildComponent {
695 return &resp.BuildComponent{ 682 return &resp.BuildComponent{
696 Type: resp.Summary, 683 Type: resp.Summary,
697 Label: label, 684 Label: label,
698 Text: []string{text}, 685 Text: []string{text},
699 Status: st, 686 Status: st,
700 } 687 }
701 } 688 }
702 689
703 // isAllowed checks if: 690 // isAllowed checks if:
704 // 1. allow_milo:1 is present. If so, it's a public job. 691 // 1. allow_milo:1 is present. If so, it's a public job.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
755 742
756 switch t := l.Value.(type) { 743 switch t := l.Value.(type) {
757 case *miloProto.Link_LogdogStream: 744 case *miloProto.Link_LogdogStream:
758 ls := t.LogdogStream 745 ls := t.LogdogStream
759 746
760 if u.Path == "" { 747 if u.Path == "" {
761 u.Path = ls.Name 748 u.Path = ls.Name
762 } else { 749 } else {
763 u.Path = strings.TrimSuffix(u.Path, "/") + "/" + ls.Name 750 u.Path = strings.TrimSuffix(u.Path, "/") + "/" + ls.Name
764 } 751 }
765 » » link := resp.Link{ 752 » » link := resp.NewLink(l.Label, u.String())
766 » » » Label: l.Label,
767 » » » URL: u.String(),
768 » » }
769 if link.Label == "" { 753 if link.Label == "" {
770 link.Label = ls.Name 754 link.Label = ls.Name
771 } 755 }
772 » » return &link 756 » » return link
773 757
774 case *miloProto.Link_Url: 758 case *miloProto.Link_Url:
775 » » return &resp.Link{ 759 » » return resp.NewLink(l.Label, t.Url)
776 » » » Label: l.Label,
777 » » » URL: t.Url,
778 » » }
779 760
780 default: 761 default:
781 return nil 762 return nil
782 } 763 }
783 } 764 }
784 765
785 func swarmingTags(v []string) map[string]string { 766 func swarmingTags(v []string) map[string]string {
786 res := make(map[string]string, len(v)) 767 res := make(map[string]string, len(v))
787 for _, tag := range v { 768 for _, tag := range v {
788 var value string 769 var value string
789 parts := strings.SplitN(tag, ":", 2) 770 parts := strings.SplitN(tag, ":", 2)
790 if len(parts) == 2 { 771 if len(parts) == 2 {
791 value = parts[1] 772 value = parts[1]
792 } 773 }
793 res[parts[0]] = value 774 res[parts[0]] = value
794 } 775 }
795 return res 776 return res
796 } 777 }
OLDNEW
« no previous file with comments | « milo/appengine/logdog/logDogBuild.go ('k') | milo/appengine/swarming/html_data.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698