blob: 17505261629dab24048c437168201389917f15bb [file] [log] [blame]
// Copyright 2021 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.tryjob;
option go_package = "go.chromium.org/luci/cv/internal/tryjob;tryjob";
import "google/protobuf/timestamp.proto";
import "go.chromium.org/luci/buildbucket/proto/builder_common.proto";
import "go.chromium.org/luci/buildbucket/proto/common.proto";
import "go.chromium.org/luci/cv/api/config/v2/cq.proto";
import "go.chromium.org/luci/cv/api/recipe/v1/cq.proto";
// Definition defines what a Tryjob should do.
//
// It must be sufficient to trigger a new tryjob.
message Definition {
oneof backend {
// For buildbucket, it's just a builder.
Buildbucket buildbucket = 1;
}
message Buildbucket {
string host = 1;
buildbucket.v2.BuilderID builder = 2;
}
// If set, existing tryjob matching this definition is deemed equivalent
// to the primary Definition.
//
// Note that recursive `equivalent_to` is not supported. Also, this doesn't
// affect triggering a new tryjob.
Definition equivalent_to = 2;
// If true, trigger a new tryjob using this definition regardless of whether
// reusable tryjobs exist.
bool disable_reuse = 3;
// If true, CV should let this tryjob run even if it becomes stale,
// e.g. by a new non-trivial patchset on one of this tryjob's CLs.
//
// If the Tryjob is not triggered by CV, then this is value is ignored and
// in effect it's assumed true. I.e. we don't cancel tryjobs not triggered
// by CV.
bool skip_stale_check = 4;
// If true, this tryjob is deemed critical for the Run to succeed, i.e.
// the Run will fail unless one of the attempts of this tryjob is successful.
bool critical = 5;
// If true, this tryjob will be marked as experimental.
//
// However, experimental tryjob could be critical iff it is requested
// explicitly (i.e. via git footer).
bool experimental = 7;
// If set to restricted, CV should only post generic messages such as
// "Build failed: https://ci.chromium.org/b/1234"
// and no summary markdown about tryjobs with this definition.
cv.config.CommentLevel result_visibility = 6;
}
// Requirement is what has to happen to verify a specific Run.
//
// It is computed based on the Project Config and specifics of the Run.
message Requirement {
// Definitions is the definitions of all Tryjobs that should be triggered
// to verify a specific Run.
repeated Definition definitions = 1;
// RetryConfig specifies retries allowed in case of Tryjob failure.
//
// No retry allowed if nil.
cv.config.Verifiers.Tryjob.RetryConfig retry_config = 2;
// Version increments by 1 every time this requirement changes.
//
// Starts with 1.
int32 version = 3;
// The timestamp when 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 last_computed_at = 4;
}
// Status is a high level status of a Tryjob from CV implementation PoV.
enum Status {
// STATUS_UNSPECIFIED is never used.
STATUS_UNSPECIFIED = 0;
// PENDING means Tryjob is being triggered by CV.
//
// *may* not yet have an external ID.
// *must* have no Result.
PENDING = 1;
// TRIGGERED means Tryjob was triggered.
//
// *must* have an External ID.
// *may* have been triggered not by CV, but by another user, service, etc.
// *may* have a Result, which *may* still change.
TRIGGERED = 2;
// ENDED means the Tryjob completed. Final status.
//
// *must* have an External ID.
// *must* have a Result, whose Status is not UNKNOWN.
ENDED = 3;
// CANCELLED means Tryjob was cancelled by CV. Final status.
//
// *must* have an External ID.
// *must* have no Result.
CANCELLED = 4;
// UNTRIGGERED means Tryjob was never triggered. Final state.
//
// *must* have no External ID.
// *must* have no Result.
//
// This status is an implementation detail of CV, used for Tryjobs which
// weren't actually triggered.
//
// TODO(crbug/1227363): add a field to the Tryjob model to record reason for
// this status, notably to record permission denied errors.
UNTRIGGERED = 5;
}
// Result of a Tryjob.
//
// It's interpreted by the Run Manager.
message Result {
// Next tag: 6.
// Status of the Result.
//
// This is the verdict of verification of Run's CLs by this Tryjob.
Status status = 1;
enum Status {
// RESULT_STATUS_UNSPECIFIED is never used.
RESULT_STATUS_UNSPECIFIED = 0;
// UNKNOWN means Tryjob didn't reach a conclusion.
//
// *must* be used only if Tryjob.Status is TRIGGERED and the Tryjob
// hasn't made a decision yet.
UNKNOWN = 1;
// SUCCEEDED means that Run's CLs are considered OK by this Tryjob.
SUCCEEDED = 2;
// FAILED_PERMANENTLY means that Run's CLs are most likely not good.
FAILED_PERMANENTLY = 3;
// FAILED_TRANSIENTLY means that Run's CLs are most likely not to blame
// for the failure.
// TODO(crbug/1227363): consider removing transiency aspect if possible.
FAILED_TRANSIENTLY = 4;
// TIMEOUT means the Tryjob ran over some deadline and did not make a
// decision about this Run's CLs.
TIMEOUT = 5;
}
// Time when the Tryjob was created in the backend.
//
// This is used by CV to determine if the Tryjob is fresh enough to be used
// to verify a Run.
google.protobuf.Timestamp create_time = 2;
// Time when the tryjob was last updated in the backend.
//
// This is used by CV to determine if it needs to refresh Tryjob's Result by
// querying its backend.
google.protobuf.Timestamp update_time = 3;
// Output is a rich result of a Tryjob.
// This includes details related to retry and reuse.
//
// It's typically set by LUCI recipes.
cq.recipe.Output output = 4;
// Backend houses backend-specific output.
oneof backend {
Buildbucket buildbucket = 5;
}
message Buildbucket {
int64 id = 1;
buildbucket.v2.BuilderID builder = 4;
buildbucket.v2.Status status = 2;
// SummaryMarkdown is a field containing a human readable summary of the
// build's result.
string summary_markdown = 3;
}
}
// ExecutionState is the state of executing Tryjobs requirement.
message ExecutionState {
// Execution tracks the execution state of each individual Tryjob requirement.
message Execution {
// Attempt represents each attempt to complete an execution.
//
// Failed attempt will trigger a retry attempt if quota allows.
message Attempt {
// TryjobId is the id of this attempt of the Tryjob.
int64 tryjob_id = 1;
// ExternalID is the external ID of this attempt of the Tryjob
string external_id = 2;
// Status is the status of this attempt of the Tryjob.
cv.internal.tryjob.Status status = 3;
// Result of this attempt of the Tryjob.
cv.internal.tryjob.Result result = 4;
// Reused indicates the current attempt of the Tryjob wasn't triggered
// by CV for this Run specifically.
//
// In other words, either:
// * tryjob was triggered by CV for a previous Run
// * tryjob was triggered by non-CV (e.g. `git cl try`)
bool reused = 5;
// TODO(yiwzhang): add model for children tryjobs. It could reuse
// `Attempt` messages, it may also have a trimmed version of `Attempt`
// message because some of the fields don't make sense to children
// Tryjobs. Also, if crbug.com/1031205 is shipped before we implement
// children support in LUCI CV, we may not even need a new field
// (maybe a new status will suffice: like WAITING_FOR_CHILDREN).
}
// Attempts records all attempts (including retries) to verify the Run with
// this Tryjob Execution.
repeated Attempt attempts = 2;
// UsedQuota is the quota consumed for retrying the execution of this
// Tryjob.
int32 used_quota = 3;
}
// Status describes the summarized status of the overall tryjob execution.
enum Status {
// Unspecified is never used.
STATUS_UNSPECIFIED = 0;
// At least one critical tryjob is not yet finished so its result is
// unknown.
RUNNING = 1;
// At least one critical tryjob in the state failed and cannot be retried.
FAILED = 2;
// All the critical tryjobs in the execution state succeeded.
SUCCEEDED = 3;
}
// Executions track the execution state of all required Tryjobs.
//
// It is one to one mapped to `requirement.definitions`
repeated Execution executions = 1;
// Requirement is the requirement that is currently being worked on.
Requirement requirement = 2;
// Status will be set to SUCCEEDED if all tryjobs succeed,
// FAILED if at least one tryjob fails and cannot be retried,
// or RUNNING otherwise.
Status status = 3;
// FailureReason provides a user-readable explanation of the
// failure.
string failure_reason = 4;
}
// TryjobUpdatedEvent describes which Tryjob entity is updated.
message TryjobUpdatedEvent {
int64 tryjob_id = 1;
}
// TryjobUpdatedEvents is a batch of TryjobUpdatedEvent.
message TryjobUpdatedEvents {
repeated TryjobUpdatedEvent events = 1;
}