| OLD | NEW |
| 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 annotation | 5 package annotation |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "path" | |
| 10 "strconv" | 9 "strconv" |
| 11 "strings" | 10 "strings" |
| 12 "time" | 11 "time" |
| 13 | 12 |
| 14 "github.com/luci/luci-go/common/clock" | 13 "github.com/luci/luci-go/common/clock" |
| 15 "github.com/luci/luci-go/common/proto/google" | 14 "github.com/luci/luci-go/common/proto/google" |
| 16 "github.com/luci/luci-go/common/proto/milo" | 15 "github.com/luci/luci-go/common/proto/milo" |
| 17 "github.com/luci/luci-go/logdog/common/types" | 16 "github.com/luci/luci-go/logdog/common/types" |
| 18 ) | 17 ) |
| 19 | 18 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 return | 92 return |
| 94 } | 93 } |
| 95 | 94 |
| 96 s.stepMap = map[string]*Step{} | 95 s.stepMap = map[string]*Step{} |
| 97 s.stepLookup = map[*milo.Step]*Step{} | 96 s.stepLookup = map[*milo.Step]*Step{} |
| 98 | 97 |
| 99 name := "steps" | 98 name := "steps" |
| 100 if s.Execution != nil { | 99 if s.Execution != nil { |
| 101 name = s.Execution.Name | 100 name = s.Execution.Name |
| 102 } | 101 } |
| 103 » s.rootStep.initializeStep(s, nil, name, 0, s.LogNameBase) | 102 » s.rootStep.initializeStep(s, nil, name) |
| 103 » s.rootStep.LogNameBase = s.LogNameBase |
| 104 s.SetCurrentStep(nil) | 104 s.SetCurrentStep(nil) |
| 105 | 105 |
| 106 // Add our Command parameters, if applicable. | 106 // Add our Command parameters, if applicable. |
| 107 if exec := s.Execution; exec != nil { | 107 if exec := s.Execution; exec != nil { |
| 108 s.rootStep.Command = &milo.Step_Command{ | 108 s.rootStep.Command = &milo.Step_Command{ |
| 109 CommandLine: exec.Command, | 109 CommandLine: exec.Command, |
| 110 Cwd: exec.Dir, | 110 Cwd: exec.Dir, |
| 111 Environ: exec.Env, | 111 Environ: exec.Env, |
| 112 } | 112 } |
| 113 } | 113 } |
| (...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 // Both prevStep and nextStep are creation-ordered, and don't change eve
n if | 529 // Both prevStep and nextStep are creation-ordered, and don't change eve
n if |
| 530 // a Step is reparented. | 530 // a Step is reparented. |
| 531 prevStep *Step | 531 prevStep *Step |
| 532 // nextStep is the step that was created immediately after this step. It
is | 532 // nextStep is the step that was created immediately after this step. It
is |
| 533 // nil if this is the latest step. | 533 // nil if this is the latest step. |
| 534 // | 534 // |
| 535 // Both prevStep and nextStep are creation-ordered, and don't change eve
n if | 535 // Both prevStep and nextStep are creation-ordered, and don't change eve
n if |
| 536 // a Step is reparented. | 536 // a Step is reparented. |
| 537 nextStep *Step | 537 nextStep *Step |
| 538 | 538 |
| 539 index int | |
| 540 level int | 539 level int |
| 541 | 540 |
| 542 » stepIndex map[string]int | 541 » // logPathIndex is a map of the number of log paths with the given base
name. |
| 542 » // Each time a log path is generated, it will register with this map and |
| 543 » // increase the count. |
| 544 » logPathIndex map[types.StreamName]int |
| 543 | 545 |
| 544 // logLines is a map of log line labels to full log stream names. | 546 // logLines is a map of log line labels to full log stream names. |
| 545 logLines map[string]types.StreamName | 547 logLines map[string]types.StreamName |
| 546 // logLineCount is a map of log line label to the number of times that l
og | 548 // logLineCount is a map of log line label to the number of times that l
og |
| 547 // line has appeared. This is to prevent the case where multiple log lin
es | 549 // line has appeared. This is to prevent the case where multiple log lin
es |
| 548 // with the same label may be emitted, which would cause duplicate log s
tream | 550 // with the same label may be emitted, which would cause duplicate log s
tream |
| 549 // names. | 551 // names. |
| 550 logLineCount map[string]int | 552 logLineCount map[string]int |
| 551 | 553 |
| 552 » // LogNameBase is the LogDog stream name root for this step. | 554 » // logNameBase is the LogDog stream name root for this step. |
| 553 » logNameBase types.StreamName | 555 » LogNameBase types.StreamName |
| 554 // hasSummary, if true, means that this Step has summary text. The summa
ry | 556 // hasSummary, if true, means that this Step has summary text. The summa
ry |
| 555 // text is stored as the first line in its Step.Text slice. | 557 // text is stored as the first line in its Step.Text slice. |
| 556 hasSummary bool | 558 hasSummary bool |
| 557 // closed is true if the element is closed. | 559 // closed is true if the element is closed. |
| 558 closed bool | 560 closed bool |
| 559 } | 561 } |
| 560 | 562 |
| 561 func (as *Step) initializeStep(s *State, parent *Step, name string, index int, l
ogNameBase types.StreamName) *Step { | 563 func (as *Step) String() string { return string(as.LogNameBase) } |
| 564 |
| 565 func (as *Step) initializeStep(s *State, parent *Step, name string) *Step { |
| 562 t := milo.Status_RUNNING | 566 t := milo.Status_RUNNING |
| 563 as.Step = milo.Step{ | 567 as.Step = milo.Step{ |
| 564 Name: name, | 568 Name: name, |
| 565 Status: t, | 569 Status: t, |
| 566 } | 570 } |
| 567 | 571 |
| 568 as.s = s | 572 as.s = s |
| 569 as.index = index | |
| 570 as.logNameBase = logNameBase | |
| 571 as.stepIndex = map[string]int{} | |
| 572 as.logLines = map[string]types.StreamName{} | 573 as.logLines = map[string]types.StreamName{} |
| 573 as.logLineCount = map[string]int{} | 574 as.logLineCount = map[string]int{} |
| 575 as.logPathIndex = map[types.StreamName]int{} |
| 574 | 576 |
| 575 // Add this Step to our parent's Substep list. | 577 // Add this Step to our parent's Substep list. |
| 576 if parent != nil { | 578 if parent != nil { |
| 577 parent.appendSubstep(as) | 579 parent.appendSubstep(as) |
| 578 } | 580 } |
| 579 s.registerStep(as) | 581 s.registerStep(as) |
| 580 | 582 |
| 581 return as | 583 return as |
| 582 } | 584 } |
| 583 | 585 |
| 584 func (as *Step) appendSubstep(s *Step) { | 586 func (as *Step) appendSubstep(s *Step) { |
| 585 if s.parent == as { | 587 if s.parent == as { |
| 586 // Already parented to as, so do nothing. | 588 // Already parented to as, so do nothing. |
| 587 return | 589 return |
| 588 } | 590 } |
| 589 s.detachFromParent() | 591 s.detachFromParent() |
| 590 | 592 |
| 591 s.parent = as | 593 s.parent = as |
| 592 as.Substep = append(as.Substep, &milo.Step_Substep{ | 594 as.Substep = append(as.Substep, &milo.Step_Substep{ |
| 593 Substep: &milo.Step_Substep_Step{ | 595 Substep: &milo.Step_Substep_Step{ |
| 594 Step: &s.Step, | 596 Step: &s.Step, |
| 595 }, | 597 }, |
| 596 }) | 598 }) |
| 599 s.regenerateLogPath() |
| 597 } | 600 } |
| 598 | 601 |
| 599 func (as *Step) detachFromParent() { | 602 func (as *Step) detachFromParent() { |
| 600 parent := as.parent | 603 parent := as.parent |
| 601 if parent == nil { | 604 if parent == nil { |
| 602 return | 605 return |
| 603 } | 606 } |
| 604 | 607 |
| 605 // Remove any instances of "as" from its current parent's Substeps. | 608 // Remove any instances of "as" from its current parent's Substeps. |
| 606 ssPtr := 0 | 609 ssPtr := 0 |
| 607 for _, ss := range parent.Substep { | 610 for _, ss := range parent.Substep { |
| 608 if ss.GetStep() != &as.Step { | 611 if ss.GetStep() != &as.Step { |
| 609 parent.Substep[ssPtr] = ss | 612 parent.Substep[ssPtr] = ss |
| 610 ssPtr++ | 613 ssPtr++ |
| 611 } | 614 } |
| 612 } | 615 } |
| 613 parent.Substep = parent.Substep[:ssPtr] | 616 parent.Substep = parent.Substep[:ssPtr] |
| 614 as.parent = nil | 617 as.parent = nil |
| 615 } | 618 } |
| 616 | 619 |
| 617 // CanonicalName returns the canonical name of this Step. This name is | |
| 618 // guaranteed to be unique witin the State. | |
| 619 func (as *Step) CanonicalName() string { | |
| 620 parts := []string(nil) | |
| 621 if as.index == 0 { | |
| 622 parts = append(parts, as.Name()) | |
| 623 } else { | |
| 624 parts = append(parts, fmt.Sprintf("%s_%d", as.Name(), as.index)) | |
| 625 } | |
| 626 for p := as.parent; p != nil; p = p.parent { | |
| 627 parts = append(parts, p.Name()) | |
| 628 } | |
| 629 for i := len(parts)/2 - 1; i >= 0; i-- { | |
| 630 opp := len(parts) - 1 - i | |
| 631 parts[i], parts[opp] = parts[opp], parts[i] | |
| 632 } | |
| 633 return path.Join(parts...) | |
| 634 } | |
| 635 | |
| 636 // Name returns the step's component name. | 620 // Name returns the step's component name. |
| 637 func (as *Step) Name() string { | 621 func (as *Step) Name() string { |
| 638 return as.Step.Name | 622 return as.Step.Name |
| 639 } | 623 } |
| 640 | 624 |
| 641 // Proto returns the Milo protobuf associated with this Step. | 625 // Proto returns the Milo protobuf associated with this Step. |
| 642 func (as *Step) Proto() *milo.Step { | 626 func (as *Step) Proto() *milo.Step { |
| 643 return &as.Step | 627 return &as.Step |
| 644 } | 628 } |
| 645 | 629 |
| 646 // BaseStream returns the supplied name prepended with this Step's base | 630 // BaseStream returns the supplied name prepended with this Step's base |
| 647 // log name. | 631 // log name. |
| 648 // | 632 // |
| 649 // For example, if the base name is "foo/bar", BaseStream("baz") will return | 633 // For example, if the base name is "foo/bar", BaseStream("baz") will return |
| 650 // "foo/bar/baz". | 634 // "foo/bar/baz". |
| 651 func (as *Step) BaseStream(name types.StreamName) types.StreamName { | 635 func (as *Step) BaseStream(name types.StreamName) types.StreamName { |
| 652 » if as.logNameBase == "" { | 636 » if as.LogNameBase == "" { |
| 653 return name | 637 return name |
| 654 } | 638 } |
| 655 » return as.logNameBase.Concat(name) | 639 » return as.LogNameBase.Concat(name) |
| 656 } | 640 } |
| 657 | 641 |
| 658 // AddStep generates a new substep. | 642 // AddStep generates a new substep. |
| 659 func (as *Step) AddStep(name string) *Step { | 643 func (as *Step) AddStep(name string) *Step { |
| 660 » // Determine/advance step index. | 644 » return (&Step{}).initializeStep(as.s, as, name) |
| 661 » index := as.stepIndex[name] | 645 } |
| 662 » as.stepIndex[name]++ | |
| 663 | 646 |
| 664 » logPath, err := types.MakeStreamName("s_", "steps", name, strconv.Itoa(i
ndex)) | 647 func (as *Step) regenerateLogPath() { |
| 665 » if err != nil { | 648 » if as.parent == nil { |
| 666 » » panic(fmt.Errorf("failed to generate step name for [%s]: %s", na
me, err)) | 649 » » panic("log path regeneration cannot be called on root step") |
| 667 } | 650 } |
| 668 | 651 |
| 669 » nas := (&Step{}).initializeStep(as.s, as, name, index, as.BaseStream(log
Path)) | 652 » // Recipe engine nests steps by prepending their parents' name, e.g. |
| 670 » return nas | 653 » // if "foo" has a nested child, it will be named "foo.bar". This is redu
ndant |
| 654 » // for our stream names, so strip that off. |
| 655 » // |
| 656 » // We throw the length conditional in just in case the child step happen
s to |
| 657 » // have the exact same name as the parent. This shouldn't happen natural
ly, |
| 658 » // but let's be robust. |
| 659 » name := as.Name() |
| 660 » if parentPrefix := (as.parent.Name() + "."); len(parentPrefix) < len(nam
e) { |
| 661 » » name = strings.TrimPrefix(name, parentPrefix) |
| 662 » } |
| 663 |
| 664 » logPath, err := types.MakeStreamName("s_", "steps", name) |
| 665 » if err != nil { |
| 666 » » panic(fmt.Errorf("failed to generate step name for [%s]: %s", as
.Name(), err)) |
| 667 » } |
| 668 |
| 669 » index := as.parent.logPathIndex[logPath] |
| 670 » as.parent.logPathIndex[logPath] = (index + 1) |
| 671 |
| 672 » // Append the index to the stream name. |
| 673 » logPath = logPath.Concat(types.StreamName(strconv.Itoa(index))) |
| 674 » if err := logPath.Validate(); err != nil { |
| 675 » » panic(fmt.Errorf("generated invalid log stream path %q: %v", log
Path, err)) |
| 676 » } |
| 677 |
| 678 » as.LogNameBase = as.parent.BaseStream(logPath) |
| 671 } | 679 } |
| 672 | 680 |
| 673 // Start marks the Step as started. | 681 // Start marks the Step as started. |
| 674 func (as *Step) Start(startTime *google.Timestamp) bool { | 682 func (as *Step) Start(startTime *google.Timestamp) bool { |
| 675 if as.Started != nil { | 683 if as.Started != nil { |
| 676 return false | 684 return false |
| 677 } | 685 } |
| 678 as.Started = startTime | 686 as.Started = startTime |
| 679 return true | 687 return true |
| 680 } | 688 } |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 913 as.StderrStream, updated = as.maybeSetLogDogStream(as.StderrStream, st) | 921 as.StderrStream, updated = as.maybeSetLogDogStream(as.StderrStream, st) |
| 914 return | 922 return |
| 915 } | 923 } |
| 916 | 924 |
| 917 func (as *Step) maybeSetLogDogStream(target *milo.LogdogStream, st *milo.LogdogS
tream) (*milo.LogdogStream, bool) { | 925 func (as *Step) maybeSetLogDogStream(target *milo.LogdogStream, st *milo.LogdogS
tream) (*milo.LogdogStream, bool) { |
| 918 if (target == nil && st == nil) || (target != nil && st != nil && *targe
t == *st) { | 926 if (target == nil && st == nil) || (target != nil && st != nil && *targe
t == *st) { |
| 919 return target, false | 927 return target, false |
| 920 } | 928 } |
| 921 return st, true | 929 return st, true |
| 922 } | 930 } |
| OLD | NEW |