blob: 77951c750d366cf15cd2976098b498d94756aafc [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.prjmanager.prjpb;
option go_package = "go.chromium.org/luci/cv/internal/prjmanager/prjpb;prjpb";
import "google/protobuf/timestamp.proto";
import "go.chromium.org/luci/cv/internal/changelist/storage.proto";
import "go.chromium.org/luci/cv/internal/run/storage.proto";
// This file keeps serializable state of PM (Project Manager).
//
// For clarity in Project Manager's code itself, a few message names start with
// "P", e.g. "PCL" instead of "CL".
//
// It's mutated in ../impl/state package and ultimately stored as Project's
// datastore entity. Logically, this proto is part of the impl of the
// ../impl/state, but it's a separate package to resolve import cycle
// because ../impl/state is importing prjmanager/ package.
// PState is the PM state of a specific LUCI project.
//
// Semantically, it's a collection of CLs somehow grouped into components (see
// Component message below), each of which may have several active (a.k.a.
// Incomplete) Runs valid at a specific project's config version.
//
// Most CLs are watched by the LUCI project, but to assist with error reporting,
// it also tracks unwatched CLs if they are dependencies of some actually
// watched CLs.
message PState {
// Name of LUCI project.
string luci_project = 1;
// Status of the Project.
Status status = 2;
// Config hash pins specific project config version.
string config_hash = 3;
// Config group names intern the names referenced in PCL entities to reduce
// memory and at-rest footprint.
//
// See also https://en.wikipedia.org/wiki/String_interning.
repeated string config_group_names = 4;
// PCLs are currently tracked CLs.
//
// Includes deps which are of not yet known kind (because CL doesn't yet have
// a snapshot) or unwatched.
//
// Sorted by CL ID.
repeated PCL pcls = 11;
// Components are a partition of CLs in the list above.
//
// An active CL (watched or used to be watched and still member of a Run) may
// belong to at most 1 component, while unwatched dep may be referenced by
// several.
repeated Component components = 12;
// PurgingCLs are CLs currently being purged.
//
// They are tracked in PState to avoid creating Runs with such CLs.
//
// A CL being purged does not necessarily have a corresponding PCL.
// A PurgingCL is kept in PState until purging process stops, regardless of
// whether purging was successful or failed.
//
// See more in PurgingCL doc.
//
// Sorted by CL ID.
repeated PurgingCL purging_cls = 13;
// If true, components partition must be redone as soon as possible.
bool repartition_required = 21;
// PRuns which can't yet be added to any component but should be. Sorted by
// Run ID.
//
// In response to OnRunCreated event, PM may append to this list new Runs if
// either:
// * not all Run's CLs are already known to PM;
// * Run's CLs are currently partitioned into different components.
//
// Thus,
// * CLs referenced by these PRuns may not be tracked;
// * If this field is not empty, re-partioning may be required.
repeated PRun created_pruns = 22;
// If set, establishes when components should be re-evaluated.
google.protobuf.Timestamp next_eval_time = 23;
}
enum Status {
STATUS_UNSPECIFIED = 0;
STARTED = 1;
STOPPING = 2;
STOPPED = 3;
}
// LogReason records why a change to the project state was logged.
//
// See ProjectLog entity.
enum LogReason {
LOG_REASON_UNSPECIFIED = 0;
// Due to passage of time or number of versions.
FYI_PERIODIC = 1;
STATUS_CHANGED = 2;
CONFIG_CHANGED = 3;
// On-demand save for debugging reasons, e.g. on caught panic.
DEBUG = 4;
}
message LogReasons {
repeated LogReason reasons = 1;
}
// PCL is a tracked CL.
message PCL {
// next tag: 18
reserved 12; // Trigger (single), replaced by Triggers.
reserved 14;
int64 clid = 1;
int64 eversion = 2;
enum Status {
option allow_alias = true;
PCL_STATUS_UNSPECIFIED = 0;
// OK means CL metadata below is correct and CL is watched by this project.
//
// Value 0 is chosen such that it's not serialized, since this is the most
// common state.
OK = 0;
// UNKNOWN means Datastore CL entity doesn't have the info yet.
UNKNOWN = 1;
// UNWATCHED means CL isn't watched by this LUCI project.
UNWATCHED = 2;
// DELETED means CL's Datastore entity got deleted.
//
// This is used to temporary mark a PCL before deleting it entirely from
// PState to avoid dangling references from components.
DELETED = 3;
}
Status status = 3;
// Indexes in PState.config_group_names identifying ConfigGroup which watches
// this CL.
//
// Normally, contains exactly 1 index.
// May have > 1 index, which means 2+ non-fallback config groups watch this
// CL, which is not allowed and will be signalled to CV users.
// TODO(tandrii): move >1 index case to be tracked via `errors` field.
repeated int32 config_group_indexes = 4;
// Deps refers to CLs in PState.PCLs which are dependencies of the PCL.
repeated changelist.Dep deps = 11;
// Triggers are the triggers currently active on the CL.
//
// Note that due to NewPatchsetRuns, CLs can be associated with more than one
// Run at any given time, for this reason, it is required that a PCL be able
// to track multiple triggers.
//
// It may be empty if CL is not triggered but nevertheless tracked as either:
// * a dependency of another CL.
// * previously triggered member of an incomplete Run, which is probably
// being finalized right now by its Run Manager.
//
// Doesn't store email nor Gerrit account ID.
cv.internal.run.Triggers triggers = 16;
// Submitted means CV isn't going to work on a CL, but CL is still tracked as
// a dep of another CL or as a member of an incomplete Run (though the other
// Run will probably finish soon).
bool submitted = 13;
// Deprecated in favor of purge_reasons.
repeated cv.internal.changelist.CLError errors = 15 [deprecated=true];
// If set, describes problems that require that some or all of the PCL's
// triggers be purged.
repeated PurgeReason purge_reasons = 17;
}
// PRun is an incomplete Run on which CV is currently working.
//
// It is referenced by at most 1 component.
message PRun {
// CV's Run ID.
string id = 1;
// IDs of CLs involved. Sorted.
//
// Actual Run may orders its CLs in a different way.
repeated int64 clids = 2;
// The mode of the Run referenced by this message.
//
// It uses the string value of run.Mode.
string mode = 3;
}
// Component is a set of CLs related to each other.
message Component {
// CL IDs of the tracked CLs in this component. Sorted.
//
// Each referenced CL must be in PState.PCLs list.
// Each referenced CL may have deps not in this list if they are either
// PCL.Status.UNKNOWN or PCL.Status.UNWATCHED.
//
// A referenced CL is normally watched by this LUCI project. In rare cases,
// referenced CL is no longer watched by this LUCI project but is still kept
// in a component because the CL is still a member of an incomplete Run in
// this component. In this case, the CL's deps are no longer tracked.
repeated int64 clids = 1;
// Decision time is the earliest time when this component should be
// re-evaluated.
//
// Can be set to far future meaning no need for re-evaluation without an
// external event (e.g., CLUpdated or RunFinished).
google.protobuf.Timestamp decision_time = 2;
// Incomplete Runs working on CLs from this component.
//
// Sorted by Run's ID.
repeated PRun pruns = 3;
// If true, this component must be triaged as soon as possible.
bool triage_required = 11;
}
// PurgingCL represents purging of a CL due to some problem.
//
// The purging process is initiated during PM state mutation while atomically
// adding a TQ task to perform the actual purge.
//
// Purging itself constitutes removing whatever triggered CV on a CL as well as
// posting the reason for purging to the user.
//
// Individual CLs are purged independently, even if CLs are related.
//
// Trying to purge multiple triggers of the same type on the same CL will
// result in only purging one of them. This is not possible today, but may have
// an unexpected effect on future features.
//
// Upon TQ task completion, the task handler notifies PM back via an
// PurgeCompleted event. For fail-safe reasons, there is a deadline to
// perform the purge. PM keeps the PurgingCL in PState until either deadline is
// reached OR PurgeCompleted event is received.
message PurgingCL {
// CL ID which is being purged.
int64 clid = 1;
// Operation ID is a unique within a project identifier of a purge operation
// to use in PurgeCompleted events.
string operation_id = 2;
// Deadline is obeyed by the purging TQ task.
//
// TQ task SHOULD not modify a CL (e.g. via Gerrit RPCs) beyond this point.
// This is merely best effort, as an RPC to external system initiated before
// this deadline may still complete after it.
//
// If PM doesn't receive PurgeCompleted event before this deadline + some grace
// period, PM will consider purge operation expired and it'll be removed from
// PState.
google.protobuf.Timestamp deadline = 3;
// What trigger(s) are being purged.
oneof apply_to {
cv.internal.run.Triggers triggers = 4;
bool all_active_triggers = 5;
}
}
// PurgeReason represent a list of errors with the CL that would require that
// the given trigger (or the whole CL if no trigger is given) be purged.
//
// Trying to purge multiple triggers of the same type on the same CL will
// result in only purging one of them. This is not possible today, but may have
// an unexpected effect on future features.
message PurgeReason {
// ClErrors are a list of errors associated with Trigger below, or with the
// a whole CL if Trigger is not given.
cv.internal.changelist.CLError cl_error = 1;
// What trigger(s) the error above applies to.
oneof apply_to {
cv.internal.run.Triggers triggers = 2;
bool all_active_triggers = 3;
}
}