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

This section is empty.

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 File_go_chromium_org_luci_cv_internal_changelist_storage_proto protoreflect.FileDescriptor

Functions

func Delete

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

Delete deletes CL and its CLMap entities trasactionally.

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 temporary error, but on retry they would return ErrNoSuchEntity.

func Update

func Update(ctx context.Context, eid ExternalID, knownCLID CLID, snapshot *Snapshot, acfg *ApplicableConfig) error

Update updates CL entity with Snapshot and ApplicableConfig.

Either ExternalID or a known CLID must be provided. Either new Snapshot or ApplicableConfig must be provided.

If CLID is not known and CL for provided ExternalID doesn't exist, then a new CL is created with the given Snapshot & ApplicableConfig.

Otherwise, an existing CL entity will be updated iff either:

* if snapshot is given and it is more recent than what is already stored,
  as measured by ExternalUpdateTime.
* same but for ApplicableConfig and ApplicableConfig.UpdateTime,
  respectively.

TODO(tandrii): emit notification events.

Types

type ApplicableConfig

type ApplicableConfig struct {

	// The timestamp when this was computed.
	UpdateTime *timestamppb.Timestamp      `protobuf:"bytes,1,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
	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

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

Deprecated: Use ApplicableConfig.ProtoReflect.Descriptor instead.

func (*ApplicableConfig) GetProjects

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

func (*ApplicableConfig) GetUpdateTime

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

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) IsUpToDate

func (s *ApplicableConfig) IsUpToDate(t time.Time) bool

IsUpToDate returns whether stored ApplicableConfig is at least as recent as given time.

func (*ApplicableConfig) ProtoMessage

func (*ApplicableConfig) ProtoMessage()

func (*ApplicableConfig) ProtoReflect

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

func (*ApplicableConfig) Reset

func (x *ApplicableConfig) Reset()

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

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 CL

type CL struct {

	// ID is auto-generated by Datastore.
	ID 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"`

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

	// ApplicableConfig keeps track of configs applicable to the CL.
	ApplicableConfig *ApplicableConfig

	// 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"`
	// contains filtered or unexported fields
}

CL is a CL entity in Datastore.

func (*CL) NeedsFetching

func (cl *CL) NeedsFetching(ctx context.Context, luciProject string) (bool, error)

NeedsFetching returns true if the CL is likely out of date and would benefit from fetching in the context of a given project.

type CLID

type CLID int64

CLID is a unique ID of a CL used internally in CV.

It's just 8 bytes long and is thus much shorter than ExternalID, which reduces CPU & RAM & storage costs of CL graphs for multi-CL Runs.

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.changelist.DepKind" json:"kind,omitempty"`
	// contains filtered or unexported fields
}

func (*Dep) Descriptor

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

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 (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

func (eid ExternalID) GetOrInsert(ctx context.Context, populate func(cl *CL)) (*CL, error)

GetOrInsert reads a CL from datastore, creating a new one if not exists 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) 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 {

	// Info contains all revisions, but non-current revisions will not have all
	// the fields populated.
	//
	// Exact fields TODO.
	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

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) 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.
	//
	// 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

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

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 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 */
	// 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

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

Deprecated: Use Snapshot.ProtoReflect.Descriptor instead.

func (*Snapshot) GetDeps

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

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) GetMinEquivalentPatchset

func (x *Snapshot) GetMinEquivalentPatchset() int32

func (*Snapshot) GetPatchset

func (x *Snapshot) GetPatchset() int32

func (*Snapshot) IsUpToDate

func (s *Snapshot) IsUpToDate(luciProject string, t time.Time) bool

IsUpToDate returns whether stored Snapshot is at least as recent as given time and was done for a matching LUCI project.

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"`
}