blob: 6ce4aec7289c425434bf3a0276d8a8e6d966d993 [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from google.appengine.ext import ndb
class IsolatedTarget(ndb.Model):
"""Represents a test target that has been isolated by a builder.
The isolated hash of a given target can't be used as the key for this model
because different builds may produce the same isolated target, e.g. if no
changes happen to the files that produce the target happen between such builds
and due to incremental compilation even the timestamps do not change.
A combination of the build id and the target is used as the key since there is
no duplicate target in the same build.
"""
# Luci project, such as 'chromium'.
luci_project = ndb.StringProperty(required=True)
# Buildbucket bucket. Note that this is expected to be the suffix to
# 'luci.<project>' e.g. For 'luci.chromium.ci' this is expected to be 'ci'.
bucket = ndb.StringProperty(required=True)
# For querying by master, important for pre-luci builds
master_name = ndb.StringProperty(required=True)
# Name of the builder name whose build configuration the isolated build
# matches. (It does not have to match the actual builder that compiled it,
# such as when the findit_variable builder compiles targets during tryjob).
# e.g. 'Linux Builder', use parent_buildername when appropriate.
builder_name = ndb.StringProperty(required=True)
# The host for the git repo. e.g. 'chromium.googlesource.com'.
gitiles_host = ndb.StringProperty(required=True)
# The git project e.g. 'chromium/src'. Note that this excludes .git.
gitiles_project = ndb.StringProperty(required=True)
# The ref that the ci builder builds from. e.g. 'refs/heads/master'.
gitiles_ref = ndb.StringProperty(required=True)
# The buildbucket id of the build, if available.
build_id = ndb.IntegerProperty(indexed=False)
# The compile target as named in the output of the
# isolating step. e.g. 'browser_tests'
target_name = ndb.StringProperty(required=True)
# An integer representing the number component of the commit position string.
# e.g. 557975 for 'refs/heads/master@{#557975}'.
commit_position = ndb.IntegerProperty(required=True)
# The gerrit patch in host/issue/patchset format
# e.g. 'chromium-review.googlesource.com/1065898/2'.
# For builds without patch this is expected to be empty.
gerrit_patch = ndb.StringProperty(indexed=False)
# The hash of the isolated target.
isolated_hash = ndb.StringProperty()
@ndb.ComputedProperty
def has_patch(self):
"""Whether the target was built with a patch.
This is to distinguish possible multiple isolates from a tryjob or cq run.
"""
return bool(self.gerrit_patch)
def GetIsolatedHash(self):
# This fallback is to be able to retrieve old entries (pre Sept. 2018) that
# were wrongly keyed with the isolated hash alone.
return self.isolated_hash or self.key.pairs()[0][1]
@property
def build_url(self):
return 'https://ci.chromium.org/b/{}'.format(self.build_id)
@classmethod
def _CreateKey(cls, build_id, target_name):
return ndb.Key(cls, '/'.join([str(build_id), target_name]))
@classmethod
def Create(cls, build_id, luci_project, bucket, master_name, builder_name,
gitiles_host, gitiles_project, gitiles_ref, gerrit_patch,
target_name, isolated_hash, commit_position):
return cls(
key=cls._CreateKey(build_id, target_name),
build_id=build_id,
luci_project=luci_project,
bucket=bucket,
master_name=master_name,
builder_name=builder_name,
gitiles_host=gitiles_host,
gitiles_project=gitiles_project,
gitiles_ref=gitiles_ref,
gerrit_patch=gerrit_patch,
target_name=target_name,
commit_position=commit_position,
isolated_hash=isolated_hash,
)
@classmethod
def FindIsolateBeforeCommitPositionByBucket(cls,
luci_project,
bucket,
builder_name,
gitiles_host,
gitiles_project,
gitiles_ref,
target_name,
commit_position,
limit=1):
"""Gets isolates with the matching config built at previous revisions.
The reulsts will exclude the revision given.
"""
# pylint: disable=singleton-comparison
return cls.query(
cls.has_patch == False, cls.luci_project == luci_project,
cls.bucket == bucket, cls.builder_name == builder_name,
cls.gitiles_host == gitiles_host,
cls.gitiles_project == gitiles_project, cls.gitiles_ref == gitiles_ref,
cls.target_name == target_name, cls.commit_position <
commit_position).order(-cls.commit_position).fetch(limit=limit)
@classmethod
def FindIsolateBeforeCommitPositionByMaster(cls,
master_name,
builder_name,
gitiles_host,
gitiles_project,
gitiles_ref,
target_name,
commit_position,
limit=1):
"""Same as FindIsolateBeforeCommitPosition, but with master_name.
The results will exclude the revision given and uses master_name instead of
project + bucket.
"""
# pylint: disable=singleton-comparison
return cls.query(
cls.has_patch == False, cls.master_name == master_name,
cls.builder_name == builder_name, cls.gitiles_host == gitiles_host,
cls.gitiles_project == gitiles_project, cls.gitiles_ref == gitiles_ref,
cls.target_name == target_name, cls.commit_position <
commit_position).order(-cls.commit_position).fetch(limit=limit)
@classmethod
def FindIsolateAtOrAfterCommitPositionByBucket(cls,
luci_project,
bucket,
builder_name,
gitiles_host,
gitiles_project,
gitiles_ref,
target_name,
commit_position,
limit=1):
"""Gets isolates with the matching config built at or after the revision.
The results may include the commit postion given.
"""
# pylint: disable=singleton-comparison
return cls.query(cls.has_patch == False, cls.luci_project == luci_project,
cls.bucket == bucket, cls.builder_name == builder_name,
cls.gitiles_host == gitiles_host,
cls.gitiles_project == gitiles_project,
cls.gitiles_ref == gitiles_ref,
cls.target_name == target_name,
cls.commit_position >= commit_position).order(
cls.commit_position).fetch(limit=limit)
@classmethod
def FindIsolateAtOrAfterCommitPositionByMaster(cls,
master_name,
builder_name,
gitiles_host,
gitiles_project,
gitiles_ref,
target_name,
commit_position,
limit=1):
"""Same as FindIsolateAtOrAfterCommitPosition, but with master_name.
The results may include the commit position given and uses master_name
instead of project + bucket.
"""
# pylint: disable=singleton-comparison
return cls.query(
cls.has_patch == False, cls.master_name == master_name,
cls.builder_name == builder_name, cls.gitiles_host == gitiles_host,
cls.gitiles_project == gitiles_project, cls.gitiles_ref == gitiles_ref,
cls.target_name == target_name,
cls.commit_position >= commit_position).order(
cls.commit_position).fetch(limit=limit)
@classmethod
def FindLatestIsolateByMaster(cls,
master_name,
builder_name,
gitiles_host,
gitiles_project,
gitiles_ref,
target_name,
limit=1):
"""Finds the latest isolate target with master_name by commit position."""
# pylint: disable=singleton-comparison
return cls.query(
cls.has_patch == False, cls.master_name == master_name,
cls.builder_name == builder_name, cls.gitiles_host == gitiles_host,
cls.gitiles_project == gitiles_project, cls.gitiles_ref == gitiles_ref,
cls.target_name == target_name).order(-cls.commit_position).fetch(
limit=limit)