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 ¶
- Variables
- func Delete(ctx context.Context, id common.CLID) error
- func Lookup(ctx context.Context, eids []ExternalID) ([]common.CLID, error)
- func RemoveUnusedGerritInfo(ci *gerritpb.ChangeInfo)
- func Sort(cls []*CL)
- type Access
- type AccessKind
- type Access_Project
- func (*Access_Project) Descriptor() ([]byte, []int)deprecated
- func (x *Access_Project) GetNoAccess() bool
- func (x *Access_Project) GetNoAccessTime() *timestamppb.Timestamp
- func (x *Access_Project) GetUpdateTime() *timestamppb.Timestamp
- func (*Access_Project) ProtoMessage()
- func (x *Access_Project) ProtoReflect() protoreflect.Message
- func (x *Access_Project) Reset()
- func (x *Access_Project) String() string
- type ApplicableConfig
- func (*ApplicableConfig) Descriptor() ([]byte, []int)deprecated
- func (x *ApplicableConfig) GetProjects() []*ApplicableConfig_Project
- func (a *ApplicableConfig) HasOnlyProject(luciProject string) bool
- func (a *ApplicableConfig) HasProject(luciProject string) bool
- func (*ApplicableConfig) ProtoMessage()
- func (x *ApplicableConfig) ProtoReflect() protoreflect.Message
- func (x *ApplicableConfig) Reset()
- func (a *ApplicableConfig) SemanticallyEqual(b *ApplicableConfig) bool
- func (x *ApplicableConfig) String() string
- type ApplicableConfig_Project
- func (*ApplicableConfig_Project) Descriptor() ([]byte, []int)deprecated
- func (x *ApplicableConfig_Project) GetConfigGroupIds() []string
- func (x *ApplicableConfig_Project) GetName() string
- func (*ApplicableConfig_Project) ProtoMessage()
- func (x *ApplicableConfig_Project) ProtoReflect() protoreflect.Message
- func (x *ApplicableConfig_Project) Reset()
- func (x *ApplicableConfig_Project) String() string
- type BatchOnCLUpdatedTask
- func (*BatchOnCLUpdatedTask) Descriptor() ([]byte, []int)deprecated
- func (x *BatchOnCLUpdatedTask) GetProjects() map[string]*CLUpdatedEvents
- func (x *BatchOnCLUpdatedTask) GetRuns() map[string]*CLUpdatedEvents
- func (*BatchOnCLUpdatedTask) ProtoMessage()
- func (x *BatchOnCLUpdatedTask) ProtoReflect() protoreflect.Message
- func (x *BatchOnCLUpdatedTask) Reset()
- func (x *BatchOnCLUpdatedTask) String() string
- type CL
- func (cl *CL) AccessKind(ctx context.Context, luciProject string) AccessKind
- func (cl *CL) AccessKindFromCodeReviewSite(ctx context.Context, luciProject string) AccessKind
- func (cl *CL) AccessKindWithReason(ctx context.Context, luciProject string) (AccessKind, string)
- func (cl *CL) Mutate(ctx context.Context, mut func(*CL) (updated bool)) (updated bool)
- func (cl *CL) URL() (string, error)
- type CLError
- func (*CLError) Descriptor() ([]byte, []int)deprecated
- func (x *CLError) GetCorruptGerritMetadata() string
- func (x *CLError) GetInvalidDeps() *CLError_InvalidDeps
- func (m *CLError) GetKind() isCLError_Kind
- func (x *CLError) GetOwnerLacksEmail() bool
- func (x *CLError) GetReusedTrigger() *CLError_ReusedTrigger
- func (x *CLError) GetSelfCqDepend() bool
- func (x *CLError) GetUnsupportedMode() string
- func (x *CLError) GetWatchedByManyConfigGroups() *CLError_WatchedByManyConfigGroups
- func (*CLError) ProtoMessage()
- func (x *CLError) ProtoReflect() protoreflect.Message
- func (x *CLError) Reset()
- func (x *CLError) String() string
- type CLError_CorruptGerritMetadata
- type CLError_InvalidDeps
- func (*CLError_InvalidDeps) Descriptor() ([]byte, []int)deprecated
- func (x *CLError_InvalidDeps) GetCombinableMismatchedMode() []*Dep
- func (x *CLError_InvalidDeps) GetCombinableUntriggered() []*Dep
- func (x *CLError_InvalidDeps) GetSingleFullDeps() []*Dep
- func (x *CLError_InvalidDeps) GetTooMany() *CLError_InvalidDeps_TooMany
- func (x *CLError_InvalidDeps) GetUnwatched() []*Dep
- func (x *CLError_InvalidDeps) GetWrongConfigGroup() []*Dep
- func (*CLError_InvalidDeps) ProtoMessage()
- func (x *CLError_InvalidDeps) ProtoReflect() protoreflect.Message
- func (x *CLError_InvalidDeps) Reset()
- func (x *CLError_InvalidDeps) String() string
- type CLError_InvalidDeps_
- type CLError_InvalidDeps_TooMany
- func (*CLError_InvalidDeps_TooMany) Descriptor() ([]byte, []int)deprecated
- func (x *CLError_InvalidDeps_TooMany) GetActual() int32
- func (x *CLError_InvalidDeps_TooMany) GetMaxAllowed() int32
- func (*CLError_InvalidDeps_TooMany) ProtoMessage()
- func (x *CLError_InvalidDeps_TooMany) ProtoReflect() protoreflect.Message
- func (x *CLError_InvalidDeps_TooMany) Reset()
- func (x *CLError_InvalidDeps_TooMany) String() string
- type CLError_OwnerLacksEmail
- type CLError_ReusedTrigger
- func (*CLError_ReusedTrigger) Descriptor() ([]byte, []int)deprecated
- func (x *CLError_ReusedTrigger) GetRun() string
- func (*CLError_ReusedTrigger) ProtoMessage()
- func (x *CLError_ReusedTrigger) ProtoReflect() protoreflect.Message
- func (x *CLError_ReusedTrigger) Reset()
- func (x *CLError_ReusedTrigger) String() string
- type CLError_ReusedTrigger_
- type CLError_SelfCqDepend
- type CLError_UnsupportedMode
- type CLError_WatchedByManyConfigGroups
- func (*CLError_WatchedByManyConfigGroups) Descriptor() ([]byte, []int)deprecated
- func (x *CLError_WatchedByManyConfigGroups) GetConfigGroups() []string
- func (*CLError_WatchedByManyConfigGroups) ProtoMessage()
- func (x *CLError_WatchedByManyConfigGroups) ProtoReflect() protoreflect.Message
- func (x *CLError_WatchedByManyConfigGroups) Reset()
- func (x *CLError_WatchedByManyConfigGroups) String() string
- type CLError_WatchedByManyConfigGroups_
- type CLMutation
- type CLUpdatedEvent
- func (*CLUpdatedEvent) Descriptor() ([]byte, []int)deprecated
- func (x *CLUpdatedEvent) GetClid() int64
- func (x *CLUpdatedEvent) GetEversion() int64
- func (*CLUpdatedEvent) ProtoMessage()
- func (x *CLUpdatedEvent) ProtoReflect() protoreflect.Message
- func (x *CLUpdatedEvent) Reset()
- func (x *CLUpdatedEvent) String() string
- type CLUpdatedEvents
- type Dep
- type DepKind
- type ExternalID
- func (eid ExternalID) Get(ctx context.Context) (*CL, error)
- func (eid ExternalID) GetOrInsert(ctx context.Context, populate func(cl *CL)) (*CL, error)
- func (e ExternalID) MustURL() string
- func (e ExternalID) ParseGobID() (host string, change int64, err error)
- func (e ExternalID) URL() (string, error)
- type Gerrit
- func (*Gerrit) Descriptor() ([]byte, []int)deprecated
- func (x *Gerrit) GetFiles() []string
- func (x *Gerrit) GetGitDeps() []*GerritGitDep
- func (x *Gerrit) GetHost() string
- func (x *Gerrit) GetInfo() *gerrit.ChangeInfo
- func (x *Gerrit) GetSoftDeps() []*GerritSoftDep
- func (*Gerrit) ProtoMessage()
- func (x *Gerrit) ProtoReflect() protoreflect.Message
- func (x *Gerrit) Reset()
- func (x *Gerrit) String() string
- type GerritGitDep
- func (*GerritGitDep) Descriptor() ([]byte, []int)deprecated
- func (x *GerritGitDep) GetChange() int64
- func (x *GerritGitDep) GetImmediate() bool
- func (*GerritGitDep) ProtoMessage()
- func (x *GerritGitDep) ProtoReflect() protoreflect.Message
- func (x *GerritGitDep) Reset()
- func (x *GerritGitDep) String() string
- type GerritSoftDep
- func (*GerritSoftDep) Descriptor() ([]byte, []int)deprecated
- func (x *GerritSoftDep) GetChange() int64
- func (x *GerritSoftDep) GetHost() string
- func (*GerritSoftDep) ProtoMessage()
- func (x *GerritSoftDep) ProtoReflect() protoreflect.Message
- func (x *GerritSoftDep) Reset()
- func (x *GerritSoftDep) String() string
- type MutateCallback
- type Mutator
- func (m *Mutator) Begin(ctx context.Context, project string, id common.CLID) (*CLMutation, error)
- func (m *Mutator) BeginBatch(ctx context.Context, project string, ids common.CLIDs) ([]*CLMutation, error)
- func (m *Mutator) FinalizeBatch(ctx context.Context, muts []*CLMutation) ([]*CL, error)
- func (m *Mutator) Update(ctx context.Context, project string, id common.CLID, clbk MutateCallback) (*CL, error)
- func (m *Mutator) Upsert(ctx context.Context, project string, eid ExternalID, clbk MutateCallback) (*CL, error)
- type Notify
- type Snapshot
- func (*Snapshot) Descriptor() ([]byte, []int)deprecated
- func (x *Snapshot) GetDeps() []*Dep
- func (x *Snapshot) GetErrors() []*CLError
- func (x *Snapshot) GetExternalUpdateTime() *timestamppb.Timestamp
- func (x *Snapshot) GetGerrit() *Gerrit
- func (m *Snapshot) GetKind() isSnapshot_Kind
- func (x *Snapshot) GetLuciProject() string
- func (x *Snapshot) GetMinEquivalentPatchset() int32
- func (x *Snapshot) GetOutdated() *Snapshot_Outdated
- func (x *Snapshot) GetPatchset() int32
- func (s *Snapshot) OwnerIdentity() (identity.Identity, error)
- func (s *Snapshot) PanicIfNotValid()
- func (*Snapshot) ProtoMessage()
- func (x *Snapshot) ProtoReflect() protoreflect.Message
- func (x *Snapshot) Reset()
- func (x *Snapshot) String() string
- type Snapshot_Gerrit
- type Snapshot_Outdated
Constants ¶
This section is empty.
Variables ¶
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.
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) ```
var File_go_chromium_org_luci_cv_internal_changelist_storage_proto protoreflect.FileDescriptor
var File_go_chromium_org_luci_cv_internal_changelist_task_proto protoreflect.FileDescriptor
Functions ¶
func Delete ¶
Delete deletes CL and its CLMap entities transactionally.
Thus, Delete 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 Lookup ¶
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 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.
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) GetByProject ¶
func (x *Access) GetByProject() map[string]*Access_Project
func (*Access) ProtoMessage ¶
func (*Access) ProtoMessage()
func (*Access) ProtoReflect ¶
func (x *Access) ProtoReflect() protoreflect.Message
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 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 int `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"` // 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"` // contains filtered or unexported fields }
CL is a CL entity in Datastore.
func LoadCLsMap ¶
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 ¶
AccessKindWithReason returns AccessKind of a CL and a reason for it.
type CLError ¶
type CLError struct { // Types that are assignable to Kind: // *CLError_OwnerLacksEmail // *CLError_WatchedByManyConfigGroups_ // *CLError_InvalidDeps_ // *CLError_UnsupportedMode // *CLError_SelfCqDepend // *CLError_CorruptGerritMetadata // *CLError_ReusedTrigger_ 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) GetCorruptGerritMetadata ¶
func (*CLError) GetInvalidDeps ¶
func (x *CLError) GetInvalidDeps() *CLError_InvalidDeps
func (*CLError) GetOwnerLacksEmail ¶
func (*CLError) GetReusedTrigger ¶
func (x *CLError) GetReusedTrigger() *CLError_ReusedTrigger
func (*CLError) GetSelfCqDepend ¶
func (*CLError) GetUnsupportedMode ¶
func (*CLError) GetWatchedByManyConfigGroups ¶
func (x *CLError) GetWatchedByManyConfigGroups() *CLError_WatchedByManyConfigGroups
func (*CLError) ProtoMessage ¶
func (*CLError) ProtoMessage()
func (*CLError) ProtoReflect ¶
func (x *CLError) ProtoReflect() protoreflect.Message
type CLError_CorruptGerritMetadata ¶
type CLError_CorruptGerritMetadata struct {
CorruptGerritMetadata string `protobuf:"bytes,6,opt,name=corrupt_gerrit_metadata,json=corruptGerritMetadata,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 (x *CLError_InvalidDeps) GetTooMany() *CLError_InvalidDeps_TooMany
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 (x *CLError_InvalidDeps_TooMany) ProtoReflect() protoreflect.Message
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:
- 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.
- 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_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 (x *CLError_WatchedByManyConfigGroups) ProtoReflect() protoreflect.Message
func (*CLError_WatchedByManyConfigGroups) Reset ¶
func (x *CLError_WatchedByManyConfigGroups) Reset()
func (*CLError_WatchedByManyConfigGroups) String ¶
func (x *CLError_WatchedByManyConfigGroups) String() 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 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.
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 (*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) ProtoMessage ¶
func (*Dep) ProtoMessage()
func (*Dep) ProtoReflect ¶
func (x *Dep) ProtoReflect() protoreflect.Message
type DepKind ¶
type DepKind int32
func (DepKind) Descriptor ¶
func (DepKind) Descriptor() protoreflect.EnumDescriptor
func (DepKind) EnumDescriptor
deprecated
func (DepKind) Number ¶
func (x DepKind) Number() protoreflect.EnumNumber
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) Get ¶
func (eid ExternalID) Get(ctx context.Context) (*CL, error)
Get reads a CL from Datastore.
Returns datastore.ErrNoSuchEntity if it doesn't exist.
func (ExternalID) GetOrInsert ¶
GetOrInsert reads a CL from Datastore, creating a new one if it doesn't exist yet.
populate is called within a transaction to populate fields of a new entity. It should be a fast function.
Warning:
- populate may be called several times since transaction can be retried.
- cl.ExternalID and cl.ID must not be changed by populate.
func (ExternalID) MustURL ¶
func (e ExternalID) MustURL() string
MustURL is like `URL()` but panic on err.
func (ExternalID) ParseGobID ¶
func (e ExternalID) ParseGobID() (host string, change int64, err error)
ParseGobID returns Gerrit host and change if this is a GobID.
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 (for current_revision only) // * message (current CL description) // * 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) GetGitDeps ¶
func (x *Gerrit) GetGitDeps() []*GerritGitDep
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
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. // // TODO(tandrii): this is replicating existing CQDaemon logic. I think // it'd be reasonable to treat all (A,B,C) as MUST BE submitted 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 ¶
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 tranasction 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:
- On the project in the context of which the CL is being modified.
- 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(pm pmNotifier, rm rmNotifier) *Mutator
func (*Mutator) Begin ¶
Begin starts mutation of one CL inside an existing transaction in the context of the given LUCI project.
func (*Mutator) BeginBatch ¶
func (*Mutator) FinalizeBatch ¶
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 Notify ¶
Notify is called with the updated CL in a transaction context after CL is successfully created/updated.
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"` // 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) GetExternalUpdateTime ¶
func (x *Snapshot) GetExternalUpdateTime() *timestamppb.Timestamp
func (*Snapshot) GetLuciProject ¶
func (*Snapshot) GetMinEquivalentPatchset ¶
func (*Snapshot) GetOutdated ¶
func (x *Snapshot) GetOutdated() *Snapshot_Outdated
func (*Snapshot) GetPatchset ¶
func (*Snapshot) OwnerIdentity ¶
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
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