| // Package workflow contains const and types to invoke Workflows. |
| package workflows |
| |
| import ( |
| "strconv" |
| "strings" |
| |
| buildbucketpb "go.chromium.org/luci/buildbucket/proto" |
| apipb "go.chromium.org/luci/swarming/proto/api_v2" |
| "go.skia.org/infra/pinpoint/go/compare" |
| "go.skia.org/infra/pinpoint/go/midpoint" |
| "go.skia.org/infra/pinpoint/go/run_benchmark" |
| pb "go.skia.org/infra/pinpoint/proto/v1" |
| ) |
| |
| // Workflow name definitions. |
| // |
| // Those are used to invoke the workflows. This is meant to decouple the |
| // souce code dependencies such that the client doesn't need to link with |
| // the actual implementation. |
| const ( |
| Bisect = "perf.bisect" |
| BuildChrome = "perf.build_chrome" |
| CatapultBisect = "perf.catapult.bisect" |
| ConvertToCatapultResponseWorkflow = "perf.catapult.response" |
| CulpritFinderWorkflow = "perf.culprit_finder" |
| RunBenchmark = "perf.run_benchmark" |
| RunBenchmarkPairwise = "perf.run_benchmark.pairwise" |
| SingleCommitRunner = "perf.single_commit_runner" |
| PairwiseCommitsRunner = "perf.pairwise_commits_runner" |
| PairwiseWorkflow = "perf.pairwise" |
| BugUpdate = "perf.bug_update" |
| ) |
| |
| const defaultPairwiseAttemptCount int32 = 30 |
| |
| // Workflow params definitions. |
| // |
| // Each workflow defines its own struct for the params, this will ensure |
| // the input parameter type safety, as well as expose them in a structured way. |
| type BuildChromeParams struct { |
| // WorkflowID is arbitrary string that tags the build. |
| // This is used to connect the downstream and know which build is used. |
| // This is usually the pinpoint job ID. |
| WorkflowID string |
| // Commit is the chromium commit hash. |
| Commit *midpoint.CombinedCommit |
| // Device is the name of the device, e.g. "linux-perf". |
| Device string |
| // Target is name of the build isolate target |
| // e.g. "performance_test_suite". |
| Target string |
| // Patch is the Gerrit patch included in the build. |
| Patch []*buildbucketpb.GerritChange |
| } |
| |
| // Build stores the build from Buildbucket. |
| type Build struct { |
| // The parameters used to make this build. |
| BuildChromeParams |
| // ID is the buildbucket ID of the Chrome build. |
| // https://github.com/luci/luci-go/blob/19a07406e/buildbucket/proto/build.proto#L138 |
| ID int64 |
| // Status is the status of the build, this is needed to surface the build failures. |
| Status buildbucketpb.Status |
| // CAS is the CAS address of the build isolate. |
| CAS *apipb.CASReference |
| } |
| |
| // TestRun stores individual benchmark test run. |
| type TestRun struct { |
| // TaskID is the swarming task ID. |
| TaskID string |
| // Status is the swarming task status. |
| Status run_benchmark.State |
| // CAS is the CAS address of the test output. |
| CAS *apipb.CASReference |
| // Values is sampled values for each benchmark story. |
| Values map[string][]float64 |
| } |
| |
| // IsEmptyValues checks the TestRun if there are values at that chart |
| func (tr *TestRun) IsEmptyValues(chart string) bool { |
| return tr == nil || tr.Values == nil || tr.Values[chart] == nil |
| } |
| |
| // RemoveDataFromChart removes chart data from that TestRun |
| func (tr *TestRun) RemoveDataFromChart(chart string) { |
| if tr.Values != nil { |
| tr.Values[chart] = nil |
| } |
| } |
| |
| // PairwiseOrder indicates in a pairwise run, which commit ran first |
| type PairwiseOrder int |
| |
| const ( |
| // LeftThenRight means Left commit ran first, then the Right commit |
| LeftThenRight PairwiseOrder = 0 |
| // RightThenLeft means Right commit ran first, then the Left commit |
| RightThenLeft PairwiseOrder = 1 |
| ) |
| |
| // PairwiseTestRun stores pairwise benchmark test run. |
| type PairwiseTestRun struct { |
| // FirstTestRun is the first benchmark test run |
| FirstTestRun *TestRun |
| // SecondTestRun is the second benchmark test run |
| SecondTestRun *TestRun |
| // First indicates which commit ran first. |
| // First = Left commit or Right commit. |
| Permutation PairwiseOrder |
| } |
| |
| type BisectParams struct { |
| // BisectWorkflow reuses BisectRequest message |
| Request *pb.ScheduleBisectRequest |
| // Available bot list. |
| // This field is for internal use. Clients of BisectionWorkflow(s) are not expected to set it. |
| BotIds []string |
| // Production if true indicates the workflow is intended to be run on production |
| // and not the dev or staging environment. |
| // Used to determine whether to write to Pinpoint prod or staging. |
| Production bool |
| // JobID for the bisect run |
| JobID string |
| } |
| |
| // GetMagnitude returns the magnitude as float64. |
| // |
| // If the given string value is invalid or unable to parse, it returns the default 1.0. |
| func (bp *BisectParams) GetMagnitude() float64 { |
| if bp.Request.ComparisonMagnitude == "" { |
| return 1.0 |
| } |
| |
| magnitude, err := strconv.ParseFloat(bp.Request.ComparisonMagnitude, 64) |
| if err != nil { |
| return 1.0 |
| } |
| return magnitude |
| } |
| |
| // GetInitialAttempt returns the initial attempt as int32. |
| // |
| // If the given string value is invalid or unable to parse, it returns the default 0. |
| func (bp *BisectParams) GetInitialAttempt() int32 { |
| if bp.Request.InitialAttemptCount == "" { |
| return 0 |
| } |
| attempt, err := strconv.ParseInt(bp.Request.InitialAttemptCount, 10, 32) |
| if err != nil { |
| return 0 |
| } |
| if attempt < 0 { |
| return 0 |
| } |
| return int32(attempt) |
| } |
| |
| // GetImprovementDirection returns the improvement direction. |
| // |
| // Returns Unknown by default regardless of input. |
| func (bp *BisectParams) GetImprovementDirection() compare.ImprovementDir { |
| switch strings.ToLower(bp.Request.ImprovementDirection) { |
| case strings.ToLower(string(compare.Up)): |
| return compare.Up |
| case strings.ToLower(string(compare.Down)): |
| return compare.Down |
| } |
| return compare.UnknownDir |
| } |
| |
| type PairwiseParams struct { |
| // PairwiseWorkflow reuses SchedulePairwiseRequest proto request |
| Request *pb.SchedulePairwiseRequest |
| |
| // CulpritVerify when true states the pairwise job is a culprit |
| // verification job. |
| CulpritVerify bool |
| } |
| |
| // GetInitialAttempt returns the initial attempt as int32. |
| // |
| // Pairwise analysis needs to run an even number of commits to ensure an equal number |
| // of pairs where commit A goes first and commit B goes first. |
| // If the given string value is invalid or unable to parse, it returns the default 30. |
| func (pp *PairwiseParams) GetInitialAttempt() int32 { |
| if pp.Request.InitialAttemptCount == "" { |
| return defaultPairwiseAttemptCount |
| } |
| attempt, err := strconv.ParseInt(pp.Request.InitialAttemptCount, 10, 32) |
| // TODO(sunxiaodi@): Cover invalid input error cases upstream of this function call |
| if err != nil { |
| return defaultPairwiseAttemptCount |
| } |
| if attempt < 0 { |
| return defaultPairwiseAttemptCount |
| } |
| // use bit shifting to ensure response is always even |
| return int32((attempt + 1) >> 1 << 1) |
| } |
| |
| // GetImprovementDirection returns the improvement direction. |
| // |
| // Returns Unknown by default. |
| func (pp *PairwiseParams) GetImprovementDirection() compare.ImprovementDir { |
| switch strings.ToLower(pp.Request.ImprovementDirection) { |
| case strings.ToLower(string(compare.Up)): |
| return compare.Up |
| case strings.ToLower(string(compare.Down)): |
| return compare.Down |
| } |
| return compare.UnknownDir |
| } |
| |
| type CulpritFinderParams struct { |
| // CulpritFinderParams embeds the pinpoint proto ScheduleCulpritFinderRequest |
| Request *pb.ScheduleCulpritFinderRequest |
| } |