changelist

package
v0.0.0-...-df660c4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2024 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package changelist implements operations on a single CL.

It is agnostic about the kind of a CL even though currently all CLs represent Gerrit changes.

Index

Constants

View Source
const (
	// BatchUpdateCLTaskClass is the Task Class ID of the BatchUpdateCLTask,
	// which is enqueued only during a transaction.
	BatchUpdateCLTaskClass = "batch-update-cl"
	// UpdateCLTaskClass is the Task Class ID of the UpdateCLTask.
	UpdateCLTaskClass = "update-cl"
)
View Source
const BatchOnCLUpdatedTaskClass = "batch-notify-on-cl-updated"

BatchOnCLUpdatedTaskClass is the Task Class ID of the BatchOnCLUpdatedTask, which is enqueued during CL mutations.

Variables

View Source
var (
	DepKind_name = map[int32]string{
		0: "DEP_KIND_UNSPECIFIED",
		1: "HARD",
		2: "SOFT",
	}
	DepKind_value = map[string]int32{
		"DEP_KIND_UNSPECIFIED": 0,
		"HARD":                 1,
		"SOFT":                 2,
	}
)

Enum value maps for DepKind.

View Source
var (
	UpdateCLTask_Requester_name = map[int32]string{
		0:  "REQUESTER_CLASS_UNSPECIFIED",
		1:  "INCR_POLL_MATCHED",
		2:  "FULL_POLL_MATCHED",
		3:  "FULL_POLL_UNMATCHED",
		4:  "PUBSUB_POLL",
		5:  "CL_PURGER",
		12: "DEP_CL_TRIGGERER",
		6:  "RPC_ADMIN",
		7:  "RUN_POKE",
		8:  "RUN_REMOVAL",
		11: "RESET_CL_TRIGGER",
		10: "UPDATE_CONFIG",
	}
	UpdateCLTask_Requester_value = map[string]int32{
		"REQUESTER_CLASS_UNSPECIFIED": 0,
		"INCR_POLL_MATCHED":           1,
		"FULL_POLL_MATCHED":           2,
		"FULL_POLL_UNMATCHED":         3,
		"PUBSUB_POLL":                 4,
		"CL_PURGER":                   5,
		"DEP_CL_TRIGGERER":            12,
		"RPC_ADMIN":                   6,
		"RUN_POKE":                    7,
		"RUN_REMOVAL":                 8,
		"RESET_CL_TRIGGER":            11,
		"UPDATE_CONFIG":               10,
	}
)

Enum value maps for UpdateCLTask_Requester.

View Source
var ErrStopMutation = errors.New("stop CL mutation")

ErrStopMutation is a special error used by MutateCallback to signal that no mutation is necessary.

This is very useful because the datastore.RunInTransaction(ctx, f, ...) does retries by default which combined with submarine writes (transaction actually succeeded, but the client didn't get to know, e.g. due to network flake) means an idempotent MutateCallback can avoid noop updates yet still keep the code clean and readable. For example,

```

cl, err := mu.Update(ctx, project, clid, func (cl *changelist.CL) error {
  if cl.Snapshot == nil {
    return ErrStopMutation // noop
  }
  cl.Snapshot = nil
  return nil
})

if err != nil {
  return errors.Annotate(err, "failed to reset Snapshot").Err()
}

doSomething(ctx, cl) ```

View Source
var File_go_chromium_org_luci_cv_internal_changelist_storage_proto protoreflect.FileDescriptor
View Source
var File_go_chromium_org_luci_cv_internal_changelist_task_proto protoreflect.FileDescriptor

Functions

func Delete

func Delete(ctx context.Context, id common.CLID) error

Delete deletes CL and its CLMap entities transactionally.

Thus, deletion and insertion (part of ExternalID.getOrInsert) are atomic with respect to one another.

However, ExternalID.get and fast path of ExternalID.getOrInsert if called concurrently with Delete may return a temporary error, but on retry they would return ErrNoSuchEntity.

func JoinExternalURLs

func JoinExternalURLs(ids []ExternalID, sep string) string

JoinExternalURLs the URL of given ExternalIDs.

Panics if any of the ExternalIDs is invalid.

func LoadCLs

func LoadCLs(ctx context.Context, cls []*CL) error

LoadCLs loads given `CL` entities.

func Lookup

func Lookup(ctx context.Context, eids []ExternalID) ([]common.CLID, error)

Lookup loads CLID for each given ExternalID.

CLID is 0 if ExternalID is not yet known. Returns a single error (not MultiError) if there were multiple errors.

func QueryCLIDsUpdatedBefore

func QueryCLIDsUpdatedBefore(ctx context.Context, before time.Time) (common.CLIDs, error)

QueryCLIDsUpdatedBefore queries all CLIDs updated before the given timestamp.

This is mainly used for data retention purpose. Result CLIDs are sorted.

func RemoveUnusedGerritInfo

func RemoveUnusedGerritInfo(ci *gerritpb.ChangeInfo)

RemoveUnusedGerritInfo mutates given ChangeInfo to remove what CV definitely doesn't need to reduce bytes shuffled to/from Datastore.

Doesn't complain if anything is missing.

NOTE: keep this function actions in sync with storage.proto doc for Gerrit.info field.

func Sort

func Sort(cls []*CL)

Sort sorts slice of CLs by their CLID.

Types

type Access

type Access struct {
	ByProject map[string]*Access_Project `` // TODO(tandrii): per-project ApplicableConfig here.
	/* 176-byte string literal not displayed */
	// contains filtered or unexported fields
}

Access records which LUCI project can or can't see a CL.

If a LUCI project has Access, it means both:

(1) the project can read details of the CL (via Git/Gerrit ACLs);
(2) the project is the only LUCI project watching this CL in CV
    (via the CV config).
    Note: there can still be several applicable ConfigGroups of the same
    project (see ApplicableConfig).

In practice, .Access is set in 4 cases:

(a) `CQ-Depend: host:number` Gerrit CL footers allow users to specify

arbitrary dependencies, which typically happens due to typos,
but malicious actors can try to get CL details of restricted projects.
Either way, CV must not be a confused deputy here and must keep track
which project can see what.

(b) due to recent re-configuration of one or more LUCI projects, either

in CV config and/or in Gerrit ACLs, the previously watched & readable CL
becomes unwatched and/or unreadable.

(c) a previously existing CL was deleted (e.g. by its owner or Gerrit

administrators).

(d) eventual consistency of Gerrit masquerading as HTTP 404 on stale replica,

while quorum of replicas think CL actually exists and specific LUCI
project having access to it.

Unfortunately, (d) isn't easy to distinguish from (b) and (c), so CV resorts to tracking time since CL became invisible -- the longer, the more likely it is (b) or (c).

Furthermore, in case of (a), iff CV knows nothing about specific Gerrit CL identified as `CQ-Depend: host:change`, CV in general can't determine which LUCI project is allowed to watch this CL *before* fetching Gerrit project (repo) and target ref.

NOTE on CV as confused deputy.

CV works with multiple LUCI projects. As of this writing (June 2021), unfortunately, CV doesn't verify that Gerrit repos watched by a LUCI project are in fact owned by that LUCI project. Thus, nothing prevents one LUCI project from starting to watch repos de-facto owned by another LUCI project. This in turn brings 2 problems:

(1) Denial of service: unsolved.

Mitigation: CV will refuse to work with CLs which are watched by more
than 1 project. Since CV will communicate by posting message to affected
CL, this should be noticed and fixed quickly.

(2) Information leaks: solved.

Each LUCI project MUST use project-scoped service account (PSSA)
(migration is under way, see https://crbug.com/824492).
CV uses this account for all interaction with Gerrit on behalf a specific
LUCI project. Corresponding Gerrit repos:
  * SHOULD limit read access to its own PSSA + developers,
  * MUST limit Submit rights to its own PSSA and possibly developers.

For example,

  • `infra` project has all its Gerrit CLs public and doesn't care about information leaks. All other LUCI projects can read its CLs, as well as the whole Internet.
  • `infra-internal` project protects its Gerrit CLs, making them visible to `infra-internal-scoped@...` account only. When CV queries Gerrit on `infra-internal` behalf, CV uses `infra-internal-scoped` account and can fetch the data.
  • Suppose malicious actor compromised `infra` repo, and placed a new CV config there to start watching CLs of the `infra-internal` project as well as super/secret/repo, which wasn't watched by any CV before.
  • Unfortunately, CV can't currently object to the new config.
  • However, when querying Gerrit on `infra` behalf, CV uses `infra-scoped@...` account, which presumably won't be configured with read access to neither infra-internal nor super/secret/repo.
  • So, corresponding CLs will have .Access entry recording that `infra` has no access to them.
  • NOTE: CLs of infra-internal will also have .ApplicableConfig with two projects there, which will prevent normal operation of `infra-internal` CV but will not cause any leaks.

func (*Access) Descriptor deprecated

func (*Access) Descriptor() ([]byte, []int)

Deprecated: Use Access.ProtoReflect.Descriptor instead.

func (*Access) GetByProject

func (x *Access) GetByProject() map[string]*Access_Project

func (*Access) ProtoMessage

func (*Access) ProtoMessage()

func (*Access) ProtoReflect

func (x *Access) ProtoReflect() protoreflect.Message

func (*Access) Reset

func (x *Access) Reset()

func (*Access) String

func (x *Access) String() string

type AccessKind

type AccessKind int

AccessKind is the level of access a LUCI project has to a CL.

const (
	// AccessUnknown means a CL needs refreshing in the context of this project
	// in order to ascertain the AccessKind.
	AccessUnknown AccessKind = iota
	// AccessGranted means this LUCI project has exclusive access to the CL.
	//
	//  * this LUCI project is configured to watch this config,
	//    * and no other project is;
	//  * this LUCI project has access to the CL in code review (e.g., Gerrit);
	AccessGranted
	// AccessDeniedProbably means there is early evidence that LUCI project lacks
	// access to the project.
	//
	// This is a mitigation to Gerrit eventual consistency, which may result in
	// HTTP 404 returned for a CL that has just been created.
	AccessDeniedProbably
	// AccessDenied means the LUCI project has no access to this CL.
	//
	// Can be either due to project config not being the only watcher of the CL,
	// or due to the inability to fetch CL from code review (e.g. Gerrit).
	AccessDenied
)

type Access_Project

type Access_Project struct {

	// Deprecated. Use no_access_time instead.
	NoAccess bool `protobuf:"varint,1,opt,name=no_access,json=noAccess,proto3" json:"no_access,omitempty"`
	// The time when this was last re-confirmed.
	UpdateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
	// The time after which CV should consider lack of access stable.
	//
	// TODO(crbug/1216630): may be unset until backfil is done,
	// in which case use `no_access` field.
	NoAccessTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=no_access_time,json=noAccessTime,proto3" json:"no_access_time,omitempty"`
	// contains filtered or unexported fields
}

func (*Access_Project) Descriptor deprecated

func (*Access_Project) Descriptor() ([]byte, []int)

Deprecated: Use Access_Project.ProtoReflect.Descriptor instead.

func (*Access_Project) GetNoAccess

func (x *Access_Project) GetNoAccess() bool

func (*Access_Project) GetNoAccessTime

func (x *Access_Project) GetNoAccessTime() *timestamppb.Timestamp

func (*Access_Project) GetUpdateTime

func (x *Access_Project) GetUpdateTime() *timestamppb.Timestamp

func (*Access_Project) ProtoMessage

func (*Access_Project) ProtoMessage()

func (*Access_Project) ProtoReflect

func (x *Access_Project) ProtoReflect() protoreflect.Message

func (*Access_Project) Reset

func (x *Access_Project) Reset()

func (*Access_Project) String

func (x *Access_Project) String() string

type ApplicableConfig

type ApplicableConfig struct {
	Projects []*ApplicableConfig_Project `protobuf:"bytes,2,rep,name=projects,proto3" json:"projects,omitempty"`
	// contains filtered or unexported fields
}

ApplicableConfig keeps track of configs applicable to a CL.

This is computed based on known set of LUCI project configs, versions of which are updated by CV independently, so the ApplicableConfig are also eventually consistent.

Normally, there is 1 applicable configs = exactly 1 project with 1 config group. If CL is no longer watched by CV, there will be 0 applicable configs.

Sometimes, there can be 2+ applicable configs. This happens if either:

  • eventual consistency: responsibility for CL is moved from one LUCI project to another. Three is no way to make this atomically, so CL may temporarily end up with 0 or 2 projects watching it, before settling on just 1.
  • misconfiguration: two projects or 2 different ConfigGroups within the same project watch the same CL.

In either case, CV refuses to guess and will abstain from processing such CLs, but storing the list is very useful for CV debugging and potentially for better diagnostic messages to CV users and LUCI project owners.

func (*ApplicableConfig) Descriptor deprecated

func (*ApplicableConfig) Descriptor() ([]byte, []int)

Deprecated: Use ApplicableConfig.ProtoReflect.Descriptor instead.

func (*ApplicableConfig) GetProjects

func (x *ApplicableConfig) GetProjects() []*ApplicableConfig_Project

func (*ApplicableConfig) HasOnlyProject

func (a *ApplicableConfig) HasOnlyProject(luciProject string) bool

HasOnlyProject returns true iff ApplicableConfig contains only the given project, regardless of the number of applicable config groups it may contain.

func (*ApplicableConfig) HasProject

func (a *ApplicableConfig) HasProject(luciProject string) bool

HasProject returns true whether ApplicableConfig contains the given project, possibly among other projects.

func (*ApplicableConfig) ProtoMessage

func (*ApplicableConfig) ProtoMessage()

func (*ApplicableConfig) ProtoReflect

func (x *ApplicableConfig) ProtoReflect() protoreflect.Message

func (*ApplicableConfig) Reset

func (x *ApplicableConfig) Reset()

func (*ApplicableConfig) SemanticallyEqual

func (a *ApplicableConfig) SemanticallyEqual(b *ApplicableConfig) bool

SemanticallyEqual checks if ApplicableConfig configs are the same.

func (*ApplicableConfig) String

func (x *ApplicableConfig) String() string

type ApplicableConfig_Project

type ApplicableConfig_Project struct {
	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	// ID of the specific ConfigGroup. See cv/internal/config.ConfigGroupID.
	//
	// The referenced version may no longer be available to datastore,
	// commonly happening if CL wasn't active for a long time.
	ConfigGroupIds []string `protobuf:"bytes,2,rep,name=config_group_ids,json=configGroupIds,proto3" json:"config_group_ids,omitempty"`
	// contains filtered or unexported fields
}

func (*ApplicableConfig_Project) Descriptor deprecated

func (*ApplicableConfig_Project) Descriptor() ([]byte, []int)

Deprecated: Use ApplicableConfig_Project.ProtoReflect.Descriptor instead.

func (*ApplicableConfig_Project) GetConfigGroupIds

func (x *ApplicableConfig_Project) GetConfigGroupIds() []string

func (*ApplicableConfig_Project) GetName

func (x *ApplicableConfig_Project) GetName() string

func (*ApplicableConfig_Project) ProtoMessage

func (*ApplicableConfig_Project) ProtoMessage()

func (*ApplicableConfig_Project) ProtoReflect

func (x *ApplicableConfig_Project) ProtoReflect() protoreflect.Message

func (*ApplicableConfig_Project) Reset

func (x *ApplicableConfig_Project) Reset()

func (*ApplicableConfig_Project) String

func (x *ApplicableConfig_Project) String() string

type BatchOnCLUpdatedTask

type BatchOnCLUpdatedTask struct {
	Projects map[string]*CLUpdatedEvents `` /* 157-byte string literal not displayed */
	Runs     map[string]*CLUpdatedEvents `` /* 149-byte string literal not displayed */
	// contains filtered or unexported fields
}

BatchOnCLUpdatedTask notifies many Projects and Runs about updated CLs.

Queue: "notify-on-cl-updated".

func (*BatchOnCLUpdatedTask) Descriptor deprecated

func (*BatchOnCLUpdatedTask) Descriptor() ([]byte, []int)

Deprecated: Use BatchOnCLUpdatedTask.ProtoReflect.Descriptor instead.

func (*BatchOnCLUpdatedTask) GetProjects

func (x *BatchOnCLUpdatedTask) GetProjects() map[string]*CLUpdatedEvents

func (*BatchOnCLUpdatedTask) GetRuns

func (x *BatchOnCLUpdatedTask) GetRuns() map[string]*CLUpdatedEvents

func (*BatchOnCLUpdatedTask) ProtoMessage

func (*BatchOnCLUpdatedTask) ProtoMessage()

func (*BatchOnCLUpdatedTask) ProtoReflect

func (x *BatchOnCLUpdatedTask) ProtoReflect() protoreflect.Message

func (*BatchOnCLUpdatedTask) Reset

func (x *BatchOnCLUpdatedTask) Reset()

func (*BatchOnCLUpdatedTask) String

func (x *BatchOnCLUpdatedTask) String() string

type BatchUpdateCLTask

type BatchUpdateCLTask struct {
	Tasks []*UpdateCLTask `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"`
	// contains filtered or unexported fields
}

BatchUpdateCLTask is for updating many CLs.

When executed, it just enqueues its tasks as individual UpdateCLTask TQ for independent execution.

Queue: "update-cl".

func (*BatchUpdateCLTask) Descriptor deprecated

func (*BatchUpdateCLTask) Descriptor() ([]byte, []int)

Deprecated: Use BatchUpdateCLTask.ProtoReflect.Descriptor instead.

func (*BatchUpdateCLTask) GetTasks

func (x *BatchUpdateCLTask) GetTasks() []*UpdateCLTask

func (*BatchUpdateCLTask) ProtoMessage

func (*BatchUpdateCLTask) ProtoMessage()

func (*BatchUpdateCLTask) ProtoReflect

func (x *BatchUpdateCLTask) ProtoReflect() protoreflect.Message

func (*BatchUpdateCLTask) Reset

func (x *BatchUpdateCLTask) Reset()

func (*BatchUpdateCLTask) String

func (x *BatchUpdateCLTask) String() string

type CL

type CL struct {

	// ID is auto-generated by Datastore.
	ID common.CLID `gae:"$id"` // int64
	// ExternalID must not be modified once entity is created.
	ExternalID ExternalID `gae:",noindex"` // string. Indexed in CLMap entities.

	// EVersion is entity version. Every update should increment it by 1.
	// See Update() function.
	EVersion int64 `gae:",noindex"`

	// UpdateTime is exact time of when this entity was last updated.
	//
	// It's not indexed to avoid hot areas in the index.
	UpdateTime time.Time `gae:",noindex"`

	// RetentionKey is for data retention purpose.
	//
	// It is indexed and tries to avoid hot areas in the index. The format is
	// `{shard_key}/{unix_time_of_UpdateTime}`. Shard key is the last 2
	// digit of CLID with left padded zero. Unix timestamp is a 10 digit integer
	// with left padded zero if necessary.
	RetentionKey string

	// Snapshot is the latest known state of a CL. It may be and often is
	// behind the source of truth, which is the code review site (e.g. Gerrit).
	Snapshot *Snapshot

	// ApplicableConfig keeps track of configs applicable to the CL.
	//
	// TODO(tandrii): merge into .Access.
	ApplicableConfig *ApplicableConfig

	// Access records per-LUCI project visibility of a CL.
	//
	// See description in protobuf type with the same name.
	//
	// TODO(tandrii): rename GAE field to `Access`.
	Access *Access `gae:"DependentMeta"`

	// IncompleteRuns tracks not yet finalized Runs working on this CL. Sorted.
	//
	// It's updated transactionally with the Run being modified.
	IncompleteRuns common.RunIDs `gae:",noindex"`

	// TriggerNewPatchsetRunAfterPS indicates the patchset number after which
	// new patchset runs can be triggered.
	//
	// E.g. if this value is set to 2, do not trigger new patchset runs for
	// patchsets 1 or 2. Presumably this is because those runs were already
	// completed/failed/otherwise purged.
	//
	// This is needed because unlike label votes which CV can remove, triggering
	// new patchset upload runs relies on the presence of the patchset in the CL
	// snapshot, which cannot be removed.
	TriggerNewPatchsetRunAfterPS int32 `gae:",noindex"`
	// contains filtered or unexported fields
}

CL is a CL entity in Datastore.

func LoadCLsByIDs

func LoadCLsByIDs(ctx context.Context, clids common.CLIDs) ([]*CL, error)

LoadCLsByIDs loads `CL` entities of the provided list of clids.

func LoadCLsMap

func LoadCLsMap(ctx context.Context, m map[common.CLID]*CL) ([]*CL, error)

LoadCLsMap loads `CL` entities which are values in the provided map.

Updates `CL` entities *in place*, but also returns them as a slice.

func (*CL) AccessKind

func (cl *CL) AccessKind(ctx context.Context, luciProject string) AccessKind

AccessKind returns AccessKind of a CL.

func (*CL) AccessKindFromCodeReviewSite

func (cl *CL) AccessKindFromCodeReviewSite(ctx context.Context, luciProject string) AccessKind

AccessKind returns AccessKind of a CL from code review site.

func (*CL) AccessKindWithReason

func (cl *CL) AccessKindWithReason(ctx context.Context, luciProject string) (AccessKind, string)

AccessKindWithReason returns AccessKind of a CL and a reason for it.

func (*CL) IsWatchedByThisAndOtherProjects

func (cl *CL) IsWatchedByThisAndOtherProjects(thisProject string) (*ApplicableConfig_Project, bool)

IsWatchedByThisAndOtherProjects checks if CL is watched by several projects, one of which is given. If so, returns the config applicable to the given project and true. Else, returns nil, false.

func (*CL) ToUpdatedEvent

func (cl *CL) ToUpdatedEvent() *CLUpdatedEvent

ToUpdatedEvent returns CLUpdatedEvent corresponding to the current CL version.

func (*CL) URL

func (cl *CL) URL() (string, error)

URL returns URL of the CL.

func (*CL) UpdateRetentionKey

func (cl *CL) UpdateRetentionKey()

UpdateRetentionKey updates the RetentionKey of the CL.

Panics if the CL.ID and/or CL.UpdateTime is absent.

type CLError

type CLError struct {

	// Next tag is 12.
	//
	// Types that are assignable to Kind:
	//
	//	*CLError_OwnerLacksEmail
	//	*CLError_WatchedByManyConfigGroups_
	//	*CLError_WatchedByManyProjects_
	//	*CLError_InvalidDeps_
	//	*CLError_UnsupportedMode
	//	*CLError_SelfCqDepend
	//	*CLError_CorruptGerritMetadata
	//	*CLError_ReusedTrigger_
	//	*CLError_CommitBlocked
	//	*CLError_TriggerDeps_
	//	*CLError_DepRunFailed
	Kind isCLError_Kind `protobuf_oneof:"kind"`
	// contains filtered or unexported fields
}

CLError encapsulates all kinds of CL errors, which ultimately result in purging of the CL while communicating the reason to the relevant users.

The primary goal of the CLError is to transport via CV guts sufficient information to generate a clear user-friendly error message.

func (*CLError) Descriptor deprecated

func (*CLError) Descriptor() ([]byte, []int)

Deprecated: Use CLError.ProtoReflect.Descriptor instead.

func (*CLError) GetCommitBlocked

func (x *CLError) GetCommitBlocked() bool

func (*CLError) GetCorruptGerritMetadata

func (x *CLError) GetCorruptGerritMetadata() string

func (*CLError) GetDepRunFailed

func (x *CLError) GetDepRunFailed() int64

func (*CLError) GetInvalidDeps

func (x *CLError) GetInvalidDeps() *CLError_InvalidDeps

func (*CLError) GetKind

func (m *CLError) GetKind() isCLError_Kind

func (*CLError) GetOwnerLacksEmail

func (x *CLError) GetOwnerLacksEmail() bool

func (*CLError) GetReusedTrigger

func (x *CLError) GetReusedTrigger() *CLError_ReusedTrigger

func (*CLError) GetSelfCqDepend

func (x *CLError) GetSelfCqDepend() bool

func (*CLError) GetTriggerDeps

func (x *CLError) GetTriggerDeps() *CLError_TriggerDeps

func (*CLError) GetUnsupportedMode

func (x *CLError) GetUnsupportedMode() string

func (*CLError) GetWatchedByManyConfigGroups

func (x *CLError) GetWatchedByManyConfigGroups() *CLError_WatchedByManyConfigGroups

func (*CLError) GetWatchedByManyProjects

func (x *CLError) GetWatchedByManyProjects() *CLError_WatchedByManyProjects

func (*CLError) ProtoMessage

func (*CLError) ProtoMessage()

func (*CLError) ProtoReflect

func (x *CLError) ProtoReflect() protoreflect.Message

func (*CLError) Reset

func (x *CLError) Reset()

func (*CLError) String

func (x *CLError) String() string

type CLError_CommitBlocked

type CLError_CommitBlocked struct {
	// Set to true when the footer "Commit: false" is present.
	CommitBlocked bool `protobuf:"varint,9,opt,name=commit_blocked,json=commitBlocked,proto3,oneof"`
}

type CLError_CorruptGerritMetadata

type CLError_CorruptGerritMetadata struct {
	CorruptGerritMetadata string `protobuf:"bytes,6,opt,name=corrupt_gerrit_metadata,json=corruptGerritMetadata,proto3,oneof"`
}

type CLError_DepRunFailed

type CLError_DepRunFailed struct {
	// Set with the CL ID of a failed Run, if the purge-requested-CL depends on
	// the CL.
	DepRunFailed int64 `protobuf:"varint,11,opt,name=dep_run_failed,json=depRunFailed,proto3,oneof"`
}

type CLError_InvalidDeps

type CLError_InvalidDeps struct {

	// Deps not watched by the same LUCI project as the dependent.
	Unwatched []*Dep `protobuf:"bytes,1,rep,name=unwatched,proto3" json:"unwatched,omitempty"`
	// Deps watched by the same LUCI project but different config group.
	WrongConfigGroup []*Dep `protobuf:"bytes,2,rep,name=wrong_config_group,json=wrongConfigGroup,proto3" json:"wrong_config_group,omitempty"`
	// Not yet submitted deps of a full run in non-combinable mode.
	SingleFullDeps []*Dep `protobuf:"bytes,3,rep,name=single_full_deps,json=singleFullDeps,proto3" json:"single_full_deps,omitempty"`
	// Not yet CQ-ed deps of a Run in combinable mode.
	CombinableUntriggered []*Dep `protobuf:"bytes,4,rep,name=combinable_untriggered,json=combinableUntriggered,proto3" json:"combinable_untriggered,omitempty"`
	// CQ-ed deps of a different mode.
	CombinableMismatchedMode []*Dep `` /* 135-byte string literal not displayed */
	// There are more non-submitted deps than is supported by CV.
	TooMany *CLError_InvalidDeps_TooMany `protobuf:"bytes,6,opt,name=too_many,json=tooMany,proto3" json:"too_many,omitempty"`
	// contains filtered or unexported fields
}

func (*CLError_InvalidDeps) Descriptor deprecated

func (*CLError_InvalidDeps) Descriptor() ([]byte, []int)

Deprecated: Use CLError_InvalidDeps.ProtoReflect.Descriptor instead.

func (*CLError_InvalidDeps) GetCombinableMismatchedMode

func (x *CLError_InvalidDeps) GetCombinableMismatchedMode() []*Dep

func (*CLError_InvalidDeps) GetCombinableUntriggered

func (x *CLError_InvalidDeps) GetCombinableUntriggered() []*Dep

func (*CLError_InvalidDeps) GetSingleFullDeps

func (x *CLError_InvalidDeps) GetSingleFullDeps() []*Dep

func (*CLError_InvalidDeps) GetTooMany

func (*CLError_InvalidDeps) GetUnwatched

func (x *CLError_InvalidDeps) GetUnwatched() []*Dep

func (*CLError_InvalidDeps) GetWrongConfigGroup

func (x *CLError_InvalidDeps) GetWrongConfigGroup() []*Dep

func (*CLError_InvalidDeps) ProtoMessage

func (*CLError_InvalidDeps) ProtoMessage()

func (*CLError_InvalidDeps) ProtoReflect

func (x *CLError_InvalidDeps) ProtoReflect() protoreflect.Message

func (*CLError_InvalidDeps) Reset

func (x *CLError_InvalidDeps) Reset()

func (*CLError_InvalidDeps) String

func (x *CLError_InvalidDeps) String() string

type CLError_InvalidDeps_

type CLError_InvalidDeps_ struct {
	InvalidDeps *CLError_InvalidDeps `protobuf:"bytes,3,opt,name=invalid_deps,json=invalidDeps,proto3,oneof"`
}

type CLError_InvalidDeps_TooMany

type CLError_InvalidDeps_TooMany struct {
	Actual     int32 `protobuf:"varint,1,opt,name=actual,proto3" json:"actual,omitempty"`
	MaxAllowed int32 `protobuf:"varint,2,opt,name=max_allowed,json=maxAllowed,proto3" json:"max_allowed,omitempty"`
	// contains filtered or unexported fields
}

func (*CLError_InvalidDeps_TooMany) Descriptor deprecated

func (*CLError_InvalidDeps_TooMany) Descriptor() ([]byte, []int)

Deprecated: Use CLError_InvalidDeps_TooMany.ProtoReflect.Descriptor instead.

func (*CLError_InvalidDeps_TooMany) GetActual

func (x *CLError_InvalidDeps_TooMany) GetActual() int32

func (*CLError_InvalidDeps_TooMany) GetMaxAllowed

func (x *CLError_InvalidDeps_TooMany) GetMaxAllowed() int32

func (*CLError_InvalidDeps_TooMany) ProtoMessage

func (*CLError_InvalidDeps_TooMany) ProtoMessage()

func (*CLError_InvalidDeps_TooMany) ProtoReflect

func (*CLError_InvalidDeps_TooMany) Reset

func (x *CLError_InvalidDeps_TooMany) Reset()

func (*CLError_InvalidDeps_TooMany) String

func (x *CLError_InvalidDeps_TooMany) String() string

type CLError_OwnerLacksEmail

type CLError_OwnerLacksEmail struct {
	OwnerLacksEmail bool `protobuf:"varint,1,opt,name=owner_lacks_email,json=ownerLacksEmail,proto3,oneof"`
}

type CLError_ReusedTrigger

type CLError_ReusedTrigger struct {

	// ID of the finalized Run.
	Run string `protobuf:"bytes,1,opt,name=run,proto3" json:"run,omitempty"`
	// contains filtered or unexported fields
}

ReusedTrigger means a CL trigger (e.g. CQ+1 vote) has already resulted in a CQ Run which was finalized.

Two known cases when this happens with a Gerrit CL:

  1. A user uploads a CL on ref A, then votes CQ+1. Before the Dry Run completes, the CL is moved to ref B, while preserving the CQ+1 vote. The old Run is finalized, but the new Run has the exact same trigger, which in CQDaemon-compatible mode means the new Run's ID is exactly the same as the old one, so CV can't create a new Run. TODO(crbug/1223349): after CQDaemon is deleted, the Run ID generation scheme can take into account the ref of a CL, and this use case can be allowed.
  2. The same as above but instead of moving CL between refs, abandon and restore the CL.

func (*CLError_ReusedTrigger) Descriptor deprecated

func (*CLError_ReusedTrigger) Descriptor() ([]byte, []int)

Deprecated: Use CLError_ReusedTrigger.ProtoReflect.Descriptor instead.

func (*CLError_ReusedTrigger) GetRun

func (x *CLError_ReusedTrigger) GetRun() string

func (*CLError_ReusedTrigger) ProtoMessage

func (*CLError_ReusedTrigger) ProtoMessage()

func (*CLError_ReusedTrigger) ProtoReflect

func (x *CLError_ReusedTrigger) ProtoReflect() protoreflect.Message

func (*CLError_ReusedTrigger) Reset

func (x *CLError_ReusedTrigger) Reset()

func (*CLError_ReusedTrigger) String

func (x *CLError_ReusedTrigger) String() string

type CLError_ReusedTrigger_

type CLError_ReusedTrigger_ struct {
	ReusedTrigger *CLError_ReusedTrigger `protobuf:"bytes,7,opt,name=reused_trigger,json=reusedTrigger,proto3,oneof"`
}

type CLError_SelfCqDepend

type CLError_SelfCqDepend struct {
	SelfCqDepend bool `protobuf:"varint,5,opt,name=self_cq_depend,json=selfCqDepend,proto3,oneof"`
}

type CLError_TriggerDeps

type CLError_TriggerDeps struct {

	// IDs of CLs for which trigger attempts failed due to permission denied.
	PermissionDenied []*CLError_TriggerDeps_PermissionDenied `protobuf:"bytes,1,rep,name=permission_denied,json=permissionDenied,proto3" json:"permission_denied,omitempty"`
	// IDs of CLs for which trigger attempts failed because they were not found
	// in Gerrit.
	NotFound []int64 `protobuf:"varint,2,rep,packed,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
	// IDs of CLs for which trigger attempts failed due to internal Gerrit
	// error.
	InternalGerritError []int64 `` /* 128-byte string literal not displayed */
	// contains filtered or unexported fields
}

TriggerDeps indicates failures for triggering deps.

func (*CLError_TriggerDeps) Descriptor deprecated

func (*CLError_TriggerDeps) Descriptor() ([]byte, []int)

Deprecated: Use CLError_TriggerDeps.ProtoReflect.Descriptor instead.

func (*CLError_TriggerDeps) GetInternalGerritError

func (x *CLError_TriggerDeps) GetInternalGerritError() []int64

func (*CLError_TriggerDeps) GetNotFound

func (x *CLError_TriggerDeps) GetNotFound() []int64

func (*CLError_TriggerDeps) GetPermissionDenied

func (x *CLError_TriggerDeps) GetPermissionDenied() []*CLError_TriggerDeps_PermissionDenied

func (*CLError_TriggerDeps) ProtoMessage

func (*CLError_TriggerDeps) ProtoMessage()

func (*CLError_TriggerDeps) ProtoReflect

func (x *CLError_TriggerDeps) ProtoReflect() protoreflect.Message

func (*CLError_TriggerDeps) Reset

func (x *CLError_TriggerDeps) Reset()

func (*CLError_TriggerDeps) String

func (x *CLError_TriggerDeps) String() string

type CLError_TriggerDeps_

type CLError_TriggerDeps_ struct {
	TriggerDeps *CLError_TriggerDeps `protobuf:"bytes,10,opt,name=trigger_deps,json=triggerDeps,proto3,oneof"`
}

type CLError_TriggerDeps_PermissionDenied

type CLError_TriggerDeps_PermissionDenied struct {
	Clid int64 `protobuf:"varint,1,opt,name=clid,proto3" json:"clid,omitempty"`
	// If set, the vote was attempted on behalf of the Gerrit user.
	Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
	// contains filtered or unexported fields
}

func (*CLError_TriggerDeps_PermissionDenied) Descriptor deprecated

func (*CLError_TriggerDeps_PermissionDenied) Descriptor() ([]byte, []int)

Deprecated: Use CLError_TriggerDeps_PermissionDenied.ProtoReflect.Descriptor instead.

func (*CLError_TriggerDeps_PermissionDenied) GetClid

func (*CLError_TriggerDeps_PermissionDenied) GetEmail

func (*CLError_TriggerDeps_PermissionDenied) ProtoMessage

func (*CLError_TriggerDeps_PermissionDenied) ProtoMessage()

func (*CLError_TriggerDeps_PermissionDenied) ProtoReflect

func (*CLError_TriggerDeps_PermissionDenied) Reset

func (*CLError_TriggerDeps_PermissionDenied) String

type CLError_UnsupportedMode

type CLError_UnsupportedMode struct {
	UnsupportedMode string `protobuf:"bytes,4,opt,name=unsupported_mode,json=unsupportedMode,proto3,oneof"`
}

type CLError_WatchedByManyConfigGroups

type CLError_WatchedByManyConfigGroups struct {

	// Config group names without LUCI project prefix.
	ConfigGroups []string `protobuf:"bytes,1,rep,name=config_groups,json=configGroups,proto3" json:"config_groups,omitempty"`
	// contains filtered or unexported fields
}

func (*CLError_WatchedByManyConfigGroups) Descriptor deprecated

func (*CLError_WatchedByManyConfigGroups) Descriptor() ([]byte, []int)

Deprecated: Use CLError_WatchedByManyConfigGroups.ProtoReflect.Descriptor instead.

func (*CLError_WatchedByManyConfigGroups) GetConfigGroups

func (x *CLError_WatchedByManyConfigGroups) GetConfigGroups() []string

func (*CLError_WatchedByManyConfigGroups) ProtoMessage

func (*CLError_WatchedByManyConfigGroups) ProtoMessage()

func (*CLError_WatchedByManyConfigGroups) ProtoReflect

func (*CLError_WatchedByManyConfigGroups) Reset

func (*CLError_WatchedByManyConfigGroups) String

type CLError_WatchedByManyConfigGroups_

type CLError_WatchedByManyConfigGroups_ struct {
	WatchedByManyConfigGroups *CLError_WatchedByManyConfigGroups `protobuf:"bytes,2,opt,name=watched_by_many_config_groups,json=watchedByManyConfigGroups,proto3,oneof"`
}

type CLError_WatchedByManyProjects

type CLError_WatchedByManyProjects struct {
	Projects []string `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"`
	// contains filtered or unexported fields
}

func (*CLError_WatchedByManyProjects) Descriptor deprecated

func (*CLError_WatchedByManyProjects) Descriptor() ([]byte, []int)

Deprecated: Use CLError_WatchedByManyProjects.ProtoReflect.Descriptor instead.

func (*CLError_WatchedByManyProjects) GetProjects

func (x *CLError_WatchedByManyProjects) GetProjects() []string

func (*CLError_WatchedByManyProjects) ProtoMessage

func (*CLError_WatchedByManyProjects) ProtoMessage()

func (*CLError_WatchedByManyProjects) ProtoReflect

func (*CLError_WatchedByManyProjects) Reset

func (x *CLError_WatchedByManyProjects) Reset()

func (*CLError_WatchedByManyProjects) String

type CLError_WatchedByManyProjects_

type CLError_WatchedByManyProjects_ struct {
	WatchedByManyProjects *CLError_WatchedByManyProjects `protobuf:"bytes,8,opt,name=watched_by_many_projects,json=watchedByManyProjects,proto3,oneof"`
}

type CLMutation

type CLMutation struct {
	// CL can be modified except the following fields:
	//  * ID
	//  * ExternalID
	//  * EVersion
	//  * UpdateTime
	CL *CL
	// contains filtered or unexported fields
}

CLMutation encapsulates one CL mutation.

func (*CLMutation) Finalize

func (clm *CLMutation) Finalize(ctx context.Context) (*CL, error)

Finalize finalizes CL mutation.

Must be called at most once. Must be called in the same Datastore transaction as Begin() which began the CL mutation.

type CLUpdatedEvent

type CLUpdatedEvent struct {
	Clid     int64 `protobuf:"varint,1,opt,name=clid,proto3" json:"clid,omitempty"`
	Eversion int64 `protobuf:"varint,2,opt,name=eversion,proto3" json:"eversion,omitempty"`
	// contains filtered or unexported fields
}

CLUpdatedEvent is just a CL ID pinned to its latest known EVersion.

func (*CLUpdatedEvent) Descriptor deprecated

func (*CLUpdatedEvent) Descriptor() ([]byte, []int)

Deprecated: Use CLUpdatedEvent.ProtoReflect.Descriptor instead.

func (*CLUpdatedEvent) GetClid

func (x *CLUpdatedEvent) GetClid() int64

func (*CLUpdatedEvent) GetEversion

func (x *CLUpdatedEvent) GetEversion() int64

func (*CLUpdatedEvent) ProtoMessage

func (*CLUpdatedEvent) ProtoMessage()

func (*CLUpdatedEvent) ProtoReflect

func (x *CLUpdatedEvent) ProtoReflect() protoreflect.Message

func (*CLUpdatedEvent) Reset

func (x *CLUpdatedEvent) Reset()

func (*CLUpdatedEvent) String

func (x *CLUpdatedEvent) String() string

type CLUpdatedEvents

type CLUpdatedEvents struct {
	Events []*CLUpdatedEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
	// contains filtered or unexported fields
}

CLUpdatedEvents is a batch of CLUpdatedEvents.

func ToUpdatedEvents

func ToUpdatedEvents(cls ...*CL) *CLUpdatedEvents

ToUpdatedEvents returns CLUpdatedEvents from a slice of CLs.

func (*CLUpdatedEvents) Descriptor deprecated

func (*CLUpdatedEvents) Descriptor() ([]byte, []int)

Deprecated: Use CLUpdatedEvents.ProtoReflect.Descriptor instead.

func (*CLUpdatedEvents) GetEvents

func (x *CLUpdatedEvents) GetEvents() []*CLUpdatedEvent

func (*CLUpdatedEvents) ProtoMessage

func (*CLUpdatedEvents) ProtoMessage()

func (*CLUpdatedEvents) ProtoReflect

func (x *CLUpdatedEvents) ProtoReflect() protoreflect.Message

func (*CLUpdatedEvents) Reset

func (x *CLUpdatedEvents) Reset()

func (*CLUpdatedEvents) String

func (x *CLUpdatedEvents) String() string

type Dep

type Dep struct {

	// CLID is internal CV ID of a CL which is the dependency.
	Clid int64   `protobuf:"varint,1,opt,name=clid,proto3" json:"clid,omitempty"`
	Kind DepKind `protobuf:"varint,2,opt,name=kind,proto3,enum=cv.internal.changelist.DepKind" json:"kind,omitempty"`
	// contains filtered or unexported fields
}

func (*Dep) Descriptor deprecated

func (*Dep) Descriptor() ([]byte, []int)

Deprecated: Use Dep.ProtoReflect.Descriptor instead.

func (*Dep) GetClid

func (x *Dep) GetClid() int64

func (*Dep) GetKind

func (x *Dep) GetKind() DepKind

func (*Dep) ProtoMessage

func (*Dep) ProtoMessage()

func (*Dep) ProtoReflect

func (x *Dep) ProtoReflect() protoreflect.Message

func (*Dep) Reset

func (x *Dep) Reset()

func (*Dep) String

func (x *Dep) String() string

type DepKind

type DepKind int32
const (
	DepKind_DEP_KIND_UNSPECIFIED DepKind = 0
	// Dep MUST be patched in / submitted before the dependent CL.
	DepKind_HARD DepKind = 1
	// Dep SHOULD be patched in / submitted before the dependent CL,
	// but doesn't have to be.
	DepKind_SOFT DepKind = 2
)

func (DepKind) Descriptor

func (DepKind) Descriptor() protoreflect.EnumDescriptor

func (DepKind) Enum

func (x DepKind) Enum() *DepKind

func (DepKind) EnumDescriptor deprecated

func (DepKind) EnumDescriptor() ([]byte, []int)

Deprecated: Use DepKind.Descriptor instead.

func (DepKind) Number

func (x DepKind) Number() protoreflect.EnumNumber

func (DepKind) String

func (x DepKind) String() string

func (DepKind) Type

func (DepKind) Type() protoreflect.EnumType

type ExternalID

type ExternalID string

ExternalID is a unique CL ID deterministically constructed based on CL data.

Currently, only Gerrit is supported.

func GobID

func GobID(host string, change int64) (ExternalID, error)

GobID makes an ExternalID for a Gerrit CL.

Host is typically "something-review.googlesource.com". Change is a number, e.g. 2515619 for https://chromium-review.googlesource.com/c/infra/luci/luci-go/+/2515619

func MustGobID

func MustGobID(host string, change int64) ExternalID

MustGobID is like GobID but panics on error.

func (ExternalID) Load

func (eid ExternalID) Load(ctx context.Context) (*CL, error)

Load reads a CL from Datastore.

Returns nil, nil if it doesn't exist.

func (ExternalID) MustCreateIfNotExists

func (eid ExternalID) MustCreateIfNotExists(ctx context.Context) *CL

MustCreateIfNotExists is for use in tests to ensure CL exists.

Panicks on errors.

func (ExternalID) MustURL

func (eid ExternalID) MustURL() string

MustURL is like `URL()` but panic on err.

func (ExternalID) ParseGobID

func (eid ExternalID) ParseGobID() (host string, change int64, err error)

ParseGobID returns Gerrit host and change if this is a GobID.

func (ExternalID) URL

func (eid ExternalID) URL() (string, error)

URL returns URL of the CL.

type FetchInput

type FetchInput struct {
	// CL of the ChangeList to fetch a snapshot of.
	//
	// If CL.ID in the input is 0, it means the CL entity doesn't exist in
	// Datastore. The cl.ExternalID is always set.
	CL *CL
	// Project is the LUCI project to use the scoped account of for the fetch
	// operation to be performed.
	Project string
	// UpdatedHint, if not zero time, is the backend-originating timestamp of
	// the most recent CL update time. It's sourced by CV by e.g. polling or
	// PubSub subscription. It is useful to detect and work around backend's
	// eventual consistency.
	UpdatedHint time.Time
	// Requester identifies various scenarios that issued the Fetch invocation.
	Requester UpdateCLTask_Requester
	Hint      *UpdateCLTask_Hint
}

FetchInput an input for UpdaterBackend.Fetch.

It contains fields for what to fetch with meta information.

func NewFetchInput

func NewFetchInput(cl *CL, task *UpdateCLTask) *FetchInput

NewFetchInput returns FetchInput for a given CL and UpdateCLTask.

type Gerrit

type Gerrit struct {

	// Gerrit host.
	Host string `protobuf:"bytes,5,opt,name=host,proto3" json:"host,omitempty"`
	// Info contains subset of ChangeInfo listed below.
	//
	// NOTE: keep this list in sync with RemoveUnusedGerritInfo() function.
	//   - number
	//   - owner
	//   - id
	//   - email (may be not set)
	//   - project
	//   - ref
	//   - status
	//   - current_revision
	//   - revisions
	//   - kind
	//   - number
	//   - ref
	//   - created
	//   - commit
	//   - id
	//   - parents
	//   - labels
	//   - optional
	//   - all (only if vote != 0)
	//   - user
	//   - id
	//   - email (may be not set)
	//   - value
	//   - messages
	//   - id
	//   - date
	//   - message
	//   - author
	//   - id
	//   - realauthor
	//   - id
	//   - updated
	//   - created
	Info *gerrit.ChangeInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
	// Files are filenames touched in the current revision.
	//
	// It's derived from gerrit.ListFilesResponse, see
	// https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-files.
	Files []string `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty"`
	// Git dependencies of the current revision.
	GitDeps []*GerritGitDep `protobuf:"bytes,3,rep,name=git_deps,json=gitDeps,proto3" json:"git_deps,omitempty"`
	// Free-form dependencies. Currently, sourced from CQ-Depend footers.
	// In the future, this may be derived from Gerrit hashtags, topics, or other
	// mechanisms.
	SoftDeps []*GerritSoftDep `protobuf:"bytes,4,rep,name=soft_deps,json=softDeps,proto3" json:"soft_deps,omitempty"`
	// contains filtered or unexported fields
}

func (*Gerrit) Descriptor deprecated

func (*Gerrit) Descriptor() ([]byte, []int)

Deprecated: Use Gerrit.ProtoReflect.Descriptor instead.

func (*Gerrit) GetFiles

func (x *Gerrit) GetFiles() []string

func (*Gerrit) GetGitDeps

func (x *Gerrit) GetGitDeps() []*GerritGitDep

func (*Gerrit) GetHost

func (x *Gerrit) GetHost() string

func (*Gerrit) GetInfo

func (x *Gerrit) GetInfo() *gerrit.ChangeInfo

func (*Gerrit) GetSoftDeps

func (x *Gerrit) GetSoftDeps() []*GerritSoftDep

func (*Gerrit) ProtoMessage

func (*Gerrit) ProtoMessage()

func (*Gerrit) ProtoReflect

func (x *Gerrit) ProtoReflect() protoreflect.Message

func (*Gerrit) Reset

func (x *Gerrit) Reset()

func (*Gerrit) String

func (x *Gerrit) String() string

type GerritGitDep

type GerritGitDep struct {

	// Gerrit Change number.
	Change int64 `protobuf:"varint,1,opt,name=change,proto3" json:"change,omitempty"`
	// Immediate is set iff this dep is an immediate parent of the Gerrit CL.
	//
	// Immediate dep must be submitted before its child.
	// Non-immediate CLs don't necessarily have to be submitted before:
	//
	//	for example, for a chain <base> <- A1 <- B1 <- C1 <- D1
	//	D1's deps are [A,B,C] but only C is immediate, and 1 stands for patchset.
	//	Developer may then swap B,C without re-uploading D (say, to avoid
	//	patchset churn), resulting in a new logical chain:
	//	   <base> <- A1 <- C2 <- B2
	//	                \
	//	                 <- B1 <- C1 <- D1
	//
	//	In this case, Gerrit's related changes for D1 will still return A1,B1,C1,
	//	which CV interprets as C must be landed before D, while B and A should
	//	be landed before D.
	Immediate bool `protobuf:"varint,2,opt,name=immediate,proto3" json:"immediate,omitempty"`
	// contains filtered or unexported fields
}

GerritGitDep is a dependency discovered via Git child->parent chain for one Gerrit CL.

func (*GerritGitDep) Descriptor deprecated

func (*GerritGitDep) Descriptor() ([]byte, []int)

Deprecated: Use GerritGitDep.ProtoReflect.Descriptor instead.

func (*GerritGitDep) GetChange

func (x *GerritGitDep) GetChange() int64

func (*GerritGitDep) GetImmediate

func (x *GerritGitDep) GetImmediate() bool

func (*GerritGitDep) ProtoMessage

func (*GerritGitDep) ProtoMessage()

func (*GerritGitDep) ProtoReflect

func (x *GerritGitDep) ProtoReflect() protoreflect.Message

func (*GerritGitDep) Reset

func (x *GerritGitDep) Reset()

func (*GerritGitDep) String

func (x *GerritGitDep) String() string

type GerritSoftDep

type GerritSoftDep struct {

	// Gerrit host.
	Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
	// Gerrit change number.
	Change int64 `protobuf:"varint,2,opt,name=change,proto3" json:"change,omitempty"`
	// contains filtered or unexported fields
}

func (*GerritSoftDep) Descriptor deprecated

func (*GerritSoftDep) Descriptor() ([]byte, []int)

Deprecated: Use GerritSoftDep.ProtoReflect.Descriptor instead.

func (*GerritSoftDep) GetChange

func (x *GerritSoftDep) GetChange() int64

func (*GerritSoftDep) GetHost

func (x *GerritSoftDep) GetHost() string

func (*GerritSoftDep) ProtoMessage

func (*GerritSoftDep) ProtoMessage()

func (*GerritSoftDep) ProtoReflect

func (x *GerritSoftDep) ProtoReflect() protoreflect.Message

func (*GerritSoftDep) Reset

func (x *GerritSoftDep) Reset()

func (*GerritSoftDep) String

func (x *GerritSoftDep) String() string

type MutateCallback

type MutateCallback func(cl *CL) error

MutateCallback is called by Mutator to mutate the CL inside a transaction.

The function should be idempotent.

If no error is returned, Mutator proceeds saving the CL.

If special ErrStopMutation is returned, Mutator aborts the transaction and returns existing CL read from Datastore and no error. In the special case of Upsert(), the returned CL may actually be nil if CL didn't exist.

If any error is returned other than ErrStopMutation, Mutator aborts the transaction and returns nil CL and the exact same error.

type Mutator

type Mutator struct {
	// contains filtered or unexported fields
}

Mutator modifies CLs and guarantees at least once notification of relevant CV components.

All CL entities in production code must be modified via the Mutator.

Mutator notifies 2 CV components: Run and Project managers. In the future, it'll also notify Tryjob Manager.

Run Manager is notified for each IncompleteRuns in the **new** CL version.

Project manager is notified in following cases:

  1. On the project in the context of which the CL is being modified.
  2. On the project which owns the Snapshot of the *prior* CL version (if it had any Snapshot).

When the number of notifications is large, Mutator may chose to transactionally enqueue a TQ task, which will send notifications in turn.

func NewMutator

func NewMutator(tqd *tq.Dispatcher, pm pmNotifier, rm rmNotifier, tj tjNotifier) *Mutator

func (*Mutator) Adopt

func (m *Mutator) Adopt(ctx context.Context, project string, cl *CL) *CLMutation

Adopt starts a mutation of a given CL which was just read from Datastore.

CL must have been loaded in the same Datastore transaction. CL must have been kept read-only after loading. It's OK to modify it after CLMutation is returned.

Adopt exists when there is substantial advantage in batching loading of CL and non-CL entities in a single Datastore RPC. Prefer to use Begin unless performance consideration is critical.

func (*Mutator) Begin

func (m *Mutator) Begin(ctx context.Context, project string, id common.CLID) (*CLMutation, error)

Begin starts mutation of one CL inside an existing transaction in the context of the given LUCI project.

func (*Mutator) BeginBatch

func (m *Mutator) BeginBatch(ctx context.Context, project string, ids common.CLIDs) ([]*CLMutation, error)

BeginBatch starts a batch of CL mutations within the same Datastore transaction.

func (*Mutator) FinalizeBatch

func (m *Mutator) FinalizeBatch(ctx context.Context, muts []*CLMutation) ([]*CL, error)

FinalizeBatch finishes a batch of CL mutations within the same Datastore transaction.

The given mutations can originate from either Begin or BeginBatch calls. The only requirement is that they must all originate within the current Datastore transaction.

It also transactionally schedules tasks to cancel stale tryjobs for any CLs in the batch whose minEquivPatchset has changed.

func (*Mutator) Update

func (m *Mutator) Update(ctx context.Context, project string, id common.CLID, clbk MutateCallback) (*CL, error)

Update mutates one CL via a dedicated transaction in the context of the given LUCI project.

If the callback returns ErrStopMutation, then Update returns the read CL entity and nil error.

func (*Mutator) Upsert

func (m *Mutator) Upsert(ctx context.Context, project string, eid ExternalID, clbk MutateCallback) (*CL, error)

Upsert creates new or updates existing CL via a dedicated transaction in the context of the given LUCI project.

Prefer to use Update if CL ID is known.

If CL didn't exist before, the callback is provided a CL with temporarily reserved ID. Until Upsert returns with success, this ID is not final, but it's fine to use it in other entities saved within the same transaction.

If CL didn't exist before and the callback returns ErrStopMutation, then Upsert returns (nil, nil).

type Snapshot

type Snapshot struct {

	// The timestamp from external system.
	// Used to determine if re-querying external system is needed.
	ExternalUpdateTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=external_update_time,json=externalUpdateTime,proto3" json:"external_update_time,omitempty"`
	// LUCI project in the context of which this snapshot was saved.
	//
	// Since a CL isn't a resource of CV, CV can't infer whether specific LUCI
	// project has access to a CL w/o re-querying Gerrit and effectively
	// recomputing the snapshot.
	LuciProject string `protobuf:"bytes,2,opt,name=luci_project,json=luciProject,proto3" json:"luci_project,omitempty"`
	// Resolved dependencies of a CL.
	Deps []*Dep `protobuf:"bytes,3,rep,name=deps,proto3" json:"deps,omitempty"`
	// Patchset is incremental number of the latest patchset (aka revision).
	Patchset int32 `protobuf:"varint,4,opt,name=patchset,proto3" json:"patchset,omitempty"`
	// MinEquivalentPatchset is the smallest and hence the earliest patchset
	// which is code-wise equivalent to the latest one.
	//
	// See gerrit.EquivalentPatchsetRange function for details.
	//
	// CV tracks this to determine which prior tryjobs can be re-used and which
	// can be canceled.
	MinEquivalentPatchset int32 `` /* 127-byte string literal not displayed */
	// If set, indicates problems while ingesting CL into CV, which ought to be
	// communicated back to user.
	Errors []*CLError `protobuf:"bytes,6,rep,name=errors,proto3" json:"errors,omitempty"`
	// Outdated if set means Snapshot must be considered likely outdated due to
	// recent CV mutations.
	//
	// In particular, Project Manager does not act on the CLs with .Outdated set.
	Outdated *Snapshot_Outdated `protobuf:"bytes,7,opt,name=outdated,proto3" json:"outdated,omitempty"`
	// Metadata is an ordered list of key-value pairs, which may later be
	// interpreted by CV guts.
	//
	// For example,
	//
	//	[("No-Tree-Checks", "True"), ("NOTRY", "TRUE")].
	//
	// In case of Gerrit CLs, these are extracted from CL descriptions,
	// The Git-Footer-Style keys are normalized.
	// The values are stripped from beginning and trailing whitespace.
	Metadata []*StringPair `protobuf:"bytes,8,rep,name=metadata,proto3" json:"metadata,omitempty"`
	// CL-kind specific data.
	//
	// Types that are assignable to Kind:
	//
	//	*Snapshot_Gerrit
	Kind isSnapshot_Kind `protobuf_oneof:"kind"`
	// contains filtered or unexported fields
}

Snapshot stores a snapshot of CL info as seen by CV at a certain time.

When stored in CL entity, represents latest known Gerrit data. When stored in RunCL entity, represents data pertaining to a fixed patchset.

func (*Snapshot) Descriptor deprecated

func (*Snapshot) Descriptor() ([]byte, []int)

Deprecated: Use Snapshot.ProtoReflect.Descriptor instead.

func (*Snapshot) GetDeps

func (x *Snapshot) GetDeps() []*Dep

func (*Snapshot) GetErrors

func (x *Snapshot) GetErrors() []*CLError

func (*Snapshot) GetExternalUpdateTime

func (x *Snapshot) GetExternalUpdateTime() *timestamppb.Timestamp

func (*Snapshot) GetGerrit

func (x *Snapshot) GetGerrit() *Gerrit

func (*Snapshot) GetKind

func (m *Snapshot) GetKind() isSnapshot_Kind

func (*Snapshot) GetLuciProject

func (x *Snapshot) GetLuciProject() string

func (*Snapshot) GetMetadata

func (x *Snapshot) GetMetadata() []*StringPair

func (*Snapshot) GetMinEquivalentPatchset

func (x *Snapshot) GetMinEquivalentPatchset() int32

func (*Snapshot) GetOutdated

func (x *Snapshot) GetOutdated() *Snapshot_Outdated

func (*Snapshot) GetPatchset

func (x *Snapshot) GetPatchset() int32

func (*Snapshot) IsSubmittable

func (s *Snapshot) IsSubmittable() (bool, error)

IsSubmittable returns whether the change has been approved by the project submit rules.

func (*Snapshot) IsSubmitted

func (s *Snapshot) IsSubmitted() (bool, error)

IsSubmitted returns whether the change has been submitted.

func (*Snapshot) OwnerIdentity

func (s *Snapshot) OwnerIdentity() (identity.Identity, error)

OwnerIdentity is the identity of a user owning this CL.

Snapshot must not be nil.

func (*Snapshot) PanicIfNotValid

func (s *Snapshot) PanicIfNotValid()

PanicIfNotValid checks that Snapshot stored has required fields set.

func (*Snapshot) ProtoMessage

func (*Snapshot) ProtoMessage()

func (*Snapshot) ProtoReflect

func (x *Snapshot) ProtoReflect() protoreflect.Message

func (*Snapshot) Reset

func (x *Snapshot) Reset()

func (*Snapshot) String

func (x *Snapshot) String() string

type Snapshot_Gerrit

type Snapshot_Gerrit struct {
	Gerrit *Gerrit `protobuf:"bytes,11,opt,name=gerrit,proto3,oneof"`
}

type Snapshot_Outdated

type Snapshot_Outdated struct {
	// contains filtered or unexported fields
}

Outdated establishes conditions for refreshing Snapshot after CV mutations.

func (*Snapshot_Outdated) Descriptor deprecated

func (*Snapshot_Outdated) Descriptor() ([]byte, []int)

Deprecated: Use Snapshot_Outdated.ProtoReflect.Descriptor instead.

func (*Snapshot_Outdated) ProtoMessage

func (*Snapshot_Outdated) ProtoMessage()

func (*Snapshot_Outdated) ProtoReflect

func (x *Snapshot_Outdated) ProtoReflect() protoreflect.Message

func (*Snapshot_Outdated) Reset

func (x *Snapshot_Outdated) Reset()

func (*Snapshot_Outdated) String

func (x *Snapshot_Outdated) String() string

type StringPair

type StringPair struct {
	Key   string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
	// contains filtered or unexported fields
}

A string key-value pair.

func (*StringPair) Descriptor deprecated

func (*StringPair) Descriptor() ([]byte, []int)

Deprecated: Use StringPair.ProtoReflect.Descriptor instead.

func (*StringPair) GetKey

func (x *StringPair) GetKey() string

func (*StringPair) GetValue

func (x *StringPair) GetValue() string

func (*StringPair) ProtoMessage

func (*StringPair) ProtoMessage()

func (*StringPair) ProtoReflect

func (x *StringPair) ProtoReflect() protoreflect.Message

func (*StringPair) Reset

func (x *StringPair) Reset()

func (*StringPair) String

func (x *StringPair) String() string

type UpdateCLTask

type UpdateCLTask struct {
	LuciProject string `protobuf:"bytes,1,opt,name=luci_project,json=luciProject,proto3" json:"luci_project,omitempty"`
	// At least one of internal or external ID must be given.
	Id         int64  `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // internal CLID
	ExternalId string `protobuf:"bytes,3,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"`
	// Requester identifies various scenarios that enqueue UpdateCLTask(s).
	//
	// This is used to track UpdateCLTask(s) by the requester for monitoring
	// purposes.
	Requester UpdateCLTask_Requester `protobuf:"varint,5,opt,name=requester,proto3,enum=cv.internal.changelist.UpdateCLTask_Requester" json:"requester,omitempty"`
	// True if the UpdateCLTask was enqueued to resolve a dependency.
	IsForDep bool `protobuf:"varint,6,opt,name=is_for_dep,json=isForDep,proto3" json:"is_for_dep,omitempty"`
	// Hint provides various hints for the snapshot to be fetched.
	Hint *UpdateCLTask_Hint `protobuf:"bytes,7,opt,name=hint,proto3" json:"hint,omitempty"`
	// contains filtered or unexported fields
}

UpdateCLTask is for updating a single CL.

Queue: "update-cl".

func (*UpdateCLTask) Descriptor deprecated

func (*UpdateCLTask) Descriptor() ([]byte, []int)

Deprecated: Use UpdateCLTask.ProtoReflect.Descriptor instead.

func (*UpdateCLTask) GetExternalId

func (x *UpdateCLTask) GetExternalId() string

func (*UpdateCLTask) GetHint

func (x *UpdateCLTask) GetHint() *UpdateCLTask_Hint

func (*UpdateCLTask) GetId

func (x *UpdateCLTask) GetId() int64

func (*UpdateCLTask) GetIsForDep

func (x *UpdateCLTask) GetIsForDep() bool

func (*UpdateCLTask) GetLuciProject

func (x *UpdateCLTask) GetLuciProject() string

func (*UpdateCLTask) GetRequester

func (x *UpdateCLTask) GetRequester() UpdateCLTask_Requester

func (*UpdateCLTask) ProtoMessage

func (*UpdateCLTask) ProtoMessage()

func (*UpdateCLTask) ProtoReflect

func (x *UpdateCLTask) ProtoReflect() protoreflect.Message

func (*UpdateCLTask) Reset

func (x *UpdateCLTask) Reset()

func (*UpdateCLTask) String

func (x *UpdateCLTask) String() string

type UpdateCLTask_Hint

type UpdateCLTask_Hint struct {

	// The external update time of the Snapshot to fetch.
	ExternalUpdateTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=external_update_time,json=externalUpdateTime,proto3" json:"external_update_time,omitempty"`
	// The meta rev ID of the Snapshot to fetch.
	MetaRevId string `protobuf:"bytes,2,opt,name=meta_rev_id,json=metaRevId,proto3" json:"meta_rev_id,omitempty"`
	// contains filtered or unexported fields
}

func (*UpdateCLTask_Hint) Descriptor deprecated

func (*UpdateCLTask_Hint) Descriptor() ([]byte, []int)

Deprecated: Use UpdateCLTask_Hint.ProtoReflect.Descriptor instead.

func (*UpdateCLTask_Hint) GetExternalUpdateTime

func (x *UpdateCLTask_Hint) GetExternalUpdateTime() *timestamppb.Timestamp

func (*UpdateCLTask_Hint) GetMetaRevId

func (x *UpdateCLTask_Hint) GetMetaRevId() string

func (*UpdateCLTask_Hint) ProtoMessage

func (*UpdateCLTask_Hint) ProtoMessage()

func (*UpdateCLTask_Hint) ProtoReflect

func (x *UpdateCLTask_Hint) ProtoReflect() protoreflect.Message

func (*UpdateCLTask_Hint) Reset

func (x *UpdateCLTask_Hint) Reset()

func (*UpdateCLTask_Hint) String

func (x *UpdateCLTask_Hint) String() string

type UpdateCLTask_Requester

type UpdateCLTask_Requester int32
const (
	UpdateCLTask_REQUESTER_CLASS_UNSPECIFIED UpdateCLTask_Requester = 0
	UpdateCLTask_INCR_POLL_MATCHED           UpdateCLTask_Requester = 1
	UpdateCLTask_FULL_POLL_MATCHED           UpdateCLTask_Requester = 2
	UpdateCLTask_FULL_POLL_UNMATCHED         UpdateCLTask_Requester = 3
	UpdateCLTask_PUBSUB_POLL                 UpdateCLTask_Requester = 4
	UpdateCLTask_CL_PURGER                   UpdateCLTask_Requester = 5
	UpdateCLTask_DEP_CL_TRIGGERER            UpdateCLTask_Requester = 12
	UpdateCLTask_RPC_ADMIN                   UpdateCLTask_Requester = 6
	UpdateCLTask_RUN_POKE                    UpdateCLTask_Requester = 7
	UpdateCLTask_RUN_REMOVAL                 UpdateCLTask_Requester = 8
	UpdateCLTask_RESET_CL_TRIGGER            UpdateCLTask_Requester = 11
	UpdateCLTask_UPDATE_CONFIG               UpdateCLTask_Requester = 10
)

func (UpdateCLTask_Requester) Descriptor

func (UpdateCLTask_Requester) Enum

func (UpdateCLTask_Requester) EnumDescriptor deprecated

func (UpdateCLTask_Requester) EnumDescriptor() ([]byte, []int)

Deprecated: Use UpdateCLTask_Requester.Descriptor instead.

func (UpdateCLTask_Requester) Number

func (UpdateCLTask_Requester) String

func (x UpdateCLTask_Requester) String() string

func (UpdateCLTask_Requester) Type

type UpdateFields

type UpdateFields struct {
	// Snapshot overwrites existing CL snapshot if newer according to its
	// .ExternalUpdateTime.
	Snapshot *Snapshot

	// ApplicableConfig overwrites existing CL ApplicableConfig if semantically
	// different from existing one.
	ApplicableConfig *ApplicableConfig

	// AddDependentMeta adds or overwrites metadata per LUCI project in CL AsDepMeta.
	// Doesn't affect metadata stored for projects not referenced here.
	AddDependentMeta *Access

	// DelAccess deletes Access records for the given projects.
	DelAccess []string
}

UpdateFields defines what parts of CL to update.

At least one field must be specified.

func (UpdateFields) Apply

func (u UpdateFields) Apply(cl *CL, backend UpdaterBackend) (changed, changedSnapshot bool)

Apply applies the UpdatedFields to a given CL.

func (UpdateFields) IsEmpty

func (u UpdateFields) IsEmpty() bool

IsEmpty returns true if no updates are necessary.

type Updater

type Updater struct {
	// contains filtered or unexported fields
}

Updater knows how to update CLs from relevant backend (e.g. Gerrit), notifying other CV parts as needed.

func NewUpdater

func NewUpdater(tqd *tq.Dispatcher, m *Mutator) *Updater

NewUpdater creates a new Updater.

Starts without backends, but they ought to be added via RegisterBackend().

func (*Updater) RegisterBackend

func (u *Updater) RegisterBackend(b UpdaterBackend)

RegisterBackend registers a backend.

Panics if backend for the same kind is already registered.

func (*Updater) ResolveAndScheduleDepsUpdate

func (u *Updater) ResolveAndScheduleDepsUpdate(ctx context.Context, luciProject string, deps map[ExternalID]DepKind, requester UpdateCLTask_Requester) ([]*Dep, error)

ResolveAndScheduleDepsUpdate resolves deps, creating new CL entities as necessary, and schedules an update task for each dep which needs an update.

It's meant to be used by the Updater backends.

Returns a sorted slice of Deps by their CL ID, ready to be stored as CL.Snapshot.Deps.

func (*Updater) Schedule

func (u *Updater) Schedule(ctx context.Context, payload *UpdateCLTask) error

Schedule dispatches a TQ task. It should be used instead of the direct tq.AddTask to allow for consistent de-duplication.

func (*Updater) ScheduleBatch

func (u *Updater) ScheduleBatch(ctx context.Context, luciProject string, cls []*CL, requester UpdateCLTask_Requester) error

ScheduleBatch schedules update of several CLs.

If called in a transaction, enqueues exactly one TQ task transactionally. This allows to write 1 Datastore entity during a transaction instead of N entities if Schedule() was used for each CL.

Otherwise, enqueues 1 TQ task per CL non-transactionally and in parallel.

func (*Updater) ScheduleDelayed

func (u *Updater) ScheduleDelayed(ctx context.Context, payload *UpdateCLTask, delay time.Duration) error

ScheduleDelayed is the same as Schedule but with a delay.

func (*Updater) TestingForceUpdate

func (u *Updater) TestingForceUpdate(ctx context.Context, task *UpdateCLTask) error

TestingForceUpdate runs the CL Updater synchronously.

For use in tests only. Production code should use Schedule() to benefit from task de-duplication.

TODO(crbug/1284393): revisit the usefulness of the sync refresh after consistency-on-demand is provided by Gerrit.

type UpdaterBackend

type UpdaterBackend interface {
	// Kind identifies the backend.
	//
	// It's also the first part of the CL's ExternalID, e.g. "gerrit".
	// Must not contain a slash.
	Kind() string

	// LookupApplicableConfig returns the latest ApplicableConfig for the previously
	// saved CL.
	//
	// See CL.ApplicableConfig field doc for more details. Roughly, it finds which
	// LUCI projects are configured to watch this CL.
	//
	// Updater calls LookupApplicableConfig() before Fetch() in order to avoid
	// the unnecessary Fetch() call entirely, e.g. if the CL is up to date or if
	// the CL is definitely not watched by a specific LUCI project.
	//
	// Returns non-nil ApplicableConfig normally.
	// Returns nil ApplicableConfig if the previously saved CL state isn't
	// sufficient to confidently determine the ApplicableConfig.
	LookupApplicableConfig(ctx context.Context, saved *CL) (*ApplicableConfig, error)

	// Fetch fetches the CL in the context of a given project.
	Fetch(ctx context.Context, input *FetchInput) (UpdateFields, error)

	// HasChanged decides whether the CL in the backend has changed from existing
	// snapshot in LUCI CV.
	HasChanged(cvCurrent, backendCurrent *Snapshot) bool

	// TQErrorSpec allows customizing logging and error TQ-specific handling.
	//
	// For example, Gerrit backend may wish to retry out of quota errors without
	// logging detailed stacktrace.
	TQErrorSpec() common.TQIfy
}

UpdaterBackend abstracts out fetching CL details from code review backend.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL