blob: 1d029ebc1bbeb751c164619be36ae9c929e16366 [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.
package run
import (
"time"
"go.chromium.org/luci/auth/identity"
"go.chromium.org/luci/gae/service/datastore"
cfgpb "go.chromium.org/luci/cv/api/config/v2"
"go.chromium.org/luci/cv/internal/changelist"
"go.chromium.org/luci/cv/internal/common"
"go.chromium.org/luci/cv/internal/configs/prjcfg"
)
const (
// RunCLKind is the Datastore entity kind for RunCL.
RunCLKind = "RunCL"
// RunLogKind is the Datastore entity kind for RunLog.
RunLogKind = "RunLog"
// MaxTryjobs limits maximum number of tryjobs that a single Run can track.
//
// Because all Run's tryjobs' results are kept in a Run.Tryjobs,
// a single Datastore entity size limit of 1 MiB applies.
//
// 1024 is chosen because:
// * it is a magnitude above what was observed in practice before;
// * although stored tryjob results are user-influenced,
// they should be highly compressible, so even at 1KiB each,
// 1024 tryjob results should amount to much less than 1 MiB.
MaxTryjobs = 1024
)
// Run is an entity that contains high-level information about a CV Run.
//
// In production, Run entities are created only by the "runcreator" package.
//
// Detailed information about CLs and Tryjobs are stored in its child entities.
type Run struct {
// $kind must match common.RunKind.
_kind string `gae:"$kind,Run"`
_extra datastore.PropertyMap `gae:"-,extra"`
// ID is the RunID generated at triggering time.
//
// See doc for type `common.RunID` about the format.
ID common.RunID `gae:"$id"`
// CreationOperationID is a string used to de-dup Run creation attempts.
CreationOperationID string `gae:",noindex"`
// Mode dictates the behavior of this Run.
Mode Mode `gae:",noindex"`
// ModeDefinition is the definition for non-standard run mode.
//
// It is supplied in the project config. Note that ModeDefinition is fixed
// after the run creation and wouldn't be changed even if the mode definition
// has changed in the project config unless the mode name has changed. In that
// case, the run would be cancelled and a new run with new mode name would be
// created.
ModeDefinition *cfgpb.Mode
// Status describes the status of this Run.
Status Status
// EVersion is the entity version.
//
// It increments by one upon every successful modification.
EVersion int64 `gae:",noindex"`
// CreateTime is the timestamp when this Run was created.
//
// This is the timestamp of the last vote, on a Gerrit CL, that triggers this Run.
CreateTime time.Time `gae:",noindex"`
// StartTime is the timestamp when this Run was started.
StartTime time.Time `gae:",noindex"`
// UpdateTime is the timestamp when this entity was last updated.
UpdateTime time.Time `gae:",noindex"`
// EndTime is the timestamp when this Run has completed.
EndTime time.Time `gae:",noindex"`
// Owner is the identity of the owner of this Run.
//
// Currently, it is the same as owner of the CL. If `combine_cls` is
// enabled for the ConfigGroup used by this Run, the owner is the CL which
// has the latest triggering timestamp.
Owner identity.Identity `gae:",noindex"`
// CreatedBy is the identity that creates this Run.
//
// For dry run and full run, it is the identity of the user who *first* makes
// the Gerrit vote that triggers this run. If the Run contains multiple CLs
// (i.e. `combine_cls` is enabled), it is the identity of the user who
// triggers the CL which has the latest triggering timestamp. For new
// patchset run, it is the the identity of the user who uploads the new
// patchset.
CreatedBy identity.Identity `gae:",noindex"`
// BilledTo is the identity whose Run quota is consumed for the Run start.
BilledTo identity.Identity
// ConfigGroupID is ID of the ConfigGroup that is used by this Run.
//
// RunManager may update the ConfigGroup in the middle of the Run if it is
// notified that a new version of Config has been imported into CV.
ConfigGroupID prjcfg.ConfigGroupID `gae:",noindex"`
// CLs are IDs of all CLs involved in this Run.
//
// The index of Runs by CL is provided via RunCL's `IndexedID` field.
CLs common.CLIDs `gae:",noindex"`
// RootCL is the CL in `CLs` that triggers this Run in the combined mode.
//
// For example, for a stack of 2 CLs, if the top CL gains a vote that triggers
// a Dry Run for the stack, the top CL is the origin CL.
RootCL common.CLID `gae:",noindex"`
// Options are Run-specific additions on top of LUCI project config.
Options *Options
// CancellationReasons are the reasons for cancelling a Run.
//
// Typically, only one reason will be available. But it's possible that Run
// Manager received multiple cancellation requested at the approximately
// the same time.
//
// Reasons should not contain duplication and empty reason.
CancellationReasons []string `gae:",noindex"`
// OngoingLongOps tracks long operations currently happening.
OngoingLongOps *OngoingLongOps
// Submission is the state of Run Submission.
//
// If set, Submission is in progress or has completed.
Submission *Submission
// Tryjobs is the state of the Run tryjobs.
Tryjobs *Tryjobs
// LatestCLsRefresh is the latest time when Run Manager scheduled async
// refresh of CLs.
LatestCLsRefresh time.Time `gae:",noindex"`
// LatestTryjobsRefresh is the latest time when Run Manager scheduled async
// refresh of Tryjobs.
LatestTryjobsRefresh time.Time `gae:",noindex"`
// DepRuns is a slice of Runs that this Run depends on.
//
// If this is set and mode == FullRun, this run
// - will not start before all the deps start.
// - will be canceled if any of the deps is canceled or failed.
// - will be submitted only after all the deps are submitted.
DepRuns common.RunIDs
// QuotaExhaustionMsgLongOpRequested is set true when the current run
// already has a pending msg posted.
QuotaExhaustionMsgLongOpRequested bool `gae:",noindex"`
}
// RunCL is an immutable snapshot of a CL at the time of the Run start.
type RunCL struct {
_kind string `gae:"$kind,RunCL"`
ID common.CLID `gae:"$id"`
Run *datastore.Key `gae:"$parent"`
ExternalID changelist.ExternalID `gae:",noindex"`
Detail *changelist.Snapshot
Trigger *Trigger
// IndexedID is a copy of ID to get an index on just the CLID,
// as the primary automatic index is on (Run(parent), ID).
IndexedID common.CLID
// TODO(tandrii): add field of (ExternalID + "/" + patchset) to support for
// direct searches for gerrit/host/change/patchset.
}
// RunLog is an immutable entry for meaningful changes to a Run's state.
//
// RunLog entity is written
type RunLog struct {
_kind string `gae:"$kind,RunLog"`
// ID is the value Run.EVersion which was saved transactionally with the
// creation of the RunLog entity.
//
// Thus, ordering by ID (default Datastore ordering) will automatically
// provide semantically chronological order.
ID int64 `gae:"$id"`
Run *datastore.Key `gae:"$parent"`
// Entries record what happened to the Run.
//
// There is always at least one.
// Ordered from logically oldest to newest.
//
// Entries are stored as a protobuf in order to expose them using Admin API
// with ease.
// This is however opaque to Datastore, and so it has a downside of inability
// to index or filter by kinds of LogEntry within the Datastore itself.
// However, total number of LogEntries per Run should be <<1000 and the
// intended consumption are humans, therefore indexing isn't a deal breaker.
Entries *LogEntries
}