blob: ed6944828f642174582f00a995cc05cc2c4120fc [file] [log] [blame]
// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package cv.internal.run;
option go_package = "go.chromium.org/luci/cv/internal/run;run";
import "google/protobuf/timestamp.proto";
import "go.chromium.org/luci/cv/api/config/v2/config.proto";
import "go.chromium.org/luci/cv/internal/gerrit/storage.proto";
import "go.chromium.org/luci/cv/internal/run/eventpb/submission.proto";
import "go.chromium.org/luci/cv/internal/tryjob/storage.proto";
import "go.chromium.org/luci/cv/internal/tryjob/task.proto";
// Status describes the status of a CV Run.
enum Status {
// Unspecified status.
STATUS_UNSPECIFIED = 0;
// Run is pending to start.
//
// It is either because Run Manager hasn't processed the StartEvent yet or
// the RunOwner has exhausted all the quota and waiting for new quota to
// be available.
PENDING = 1;
// Run is running.
RUNNING = 2;
// Run is waiting for submission.
//
// Run is in this status if one of the following scenario is true:
// 1. Tree is closed at the time Run attempts to submit.
// 2. There is another Run in the same LUCI Project that is currently
// submitting.
// 3. The submission is rate limited according to the submit option in
// Project Config.
//
// This status is cancellable.
WAITING_FOR_SUBMISSION = 4;
// Run is submitting.
//
// A Run can't be cancelled while submitting. A Run may transition from
// this status to either `WAITING_FOR_SUBMISSION` status or a non-cancelled
// terminal status.
SUBMITTING = 5;
// End of non-terminal status; MUST have value less than `ENDED_MASK`.
/////////////////////////////////////////////////////////////////////////////
// Terminal Status
// ENDED_MASK can be used as a bitmask to check if a Run has ended.
// This MUST NOT be used as the status of a Run.
ENDED_MASK = 64;
// Run ends successfully.
SUCCEEDED = 65;
// Run ends unsuccessfully.
FAILED = 66;
// Run is cancelled.
CANCELLED = 67;
}
// Trigger describes who/how CV was triggered on a specific CL.
message Trigger {
reserved 5; // additional_label
google.protobuf.Timestamp time = 1;
// Mode is string value of run.Mode.
string mode = 2;
// ModeDefinition is the definition of the mode in case `mode` is not standard
// modes supported by LUCI CV (e.g. DRY_RUN, FULL_RUN, NEW_PATCHSET_RUN) but
// is provided through project configuration.
//
// This field is empty if `mode` is standard mode in LUCI CV.
cv.config.Mode mode_definition = 6;
// Triggering user email. Always known for Runs created since July 2021.
//
// Gerrit doesn't guarantee that every user has set their preferred email,
// but LUCI ACLs are based entirely on user emails. Thus, Project Manager will
// refuse starting Runs for users without the email.
string email = 3;
// Gerrit account ID. Always known.
int64 gerrit_account_id = 4;
}
// Triggers describes the triggers associated with a CL.
message Triggers {
// CQVoteTrigger is the trigger that corresponds to a vote on the
// `Commit-Queue` Gerrit label.
Trigger cq_vote_trigger = 1;
// NewPatchsetRunTrigger is the trigger that is created automatically by
// uploading a new patchset.
//
// This is only populated if the CL matches a config group that allows this.
Trigger new_patchset_run_trigger = 2;
}
// Submission describes the state of Run submission.
message Submission {
// The deadline of this submission.
//
// If the deadline is not set or has already expired, a RunManager task
// can claim the exclusive privilege by setting the deadline to a future
// timestamp (generally, end of task deadline).
google.protobuf.Timestamp deadline = 1;
// ID of the task that executes this submission.
string task_id = 2;
// IDs of all CLs that should be submitted in this submission.
//
// Must be ordered in submission order.
repeated int64 cls = 3;
// IDs of all CLs that have been submitted successfully already.
repeated int64 submitted_cls = 4;
// IDs of all CLs that fails to submit if any.
//
// CLs that are neither in this list nor in the `submitted_cls` should be
// treated as if CV has never attempted to submit them.
//
// This could be empty even when the entire submission fails, which would be
// typically caused by faulty infrastructure (e.g. Task Queue not executing
// a Run Manager task before the whole submission timeout is reached).
repeated int64 failed_cls = 5;
// If True, Tree is currently in open state.
bool tree_open = 10;
// The timestamp when the last attempt to fetch the Tree status occurred.
google.protobuf.Timestamp last_tree_check_time = 11;
// The timestamp when an attempt to fetch the Tree status first resulted in
// an error.
google.protobuf.Timestamp tree_error_since = 12;
}
// Options are Run-specific additions on top of LUCI project config.
message Options {
// If true, submitting the Run isn't blocked on open tree.
//
// If false (default), respects project configuration.
bool skip_tree_checks = 1;
// If true, `builders.equivalent_to{...}` sections are ignored when triggering
// tryjobs.
//
// If false (default), respects project configuration.
bool skip_equivalent_builders = 2;
// If true, no longer useful tryjobs won't be cancelled.
//
// If false (default), respects project configuration.
bool avoid_cancelling_tryjobs = 3;
// If true, no tryjobs will be triggered except "presubmit" regardless of
// project configuration.
//
// "presubmit" builders are legacy which are currently configured with
// "disable_reuse: true" in project config. To skip triggering them,
// skip_presubmit must be set to true.
// TODO(https://crbug.com/950074): ignore.
//
// If false (default), respects project configuration.
bool skip_tryjobs = 4;
// Deprecated per https://crbug.com/950074.
// See skip_tryjobs doc.
bool skip_presubmit = 5;
// Contains the directives to include specific builder in the Run.
//
// Its elements are strings of the form:
// project/bucket:builder1,builder2;project2/bucket:builder3
//
// Note that there may be duplication in the directives, it's up to the
// consumer of this information to handle it.
//
// Mutually exclusive with `overridden_tryjobs` option.
// This option is ignored if `skip_tryjobs` is true.
repeated string included_tryjobs = 6;
// Overrides all the Tryjobs triggered for this Run regardless of Project
// configuration.
//
// Its elements are strings of the form:
// project/bucket:builder1,builder2;project2/bucket:builder3
//
// Note that there may be duplication in the directives, it's up to the
// consumer of this information to handle it.
//
// Mutually exclusive with `included_tryjobs` option.
// This option is ignored if `skip_tryjobs` is true.
repeated string overridden_tryjobs = 8;
// Contains the custom Tryjob tags that should be added when launching
// Tryjobs for this Run.
//
// Each element SHOULD be in the format of "$tag_key:$tag_value" and the
// character set for tag key is [a-z0-9_\-].
repeated string custom_tryjob_tags = 7;
}
// LogEntries contains 1+ LogEntry ordered from logically oldest to newest.
message LogEntries {
repeated LogEntry entries = 1;
}
// LogEntry records what changed in a Run.
message LogEntry {
// Next tag: 7.
// Time is when something was changed.
google.protobuf.Timestamp time = 1;
oneof kind {
// Run was created.
Created created = 2;
// Run was started.
Started started = 6;
// Run updated to a new project config version.
ConfigChanged config_changed = 3;
// Tryjobs requirement was (re-)computed.
TryjobsRequirementUpdated tryjobs_requirement_updated = 4;
// Applicable tryjobs were updated.
TryjobsUpdated tryjobs_updated = 5;
// TODO(crbug/1232158): add & implement events related to Submission and
// ending of the Run.
// Intended for informational logs (E.g. temporary/during migration)
Info info = 7;
// The tree is configured and was checked.
TreeChecked tree_checked = 8;
// The run has been added to the submit queue's waitlist.
Waitlisted waitlisted = 9;
// The run is current on the queue.
AcquiredSubmitQueue acquired_submit_queue = 10;
ReleasedSubmitQueue released_submit_queue = 11;
// CL(s) submitted successfully.
CLSubmitted cl_submitted = 12;
// Submission failed.
SubmissionFailure submission_failure = 13;
RunEnded run_ended = 14;
}
message Created {
string config_group_id = 1;
}
message Started {
}
message ConfigChanged {
string config_group_id = 1;
}
message TryjobsRequirementUpdated {
// TODO(crbug/1227363): define a Tryjobs.Requirement diff.
}
message Info {
// If you have the need to add fields here, consider instead adding a new
// dedicated kind
string label = 1;
string message = 2;
}
message TryjobsUpdated {
// Which tryjobs had a meaningful change (e.g. change of status).
repeated Tryjob tryjobs = 2;
}
message TreeChecked{
bool open = 1;
}
message Waitlisted{
}
message AcquiredSubmitQueue{
}
message ReleasedSubmitQueue{
}
message CLSubmitted{
// The CLs that were submitted in this event.
repeated int64 newly_submitted_cls = 1;
// The number of CLs submitted for this run, so far.
int64 total_submitted = 2;
}
message SubmissionFailure{
cv.internal.run.eventpb.SubmissionCompleted event = 1;
}
message RunEnded{
}
}
// Tryjobs is the state of Run's tryjobs.
message Tryjobs {
// Requirement is what has to happen to verify a given Run.
cv.internal.tryjob.Requirement requirement = 1;
// StagingRequirement will be promoted to requirement.
//
// It is typically set when the existing requirement is executing and waiting
// to be cancelled. For example:
// T0: Run starts and computes the requirement. A long op task is executing
// the requirement.
// T1: A new config is ingested and results in a new requirement. RM
// requests a cancellation for the long op task and set the new
// requirement to this field.
// T2: The long op task is successfully cancelled. RM promotes the
// staging requirement to requirement and enqueue a new long op task to
// execute the new requirement.
cv.internal.tryjob.Requirement staging_requirement = 4;
// RequirementVersion increments by 1 every time this requirement changes.
//
// Starts with 1.
int32 requirement_version = 6;
// The timestamp when the requirement is last computed.
//
// Every requirement computation will update this field even if the result
// requirement is the same as the existing one.
google.protobuf.Timestamp requirement_computed_at = 7;
// Tryjobs tracks tryjobs of a Run.
//
// It may contain Tryjobs which are no longer required.
// It does contain all Tryjobs which weren't reused even if no longer
// required.
//
// TODO(crbug/1227363): replace this field in favor of `state`
repeated Tryjob tryjobs = 2 [deprecated = true];
// The timestamp of the CQDaemon report last incorporated into `tryjobs`.
//
// TODO(crbug/1227523): delete this field.
google.protobuf.Timestamp cqd_update_time = 3;
// State is the latest state reported by Tryjob Executor task.
//
// Once the Run is ended, this state is finalized.
cv.internal.tryjob.ExecutionState state = 5;
}
// Tryjob represents a Run's view of a tryjob.
message Tryjob {
cv.internal.tryjob.Definition definition = 1;
// ID is a CV internal Tryjob ID, corresponding to a Datastore entity.
//
// During migration from CQDaemon, the ID may be not set but then ExternalID
// is set.
// TODO(crbug/1227523): make this field required.
int64 id = 2;
// EVersion of the Tryjob entity last observed by this Run.
int64 eversion = 3;
// ExternalID is the external job ID.
//
// It's kept here for ease of URL generation and to ease migration from
// CQDaemon.
// TODO(crbug/1227523): update comment above after CQDaemon migration.
string external_id = 4;
// Status of the Tryjob.
cv.internal.tryjob.Status status = 5;
// Reused is true, if this tryjob wasn't triggered by CV for this Run.
//
// In other words, either:
// * tryjob was triggered by CV for a previous Run
// * tryjob was triggered by non-CV.
bool reused = 6;
// If true, indicates this tryjob must pass in order for the Run to be
// considered successful.
//
// It is typically true when the tryjob is NOT experimental or triggered
// because of `Cq-Include-TryBot` git footer.
bool critical = 9;
// Result of the tryjob.
cv.internal.tryjob.Result result = 7;
// If true, this Tryjob was computed based on CQDaemon's input.
//
// TODO(crbug/1227523): delete after CQDaemon migration.
bool cqd_derived = 8;
}
// OngoingLongOps tracks ongoing long operations.
message OngoingLongOps {
message Op {
reserved 4; // cancel_triggers
// Deadline best-effort limits the lifetime of this work.
//
// Run Manager expects to receive a LongOpCompleted event by this deadline.
google.protobuf.Timestamp deadline = 1;
// If true, signals to the Long Op handler that it should stop as soon
// possible.
bool cancel_requested = 2;
message ResetTriggers {
// Request is to request resetting the trigger of a CL.
message Request {
// internal CLID.
int64 clid = 1;
// Message explains why trigger is reset. Will be posted to the CL.
string message = 2;
// Whom to notify.
repeated gerrit.Whom notify = 3;
// Whom to add to the attention set.
repeated gerrit.Whom add_to_attention = 4;
// Reason explains the reason of attention set change.
string add_to_attention_reason = 5;
}
repeated Request requests = 1;
// The status Run will transition to if all triggers are successfully
// cancelled.
//
// Must be one of the terminal statues.
Status run_status_if_succeeded = 2;
}
// ExecutePostActionPayload is the payload of a long-op task that executes
// a given post action.
message ExecutePostActionPayload {
reserved 1; // action
// Name of the post action.
//
// For config action, it should be the same as the name in the config
// action.
string name = 2;
message CreditRunQuota {}
oneof kind {
// ConfigAction is the post action defined in the config group.
cv.config.ConfigGroup.PostAction config_action = 3;
// CreditRunQuota credits the quota back to the run creator and ask
// next run that is blocked on the quota to start.
CreditRunQuota credit_run_quota = 4;
}
}
// PostGerritMessage is the payload for posting a bot message within
// a run's gerrit CLs.
message PostGerritMessage {
// Message to post to gerrit. The current implementation treats the
// message as a unique key to achieve idempotence. If multiple payloads
// with the same message is triggered for the same run, only one would
// be posted.
string message = 1;
}
oneof work {
// If true, posts a start message.
bool post_start_message = 3;
ResetTriggers reset_triggers = 6;
cv.internal.tryjob.ExecuteTryjobsPayload execute_tryjobs = 5;
// The PostActions from different runs can race each other in case
// the actions from an earlier Run transiently failed and are being
// retried. However, the resolution depends on the type of the post
// action.
//
// If the action is to export the Run records to BQ, each of the Run
// records should be exported, and CV should still attempt to execute post
// actions for all the Runs ever triggered.
//
// In contrast, if the action is to vote a label, the post action handler
// should check if the originated patchset is still the latest at the time
// of the long-op processing time, and skip the action, if the patchset
// is no longer the latest.
ExecutePostActionPayload execute_post_action = 7;
// Post a custom message to all the CLs linked to a run.
PostGerritMessage post_gerrit_message = 8;
}
}
// Ops map operation ID to details.
map<string, Op> ops = 1;
}