| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Package track implements shared tracking functionality for the Tricium service modules. |
| // |
| // Overview diagram: |
| // |
| // +-----------------+ |
| // |AnalyzeRequest | |
| // |id=<generated_id>| |
| // +---+-------------+ |
| // | |
| // +----------------------+ |
| // | | |
| // +---+----------------+ +---+-------+ |
| // |AnalyzeRequestResult| |WorkflowRun| |
| // |id=1 | |id=1 | |
| // +---+----------------+ +-----------+ |
| // | |
| // +----------------------+ |
| // | | |
| // +---+-------------+ +---+-------------+ |
| // |WorkflowRunResult| |FunctionRun | |
| // |id=1 | |id=<functionName>| |
| // +-----------------+ +---+-------------+ |
| // | |
| // +-------------------+ |
| // | | |
| // +---+-------------+ +---+----------------------+ |
| // |FunctionRunResult| |WorkerRun | |
| // |id=1 | |id=<functionName_platform>| |
| // +-----------------+ +---+----------------------+ |
| // | |
| // +--------+---------+ |
| // | | |
| // +---+-----------+ +----+------------+ |
| // |WorkerRunResult| |Comment | |
| // |id=1 | |id=<generated_id>| |
| // +---------------+ +----+------------+ |
| // | |
| // +------------------+ |
| // | | |
| // +--+-------------+ +--+------------+ |
| // |CommentSelection| |CommentFeedback| |
| // |id=1 | |id=1 | |
| // +----------- --+ +---------------+ |
| // |
| package track |
| |
| import ( |
| "bytes" |
| "context" |
| "strings" |
| "time" |
| |
| "github.com/golang/protobuf/jsonpb" |
| "go.chromium.org/luci/common/errors" |
| ds "go.chromium.org/luci/gae/service/datastore" |
| |
| tricium "infra/tricium/api/v1" |
| ) |
| |
| // AnalyzeRequest represents one Tricium Analyze RPC request. |
| // |
| // Immutable root entry. |
| type AnalyzeRequest struct { |
| // LUCI datastore ID field with generated value. |
| ID int64 `gae:"$id"` |
| // Time when the corresponding request was received. |
| Received time.Time |
| // The name of the project in luci-config that specifies |
| // the configuration and project details that are used in |
| // this analyze request. |
| Project string |
| // Files listed in the request, including metadata. |
| Files []tricium.Data_File `gae:",noindex"` |
| // Paths is retained for backward compatibility but should not be used |
| // in new entities. Files is used instead. See crbug.com/934246. |
| Paths []string `gae:",noindex"` |
| // Full URL of Git repository hosting files in the request. |
| GitURL string `gae:",noindex"` |
| // Git ref to use in the git repo. |
| GitRef string `gae:",noindex"` |
| // Gerrit details if applicable. |
| // GerritHost and GerritChange can be used to uniquely identify a Gerrit |
| // change; these fields are indexed to enable querying for all runs for a |
| // particular change. |
| GerritHost string |
| GerritProject string `gae:",noindex"` |
| // GerritChange includes project, branch, and Change-Id footer. |
| GerritChange string |
| // Disabled Gerrit reporting means that no progress or result messages |
| // are sent to Gerrit. |
| GerritReportingDisabled bool `gae:",noindex"` |
| // Commit message provided by Gerrit if applicable. This may not be present |
| // in older entities, and may be empty for non-Gerrit requests. |
| CommitMessage string `gae:",noindex"` |
| } |
| |
| // AnalyzeRequestResult tracks the state of a tricium.Analyze request. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to AnalyzeRequest entity) fields. |
| type AnalyzeRequestResult struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // State of the Analyze request; running, success, or failure. |
| State tricium.State |
| } |
| |
| // WorkflowRun declares a request to execute a Tricium workflow. |
| // |
| // Immutable root of the complete workflow execution. |
| // LUCI datastore ID (=1) and parent (=key to AnalyzeRequest entity) fields. |
| type WorkflowRun struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Name of analyzers included in this workflow. |
| // |
| // Included here to allow for direct access without queries. |
| Functions []string `gae:",noindex"` |
| // DEPRECATED, ignored. |
| IsolateServerURL string `gae:",noindex"` |
| // DEPRECATED, ignored. |
| SwarmingServerURL string `gae:",noindex"` |
| // Buildbucket server hostname. |
| BuildbucketServerHost string `gae:",noindex"` |
| } |
| |
| // WorkflowRunResult tracks the state of a workflow run. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to WorkflowRun entity) fields. |
| type WorkflowRunResult struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // State of the parent request; running, success, or failure. |
| // |
| // This state is an aggregation of the run state of triggered analyzers. |
| State tricium.State |
| // Number of comments produced for this analyzer. |
| // If results were merged, then this is the merged number of results. |
| NumComments int |
| // If the results for this analyzer were merged. |
| HasMergedResults bool |
| } |
| |
| // FunctionRun declares a request to execute an analyzer. |
| // |
| // Immutable entity. |
| // LUCI datastore ID (="FunctionName") and parent (=key to WorkflowRun entity) fields. |
| type FunctionRun struct { |
| ID string `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Name of workers launched for this function. |
| // |
| // Included here to allow for direct access without queries. |
| Workers []string `gae:",noindex"` |
| // Owner email and monorail component for this function. |
| // |
| // Included here for convenience so that this information can |
| // later be used to populate a bug filing template. |
| |
| // Cached here from the Function instance so this information can |
| // later be used to populate a bug filing template, even if the |
| // Function instance changed in the meantime. |
| Owner string `gae:",noindex"` |
| MonorailComponent string `gae:",noindex"` |
| } |
| |
| // FunctionRunResult tracks the state of an analyzer run. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to FunctionRun entity) fields. |
| type FunctionRunResult struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Name of analyzer. |
| // |
| // Added here in addition to in the parent key for indexing. |
| Name string |
| // State of the parent analyzer run; running, success, or failure. |
| // |
| // This state is an aggregation of the run state of triggered analyzer workers. |
| State tricium.State |
| |
| // Number of comments produced for this analyzer. |
| // |
| // If results were merged, then this is the merged number of results. |
| NumComments int |
| |
| // If the results for this analyzer were merged. |
| HasMergedResults bool |
| } |
| |
| // WorkerRun declare a request to execute an analyzer worker. |
| // |
| // Immutable entity. |
| // LUCI datastore ID (="WorkerName") and parent (=key to FunctionRun entity) fields. |
| type WorkerRun struct { |
| ID string `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Platform this worker is producing results for. |
| Platform tricium.Platform_Name |
| // Names of workers succeeding this worker in the workflow. |
| Next []string `gae:",noindex"` |
| } |
| |
| // WorkerRunResult tracks the state of a worker run. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to WorkerRun entity) fields. |
| type WorkerRunResult struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Name of worker. |
| // |
| // Stored here, in addition to in the parent ID, for indexing |
| // and convenience. |
| Name string |
| // Name of the function for this worker run. |
| // |
| // Stored here, in addition to in the ID of ancestors, for indexing |
| // and convenience. |
| Function string |
| // Platform this worker is running on. |
| Platform tricium.Platform_Name |
| // State of the parent worker run; running, success, or failure. |
| State tricium.State |
| // DEPRECATED, ignored. |
| IsolatedInput string `gae:",noindex"` |
| |
| // Outputs: BuildbucketOutput |
| |
| // DEPRECATED, ignored. |
| IsolatedOutput string `gae:",noindex"` |
| // Output as collected from the corresponding buildbucket run. |
| BuildbucketOutput string `gae:",noindex"` |
| // DEPRECATED, ignored. |
| SwarmingTaskID string `gae:",noindex"` |
| // Buildbucket build ID. |
| BuildbucketBuildID int64 `gae:",noindex"` |
| // Number of comments produced by this worker. |
| NumComments int `gae:",noindex"` |
| // Tricium result encoded as JSON. |
| Result string `gae:",noindex"` |
| } |
| |
| // Comment tracks a comment generated by a worker. |
| // |
| // Immutable entity. |
| // LUCI datastore ID (=generated) and parent (=key to WorkerRun entity) fields. |
| type Comment struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Comment UUID. |
| // |
| // This is the external ID for the comment and the ID used in any external |
| // communication about the comment by the service. For instance, |
| // this is the ID used to report feedback for a comment. |
| UUID string |
| // Comment creation time. |
| // |
| // Comment creation time in terms of when it is tracked in the service not |
| // when it is created by the analyzer. This timestamp allows for filtering |
| // on time when summarizing analyzer feedback. |
| CreationTime time.Time |
| // Comment encoded as JSON. |
| // |
| // The comment must be an encoded tricium.Data_Comment JSON message |
| Comment []byte `gae:",noindex"` |
| // Analyzer function name. |
| // |
| // This field allows for filtering on analyzer name. |
| Analyzer string |
| // Comment category with subcategories. |
| // |
| // This includes the analyzer name, e.g., "ClangTidy/llvm-header-guard". |
| Category string |
| // Platforms this comment applies to. |
| // |
| // This is a int64 bit map using the tricium.Platform_Name number |
| // values for platforms. |
| Platforms int64 |
| } |
| |
| // UnpackComment returns the proto encoded Comment. |
| func (c *Comment) UnpackComment(ctx context.Context, out *tricium.Data_Comment) error { |
| if c.Comment == nil { |
| return errors.New("missing comment.Comment") |
| } |
| if err := jsonpb.Unmarshal(bytes.NewReader(c.Comment), out); err != nil { |
| return errors.Annotate(err, "failed to unpack proto tricium.Data_Comment").Err() |
| } |
| return nil |
| } |
| |
| // CommentSelection tracks selection of comments. |
| // |
| // When an analyzer has several workers running the analyzer using different |
| // configurations the resulting comments are merged to avoid duplication of |
| // results for users. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to Comment entity) fields. |
| type CommentSelection struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Whether this comments was included in the overall result of the |
| // enclosing request. |
| // |
| // All comments are included by default, but comments may need to be |
| // merged in the case when comments for a category are produced for |
| // multiple platforms. |
| Included bool |
| } |
| |
| // CommentFeedback tracks 'not useful' user feedback for a comment. |
| // |
| // Mutable entity. |
| // LUCI datastore ID (=1) and parent (=key to Comment entity) fields. |
| type CommentFeedback struct { |
| ID int64 `gae:"$id"` |
| Parent *ds.Key `gae:"$parent"` |
| // Number of 'not useful' clicks. |
| // Note that this doesn't store reporter information, so multiple clicks by |
| // the same user would all be counted. In practice the Tricium plugin |
| // disables the report button after one click so this usually isn't an |
| // issue. |
| NotUsefulReports int |
| } |
| |
| const workerSeparator = "_" |
| |
| // ExtractFunctionPlatform extracts the analyzer and platform name from a |
| // worker name. |
| // |
| // The worker name must be on the form 'FunctionName_PLATFORM'. |
| func ExtractFunctionPlatform(workerName string) (string, string, error) { |
| parts := strings.SplitN(workerName, workerSeparator, 2) |
| if len(parts) != 2 { |
| return "", "", errors.Reason("malformed worker name: %s", workerName).Err() |
| } |
| return parts[0], parts[1], nil |
| |
| } |