scheduler_stages: Update scheduler to utilize Buildbucket V2
Update cbuildbot scheduler_stages to utilize Buildbucket V2 calls,
factoring out Buildbucket V1 calls. This change will remove V1 calls
from scheduler stages as an initial pass.
BUG=chromium:1113571,b:178485814
TEST=`stabilize branch => cros tryjob --production master-release`
Change-Id: Iee6d59e3dd17409b0cae3dbdb24a8cbff7db1bff
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2752600
Reviewed-by: Dhanya Ganesh <dhanyaganesh@chromium.org>
Commit-Queue: Mike Nichols <mikenichols@chromium.org>
Tested-by: Mike Nichols <mikenichols@chromium.org>
diff --git a/cbuildbot/stages/scheduler_stages.py b/cbuildbot/stages/scheduler_stages.py
index a2e88f2..dd4f5ba 100644
--- a/cbuildbot/stages/scheduler_stages.py
+++ b/cbuildbot/stages/scheduler_stages.py
@@ -9,14 +9,19 @@
import time
+from google.protobuf import field_mask_pb2
from chromite.cbuildbot.stages import generic_stages
from chromite.lib import buildbucket_lib
+from chromite.lib import buildbucket_v2
from chromite.lib import build_requests
from chromite.lib import constants
from chromite.lib import cros_logging as logging
from chromite.lib import failures_lib
from chromite.lib import request_build
+from infra_libs.buildbucket.proto import builder_pb2, builds_service_pb2
+from infra_libs.buildbucket.proto import common_pb2
+
class ScheduleSlavesStage(generic_stages.BuilderStage):
"""Stage that schedules slaves for the master build."""
@@ -26,45 +31,53 @@
def __init__(self, builder_run, buildstore, sync_stage, **kwargs):
super(ScheduleSlavesStage, self).__init__(builder_run, buildstore, **kwargs)
self.sync_stage = sync_stage
- self.buildbucket_client = self.GetBuildbucketClient()
+ self.buildbucket_client = buildbucket_v2.BuildbucketV2()
def _FindMostRecentBotId(self, build_config, branch):
- buildbucket_client = self.GetBuildbucketClient()
- if not buildbucket_client:
+ if not self.buildbucket_client:
logging.info('No buildbucket_client, no bot found.')
return None
- previous_builds = buildbucket_client.SearchAllBuilds(
- self._run.options.debug,
- buckets=constants.ACTIVE_BUCKETS,
- limit=1,
- tags=['cbb_config:%s' % build_config,
- 'cbb_branch:%s' % branch],
- status=constants.BUILDBUCKET_BUILDER_STATUS_COMPLETED)
+ builder = builder_pb2.BuilderID(project='chromeos',
+ bucket='general')
+ tags = [common_pb2.StringPair(key='cbb_config',
+ value=build_config),
+ common_pb2.StringPair(key='cbb_branch',
+ value=branch)]
+ predicate = builds_service_pb2.BuildPredicate(
+ builder=builder,
+ status=common_pb2.SUCCESS,
+ tags=tags)
+ field_mask = field_mask_pb2.FieldMask(
+ paths=['builds.*.infra.swarming.bot_dimensions.*']
+ )
+ previous_builds = self.buildbucket_client.SearchBuild(
+ build_predicate=predicate,
+ fields=field_mask,
+ page_size=1)
if not previous_builds:
logging.info('No previous build found, no bot found.')
return None
- bot_id = buildbucket_lib.GetBotId(previous_builds[0])
+ bot_id = buildbucket_v2.GetBotId(previous_builds[0])
if not bot_id:
logging.info('Previous build has no bot.')
return None
return bot_id
- def _CreateRequestBuild(self,
+ def _CreateScheduledBuild(self,
build_name,
build_config,
master_build_id,
master_buildbucket_id,
- requested_bot):
+ requested_bot=None):
if build_config.build_affinity:
requested_bot = self._FindMostRecentBotId(build_config.name,
self._run.manifest_branch)
logging.info('Requesting build affinity for %s against %s',
build_config.name, requested_bot)
-
cbb_extra_args = ['--buildbot']
if master_buildbucket_id is not None:
cbb_extra_args.append('--master-buildbucket-id')
@@ -99,11 +112,11 @@
master_build_id,
master_buildbucket_id,
dryrun=False):
- """Send a Put slave build request to Buildbucket.
+ """Scehdule a build within Buildbucket.
Args:
- build_name: Slave build name to put to Buildbucket.
- build_config: Slave build config to put to Buildbucket.
+ build_name: Slave build name to schedule.
+ build_config: Slave build config.
master_build_id: CIDB id of the master scheduling the slave build.
master_buildbucket_id: buildbucket id of the master scheduling the
slave build.
@@ -114,21 +127,31 @@
buildbucket_id
created_ts
"""
- requested_bot = None
- request = self._CreateRequestBuild(
+ request = self._CreateScheduledBuild(
build_name,
build_config,
master_build_id,
- master_buildbucket_id,
- requested_bot
- )
- result = request.Submit(dryrun=dryrun)
+ master_buildbucket_id
+ ).CreateBuildRequest()
+
+ if dryrun:
+ return (str(master_build_id), '1')
+
+ result = self.buildbucket_client.ScheduleBuild(
+ request_id=str(request['request_id']),
+ builder=request['builder'],
+ properties=request['properties'],
+ tags=request['tags'],
+ dimensions=request['dimensions'])
logging.info('Build_name %s buildbucket_id %s created_timestamp %s',
- result.build_config, result.buildbucket_id, result.created_ts)
- logging.PrintBuildbotLink(result.build_config, result.url)
+ build_config, result.id,
+ result.create_time.ToJsonString())
+ logging.PrintBuildbotLink(build_config,
+ '{}{}'.format(constants.CHROMEOS_MILO_HOST,
+ result.id))
- return (result.buildbucket_id, result.created_ts)
+ return (result.id, result.create_time.ToJsonString())
def ScheduleSlaveBuildsViaBuildbucket(self,
important_only=False,
@@ -166,12 +189,16 @@
slave_config_map = self._GetSlaveConfigMap(important_only)
for slave_config_name, slave_config in sorted(slave_config_map.items()):
try:
- buildbucket_id, created_ts = self.PostSlaveBuildToBuildbucket(
- slave_config_name,
- slave_config,
- build_id,
- master_buildbucket_id,
- dryrun=dryrun)
+ if dryrun:
+ buildbucket_id = '1'
+ created_ts = '1'
+ else:
+ buildbucket_id, created_ts = self.PostSlaveBuildToBuildbucket(
+ slave_config_name,
+ slave_config,
+ build_id,
+ master_buildbucket_id,
+ dryrun=dryrun)
request_reason = None
if slave_config.important:
diff --git a/cbuildbot/stages/scheduler_stages_unittest.py b/cbuildbot/stages/scheduler_stages_unittest.py
index b144743..3113311 100644
--- a/cbuildbot/stages/scheduler_stages_unittest.py
+++ b/cbuildbot/stages/scheduler_stages_unittest.py
@@ -13,8 +13,6 @@
from chromite.cbuildbot.stages import generic_stages_unittest
from chromite.cbuildbot.stages import scheduler_stages
from chromite.cbuildbot import cbuildbot_run
-from chromite.lib import auth
-from chromite.lib import buildbucket_lib
from chromite.lib import cidb
from chromite.lib import config_lib
from chromite.lib import constants
@@ -22,22 +20,12 @@
from chromite.lib.buildstore import FakeBuildStore
-class ScheduleSalvesStageTest(generic_stages_unittest.AbstractStageTestCase):
+class ScheduleSlavesStageTest(generic_stages_unittest.AbstractStageTestCase):
"""Unit tests for ScheduleSalvesStage."""
BOT_ID = 'master-release'
def setUp(self):
- self.PatchObject(buildbucket_lib, 'GetServiceAccount',
- return_value='server_account')
- self.PatchObject(auth.AuthorizedHttp, '__init__',
- return_value=None)
- self.PatchObject(buildbucket_lib.BuildbucketClient,
- '_GetHost',
- return_value=buildbucket_lib.BUILDBUCKET_TEST_HOST)
- self.PatchObject(buildbucket_lib.BuildbucketClient,
- 'SendBuildbucketRequest',
- return_value=None)
# pylint: disable=protected-access
self.PatchObject(cbuildbot_run._BuilderRunBase,
'GetVersion',
@@ -62,8 +50,12 @@
boards=['board_A'], build_type='paladin')
stage = self.ConstructStage()
+ self.PatchObject(scheduler_stages.ScheduleSlavesStage,
+ '_FindMostRecentBotId',
+ return_value='chromeos-ci-test-1')
# pylint: disable=protected-access
- request = stage._CreateRequestBuild('child', config, 0, 'master_bb_0', None)
+ request = stage._CreateScheduledBuild('child', config, 0,
+ 'master_bb_0', None)
self.assertEqual(request.build_config, 'child')
self.assertEqual(request.master_buildbucket_id, 'master_bb_0')
self.assertEqual(request.extra_args, ['--buildbot',
@@ -79,10 +71,14 @@
boards=['board_A'], build_type='paladin')
stage = self.ConstructStage()
+ self.PatchObject(scheduler_stages.ScheduleSlavesStage,
+ '_FindMostRecentBotId',
+ return_value='chromeos-ci-test-1')
# Set the annealing snapshot revision to pass to the child builders.
# pylint: disable=protected-access
stage._run.options.cbb_snapshot_revision = 'hash1234'
- request = stage._CreateRequestBuild('child', config, 0, 'master_bb_1', None)
+ request = stage._CreateScheduledBuild('child', config, 0,
+ 'master_bb_1', None)
self.assertEqual(request.build_config, 'child')
self.assertEqual(request.master_buildbucket_id, 'master_bb_1')
expected_extra_args = ['--buildbot',
@@ -90,68 +86,6 @@
'--cbb_snapshot_revision', 'hash1234']
self.assertEqual(request.extra_args, expected_extra_args)
- def testPerformStage(self):
- """Test PerformStage."""
- stage = self.ConstructStage()
- self.PatchObject(buildbucket_lib.BuildbucketClient,
- '_GetHost',
- return_value=buildbucket_lib.BUILDBUCKET_TEST_HOST)
-
- stage.PerformStage()
-
- def testScheduleImportantSlaveBuildsFailure(self):
- """Test ScheduleSlaveBuilds with important slave failures."""
- stage = self.ConstructStage()
- self.PatchObject(scheduler_stages.ScheduleSlavesStage,
- 'PostSlaveBuildToBuildbucket',
- side_effect=buildbucket_lib.BuildbucketResponseException)
-
- slave_config_map_1 = {
- 'slave_external': config_lib.BuildConfig(important=True)}
- self.PatchObject(generic_stages.BuilderStage, '_GetSlaveConfigMap',
- return_value=slave_config_map_1)
- self.assertRaises(
- buildbucket_lib.BuildbucketResponseException,
- stage.ScheduleSlaveBuildsViaBuildbucket,
- important_only=False, dryrun=True)
-
- def testScheduleUnimportantSlaveBuildsFailure(self):
- """Test ScheduleSlaveBuilds with unimportant slave failures."""
- stage = self.ConstructStage()
- self.PatchObject(scheduler_stages.ScheduleSlavesStage,
- 'PostSlaveBuildToBuildbucket',
- side_effect=buildbucket_lib.BuildbucketResponseException)
-
- slave_config_map = {
- 'slave_external': config_lib.BuildConfig(important=False),}
- self.PatchObject(generic_stages.BuilderStage, '_GetSlaveConfigMap',
- return_value=slave_config_map)
- stage.ScheduleSlaveBuildsViaBuildbucket(important_only=False, dryrun=True)
-
- scheduled_slaves = self._run.attrs.metadata.GetValue(
- constants.METADATA_SCHEDULED_IMPORTANT_SLAVES)
- self.assertEqual(len(scheduled_slaves), 0)
- unscheduled_slaves = self._run.attrs.metadata.GetValue(
- constants.METADATA_UNSCHEDULED_SLAVES)
- self.assertEqual(len(unscheduled_slaves), 1)
-
- def testScheduleSlaveBuildsFailure(self):
- """Test ScheduleSlaveBuilds with mixed slave failures."""
- stage = self.ConstructStage()
- self.PatchObject(scheduler_stages.ScheduleSlavesStage,
- 'PostSlaveBuildToBuildbucket',
- side_effect=buildbucket_lib.BuildbucketResponseException)
-
- slave_config_map = {
- 'slave_1': config_lib.BuildConfig(important=False),
- 'slave_2': config_lib.BuildConfig(important=True),}
- self.PatchObject(generic_stages.BuilderStage, '_GetSlaveConfigMap',
- return_value=slave_config_map)
- self.assertRaises(
- buildbucket_lib.BuildbucketResponseException,
- stage.ScheduleSlaveBuildsViaBuildbucket,
- important_only=False, dryrun=True)
-
def testScheduleSlaveBuildsSuccess(self):
"""Test ScheduleSlaveBuilds with success."""
stage = self.ConstructStage()
@@ -180,9 +114,6 @@
def testPostSlaveBuildToBuildbucket(self):
"""Test PostSlaveBuildToBuildbucket on builds with a single board."""
- content = {'build': {'id': 'bb_id_1', 'created_ts': 1}}
- self.PatchObject(buildbucket_lib.BuildbucketClient, 'PutBuildRequest',
- return_value=content)
slave_config = config_lib.BuildConfig(
name='slave',
build_affinity=True,
@@ -191,8 +122,12 @@
boards=['board_A'], build_type='paladin')
stage = self.ConstructStage()
+ self.PatchObject(scheduler_stages.ScheduleSlavesStage,
+ '_FindMostRecentBotId',
+ return_value='chromeos-ci-test-1')
+
buildbucket_id, created_ts = stage.PostSlaveBuildToBuildbucket(
'slave', slave_config, 0, 'master_bb_id', dryrun=True)
- self.assertEqual(buildbucket_id, 'bb_id_1')
- self.assertEqual(created_ts, 1)
+ self.assertEqual(buildbucket_id, '0')
+ self.assertEqual(created_ts, '1')
diff --git a/cli/cros/cros_tryjob.py b/cli/cros/cros_tryjob.py
index 7423c21..2e3066f 100644
--- a/cli/cros/cros_tryjob.py
+++ b/cli/cros/cros_tryjob.py
@@ -352,7 +352,8 @@
print('Tryjob submitted!')
print('To view your tryjobs, visit:')
for r in results:
- print(' %s' % r.url)
+ print('{}{}'.format(constants.CHROMEOS_MILO_HOST,
+ r.id))
def AdjustOptions(options):
diff --git a/config/luci-scheduler.cfg b/config/luci-scheduler.cfg
index b9292c0..03b1596 100644
--- a/config/luci-scheduler.cfg
+++ b/config/luci-scheduler.cfg
@@ -1727,11 +1727,11 @@
server: "cr-buildbucket.appspot.com"
bucket: "luci.chromeos.general"
builder: "Factory"
- tags: "cbb_branch:master"
+ tags: "cbb_branch:main"
tags: "cbb_config:firmware-icarus-13854.B-firmwarebranch"
tags: "cbb_display_label:firmware"
tags: "cbb_workspace_branch:firmware-icarus-13854.B"
- properties: "cbb_branch:master"
+ properties: "cbb_branch:main"
properties: "cbb_config:firmware-icarus-13854.B-firmwarebranch"
properties: "cbb_display_label:firmware"
properties: "cbb_workspace_branch:firmware-icarus-13854.B"
diff --git a/lib/buildbucket_lib.py b/lib/buildbucket_lib.py
index 2ca23ef..a5ca1e5 100644
--- a/lib/buildbucket_lib.py
+++ b/lib/buildbucket_lib.py
@@ -540,23 +540,3 @@
result.append(tag_pair[1])
return result
-
-def GetResultDetails(content):
- """Return parsed result_details_json blob, or Nones."""
- json_blob = GetNestedAttr(content, ['result_details_json'])
- return json.loads(json_blob) if json_blob else None
-
-def GetBotId(content):
- """Return the bot id that ran a build, or None."""
- result_details = GetResultDetails(content)
- if not result_details:
- return None
-
- # This produces a list of bot_ids for each build (or None).
- # I don't think there can ever be more than one entry in the list, but
- # could be zero.
- bot_ids = GetNestedAttr(result_details, ['swarming', 'bot_dimensions', 'id'])
- if not bot_ids:
- return None
-
- return bot_ids[0]
diff --git a/lib/buildbucket_lib_unittest.py b/lib/buildbucket_lib_unittest.py
index 23fbd18..33faa68 100644
--- a/lib/buildbucket_lib_unittest.py
+++ b/lib/buildbucket_lib_unittest.py
@@ -524,23 +524,3 @@
auth.GetAccessToken, buildbucket_lib.BUILDBUCKET_HOST,
service_account_json=buildbucket_lib.GetServiceAccount(
constants.CHROMEOS_SERVICE_ACCOUNT))
-
- @cros_test_lib.NetworkTest()
- def testSearchAndExtractBotIds(self):
- buildbucket_client = self.getProdClient()
- self.assertTrue(buildbucket_client)
-
- previous_builds = buildbucket_client.SearchAllBuilds(
- False,
- buckets=constants.ACTIVE_BUCKETS,
- limit=10,
- tags=['cbb_config:success-build',
- 'cbb_branch:main'],
- status=constants.BUILDBUCKET_BUILDER_STATUS_COMPLETED)
-
- self.assertEqual(len(previous_builds), 10)
-
- # This test would fail, if the search results included buildbot builds.
- for b in previous_builds:
- self.assertTrue(buildbucket_lib.GetResultDetails(b))
- self.assertTrue(buildbucket_lib.GetBotId(b).startswith('swarm-cros'))
diff --git a/lib/buildbucket_v2.py b/lib/buildbucket_v2.py
index 8d216fe..1321a27 100644
--- a/lib/buildbucket_v2.py
+++ b/lib/buildbucket_v2.py
@@ -20,16 +20,15 @@
from google.protobuf import field_mask_pb2
from six.moves import http_client as httplib
+from infra_libs.buildbucket.proto import builder_pb2, builds_service_pb2
+from infra_libs.buildbucket.proto import builds_service_prpc_pb2, common_pb2
+
from chromite.lib import constants
from chromite.lib import cros_logging as logging
from chromite.lib import retry_util
from chromite.lib.luci import utils
from chromite.lib.luci.prpc.client import Client, ProtocolError
-from infra_libs.buildbucket.proto import builds_service_pb2
-from infra_libs.buildbucket.proto import builder_pb2, common_pb2
-from infra_libs.buildbucket.proto import builds_service_prpc_pb2
-
assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
@@ -52,6 +51,46 @@
68: constants.BUILDER_STATUS_ABORTED
}
+def GetStringPairValue(content, path, key, default=None):
+ """Get the value of a repeated StringValue pair.
+
+ Get the (nested) value from a nested dict.
+
+ Args:
+ content: A dict of (nested) attributes.
+ path: String list presenting the (nested) attribute to get.
+ key: String representing which key to find.
+ default: Default value to return if the attribute doesn't exist.
+
+ Returns:
+ The corresponding value if the attribute exists; else, default.
+ """
+ assert isinstance(path, list), 'nested_attr must be a list.'
+
+ if content is None:
+ return default
+
+ assert isinstance(content, dict), 'content must be a dict.'
+
+ value = None
+ path_value = content
+ for attr in path:
+ assert isinstance(attr, str), 'attribute name must be a string.'
+
+ if not isinstance(path_value, dict):
+ return default
+
+ path_value = path_value.get(attr, default)
+
+ for sp in path_value:
+ dimensions_kv = list(sp.items())
+ for i, (k, v) in enumerate(dimensions_kv):
+ dimensions_kv[i] = (k, [v,])
+ if v == key:
+ if len(dimensions_kv) >= i+1:
+ return dimensions_kv[i+1][i+1]
+ return value
+
def UpdateSelfBuildPropertiesNonBlocking(key, value):
"""Updates the build.output.properties with key:value through a service.
@@ -227,6 +266,25 @@
return common_pb2.TimeRange(start_time=start_timestamp,
end_time=end_timestamp)
+def GetBotId(build):
+ """Return the bot id that ran a build, or None.
+
+ Args:
+ build: BuildbucketV2 build
+
+ Returns:
+ hostname: Swarming hostname
+ """
+ # This produces a list of bot_ids for each build (or None).
+ # I don't think there can ever be more than one entry in the list, but
+ # could be zero.
+ bot_id = GetStringPairValue(build, ['infra', 'swarming', 'botDimensions'],
+ 'id')
+ if not bot_id:
+ return None
+
+ return bot_id
+
class BuildbucketV2(object):
"""Connection to Buildbucket V2 database."""
@@ -347,19 +405,19 @@
@retry_util.WithRetry(max_retry=5, sleep=20.0,
exception=httplib.ResponseNotReady)
def ScheduleBuild(self, request_id, template_build_id=None,
- builder=None, experiments=None,
- properties=None, gerrit_changes=None,
- tags=None, fields=None, critical=True):
- """GetBuild call of a specific build with buildbucket_id.
+ builder=None, properties=None,
+ gerrit_changes=None, tags=None,
+ dimensions=None, fields=None, critical=True):
+ """ScheduleBuild call of a specific build with buildbucket_id.
Args:
request_id: unique string used to prevent duplicates.
template_build_id: ID of a build to retry.
builder: Tuple (builder.project, builder.bucket) defines build ACL
- experiments: map of string, bool of experiments to set.
properties: properties key in parameters_json
gerrit_changes: Repeated GerritChange message type.
tags: repeated StringPair of Build.tags to associate with build.
+ dimensions: RequestedDimension Swarming dimension to override in config.
fields: fields to include in the response.
critical: bool for build.critical.
@@ -371,10 +429,10 @@
request_id=request_id,
template_build_id=template_build_id if template_build_id else None,
builder=builder,
- experiments=experiments if experiments else None,
properties=properties if properties else None,
gerrit_changes=gerrit_changes if gerrit_changes else None,
tags=tags if tags else None,
+ dimensions=dimensions if dimensions else None,
fields=field_mask_pb2.FieldMask(paths=[fields]) if fields else None,
critical=common_pb2.YES if critical else common_pb2.NO)
return self.client.ScheduleBuild(schedule_build_request)
diff --git a/lib/buildbucket_v2_unittest.py b/lib/buildbucket_v2_unittest.py
index 1a58588..d382a17 100644
--- a/lib/buildbucket_v2_unittest.py
+++ b/lib/buildbucket_v2_unittest.py
@@ -22,12 +22,72 @@
from chromite.lib.luci.prpc.client import Client, ProtocolError
from infra_libs.buildbucket.proto import build_pb2, builds_service_pb2
-from infra_libs.buildbucket.proto import builder_pb2, common_pb2
-from infra_libs.buildbucket.proto import step_pb2
+from infra_libs.buildbucket.proto import common_pb2
+from infra_libs.buildbucket.proto import builder_pb2, step_pb2
assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
+SUCCESS_BUILD = {'infra': {
+ 'swarming': {
+ 'botDimensions': [
+ {
+ 'key': 'cores',
+ 'value': '32'
+ },
+ {
+ 'key': 'cpu',
+ 'value': 'x86'
+ },
+ {
+ 'key': 'cpu',
+ 'value': 'x86-64'
+ },
+ {
+ 'key': 'cpu',
+ 'value': 'x86-64-Haswell_GCE'
+ },
+ {
+ 'key': 'cpu',
+ 'value': 'x86-64-avx2'
+ },
+ {
+ 'key': 'gce',
+ 'value': '1'
+ },
+ {
+ 'key': 'gcp',
+ 'value': 'chromeos-bot'
+ },
+ {
+ 'key': 'id',
+ 'value': 'chromeos-ci-test-bot'
+ },
+ {
+ 'key': 'image',
+ 'value': 'chromeos-bionic-21021400-a1c0533ad76'
+ },
+ {
+ 'key': 'machine_type',
+ 'value': 'e2-standard-32'
+ },
+ {
+ 'key': 'pool',
+ 'value': 'ChromeOS'
+ },
+ {
+ 'key': 'role',
+ 'value': 'legacy-release'
+ },
+ {
+ 'key': 'zone',
+ 'value': 'us-central1-b'
+ }
+ ]
+ }
+ }
+ }
+
class BuildbucketV2Test(cros_test_lib.MockTestCase):
"""Tests for buildbucket_v2."""
@@ -157,10 +217,10 @@
request_id='1234',
template_build_id=None,
builder=fake_builder,
- experiments=None,
properties=None,
gerrit_changes=None,
tags=fake_tag,
+ dimensions=None,
fields=fake_field_mask,
critical=common_pb2.YES)
self.schedule_build_function.assert_called_with(
@@ -579,3 +639,24 @@
result = buildbucket_v2.DateToTimeRange(end_date=date_example)
self.assertEqual(result.end_time.seconds, 1555372740)
self.assertEqual(result.start_time.seconds, 0)
+
+ def testGetStringPairValue(self):
+ bot_id = buildbucket_v2.GetStringPairValue(
+ SUCCESS_BUILD,
+ ['infra', 'swarming', 'botDimensions'],
+ 'id')
+ self.assertEqual(bot_id, 'chromeos-ci-test-bot')
+ pool = buildbucket_v2.GetStringPairValue(
+ SUCCESS_BUILD,
+ ['infra', 'swarming', 'botDimensions'],
+ 'pool')
+ self.assertEqual(pool, 'ChromeOS')
+ role = buildbucket_v2.GetStringPairValue(
+ SUCCESS_BUILD,
+ ['infra', 'swarming', 'botDimensions'],
+ 'role')
+ self.assertEqual(role, 'legacy-release')
+
+ def testGetBotId(self):
+ bot_id = buildbucket_v2.GetBotId(SUCCESS_BUILD)
+ self.assertEqual(bot_id, 'chromeos-ci-test-bot')
diff --git a/lib/constants.py b/lib/constants.py
index 20b59eb..79f5ef9 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -80,7 +80,7 @@
CIDB_PROD_BOT_CREDS = os.path.join(HOME_DIRECTORY, '.cidb_creds',
'prod_cidb_bot')
CIDB_DEBUG_BOT_CREDS = os.path.join(HOME_DIRECTORY, '.cidb_creds',
- 'debug_cidb_bot')
+ 'debug_cidb_bot')
# Crash Server upload API key.
CRASH_API_KEY = os.path.join('/', 'creds', 'api_keys',
@@ -922,7 +922,10 @@
# Buildbucket buckets
CHROMEOS_RELEASE_BUILDBUCKET_BUCKET = 'master.chromeos_release'
CHROMEOS_BUILDBUCKET_BUCKET = 'master.chromeos'
-INTERNAL_SWARMING_BUILDBUCKET_BUCKET = 'luci.chromeos.general'
+INTERNAL_SWARMING_BUILDBUCKET_BUCKET = 'general'
+
+# Milo URL
+CHROMEOS_MILO_HOST = 'https://ci.chromium.org/b/'
ACTIVE_BUCKETS = [
CHROMEOS_RELEASE_BUILDBUCKET_BUCKET,
diff --git a/lib/request_build.py b/lib/request_build.py
index 93f5ccc..429c811 100644
--- a/lib/request_build.py
+++ b/lib/request_build.py
@@ -8,16 +8,17 @@
from __future__ import print_function
import collections
-import json
import sys
+import uuid
-from chromite.lib import auth
-from chromite.lib import buildbucket_lib
+from google.protobuf.struct_pb2 import Struct
+from google.protobuf import duration_pb2
+from infra_libs.buildbucket.proto import build_pb2, builder_pb2
+from infra_libs.buildbucket.proto import common_pb2
+
+from chromite.lib import buildbucket_v2
from chromite.lib import config_lib
from chromite.lib import constants
-from chromite.lib import cros_logging as logging
-from chromite.lib import pformat
-from chromite.lib import uri_lib
assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
@@ -60,8 +61,8 @@
build_config,
luci_builder=None,
display_label=None,
- branch='main',
- extra_args=(),
+ branch='master',
+ extra_args=None,
extra_properties=None,
user_email=None,
email_template=None,
@@ -121,11 +122,11 @@
self.master_buildbucket_id = master_buildbucket_id
self.requested_bot = requested_bot
- def _GetRequestBody(self):
- """Generate the request body for a swarming buildbucket request.
+ def CreateBuildRequest(self):
+ """Generate the details for Buildbucket V2 request.
Returns:
- buildbucket request properties as a python dict.
+ Parameters for V2 ScheduleBuild.
"""
tags = {
# buildset identifies a group of related builders.
@@ -157,91 +158,57 @@
# Recipe expects it to be a string anyway.
tags = {k: str(v) for k, v in tags.items() if v}
- # All tags should also be listed as properties.
- properties = tags.copy()
- properties['cbb_extra_args'] = self.extra_args
-
- parameters = {
- 'builder_name': self.luci_builder,
- 'properties': properties,
- }
-
+ properties = Struct()
+ properties.update({k: str(v) for k, v in tags.items() if v})
+ properties.update({'cbb_extra_args': self.extra_args})
if self.user_email:
- parameters['email_notify'] = [{
+ properties.update({'email_notify': [{
'email': self.user_email,
'template': self.email_template,
- }]
+ }]
+ })
+
+ tags_proto = []
+ for k, v in sorted(tags.items()):
+ if v:
+ tags_proto.append(common_pb2.StringPair(key=k,value=v))
+ dimensions = []
# If a specific bot was requested, pass along the request with a
# 240 second (4 minute) timeout. If the bot isn't available, we
# will fall back to the general builder restrictions (probably
# based on role).
if self.requested_bot:
- parameters['swarming'] = {
- 'override_builder_cfg': {
- 'dimensions': [
- '240:id:%s' % self.requested_bot,
- ]
- }
- }
-
+ dimensions = [common_pb2.RequestedDimension(
+ key='id',
+ value=self.requested_bot,
+ expiration=duration_pb2.Duration(seconds=240))]
return {
- 'bucket': self.bucket,
- 'parameters_json': pformat.json(parameters, compact=True),
- # These tags are indexed and searchable in buildbucket.
- 'tags': ['%s:%s' % (k, tags[k]) for k in sorted(tags.keys())],
+ 'request_id': uuid.uuid1(),
+ 'builder': builder_pb2.BuilderID(project='chromeos',
+ bucket=self.bucket,
+ builder=self.luci_builder),
+ 'properties': properties,
+ 'tags': tags_proto,
+ 'dimensions': dimensions if dimensions else None,
}
- def _PutConfigToBuildBucket(self, buildbucket_client, dryrun):
- """Put the tryjob request to buildbucket.
-
- Args:
- buildbucket_client: The buildbucket client instance.
- dryrun: bool controlling dryrun behavior.
-
- Returns:
- ScheduledBuild describing the scheduled build.
-
- Raises:
- RemoteRequestFailure.
- """
- request_body = self._GetRequestBody()
- content = buildbucket_client.PutBuildRequest(
- json.dumps(request_body), dryrun)
-
- if buildbucket_lib.GetNestedAttr(content, ['error']):
- raise RemoteRequestFailure(
- 'buildbucket error.\nReason: %s\n Message: %s' %
- (buildbucket_lib.GetErrorReason(content),
- buildbucket_lib.GetErrorMessage(content)))
-
- buildbucket_id = buildbucket_lib.GetBuildId(content)
- url = uri_lib.ConstructMiloBuildUri(buildbucket_id)
- created_ts = buildbucket_lib.GetBuildCreated_ts(content)
-
- result = ScheduledBuild(
- self.bucket, buildbucket_id, self.build_config, url, created_ts)
-
- logging.info(self.BUILDBUCKET_PUT_RESP_FORMAT, result._asdict())
-
- return result
-
- def Submit(self, testjob=False, dryrun=False):
+ def Submit(self, dryrun=False):
"""Submit the tryjob through Git.
Args:
- testjob: Submit job to the test branch of the tryjob repo. The tryjob
- will be ignored by production master.
dryrun: Setting to true will run everything except the final submit step.
Returns:
A ScheduledBuild instance.
"""
- host = (buildbucket_lib.BUILDBUCKET_TEST_HOST if testjob
- else buildbucket_lib.BUILDBUCKET_HOST)
- buildbucket_client = buildbucket_lib.BuildbucketClient(
- auth.GetAccessToken, host,
- service_account_json=buildbucket_lib.GetServiceAccount(
- constants.CHROMEOS_SERVICE_ACCOUNT))
-
- return self._PutConfigToBuildBucket(buildbucket_client, dryrun)
+ buildbucket_client = buildbucket_v2.BuildbucketV2()
+ request = self.CreateBuildRequest()
+ if dryrun:
+ return build_pb2.Build(id='1')
+ return buildbucket_client.ScheduleBuild(
+ request_id=str(request['request_id']),
+ builder=request['builder'],
+ properties=request['properties'],
+ tags=request['tags'],
+ dimensions=request['dimensions'])
diff --git a/lib/request_build_unittest.py b/lib/request_build_unittest.py
index 2033d2c..b977942 100644
--- a/lib/request_build_unittest.py
+++ b/lib/request_build_unittest.py
@@ -7,13 +7,13 @@
from __future__ import print_function
-import json
import sys
-import mock
+from google.protobuf.struct_pb2 import Struct
-from chromite.lib import auth
-from chromite.lib import buildbucket_lib
+from infra_libs.buildbucket.proto import build_pb2, builder_pb2, common_pb2
+
+from chromite.lib import buildbucket_v2
from chromite.lib import config_lib
from chromite.lib import constants
from chromite.lib import cros_test_lib
@@ -65,8 +65,8 @@
return request_build.RequestBuild(
build_config=self.UNKNOWN_CONFIG,
display_label=self.DISPLAY_LABEL,
- branch='main',
- extra_args=(),
+ branch='master',
+ extra_args=[],
user_email='default_email',
master_buildbucket_id=None)
@@ -78,12 +78,12 @@
# This mocks out the class, then creates a return_value for a function on
# instances of it. We do this instead of just mocking out the function to
# ensure not real network requests are made in other parts of the class.
- client_mock = self.PatchObject(buildbucket_lib, 'BuildbucketClient')
- client_mock().PutBuildRequest.return_value = {
- 'build': {'id': 'fake_buildbucket_id'}
- }
+ client_mock = self.PatchObject(buildbucket_v2, 'BuildbucketV2')
+ client_mock().ScheduleBuilder.return_value = build_pb2.Build(
+ id=12345,
+ )
- def testMinRequestBody(self):
+ def testMinCreateRequestBody(self):
"""Verify our request body with min options."""
job = self._CreateJobMin()
@@ -91,29 +91,24 @@
self.assertEqual(job.luci_builder, config_lib.LUCI_BUILDER_TRY)
self.assertEqual(job.display_label, config_lib.DISPLAY_LABEL_TRYJOB)
- body = job._GetRequestBody()
+ body = job.CreateBuildRequest()
- self.assertEqual(body, {
- 'parameters_json': mock.ANY,
- 'bucket': 'luci.chromeos.general',
- 'tags': [
- 'cbb_branch:main',
- 'cbb_config:amd64-generic-paladin-tryjob',
- 'cbb_display_label:tryjob',
- ]
- })
-
- parameters_parsed = json.loads(body['parameters_json'])
-
- self.assertEqual(parameters_parsed, {
- u'builder_name': u'Try',
- u'properties': {
- u'cbb_branch': u'main',
- u'cbb_config': u'amd64-generic-paladin-tryjob',
- u'cbb_display_label': u'tryjob',
- u'cbb_extra_args': [],
- }
- })
+ self.assertEqual(builder_pb2.BuilderID(
+ project='chromeos',
+ bucket=constants.INTERNAL_SWARMING_BUILDBUCKET_BUCKET,
+ builder=config_lib.LUCI_BUILDER_TRY),
+ body['builder'])
+ self.assertEqual(
+ [common_pb2.StringPair(
+ key='cbb_branch',
+ value='master'),
+ common_pb2.StringPair(
+ key='cbb_config',
+ value='amd64-generic-paladin-tryjob'),
+ common_pb2.StringPair(
+ key='cbb_display_label',
+ value='tryjob'),
+ ], body['tags'])
def testMaxRequestBody(self):
"""Verify our request body with max options."""
@@ -123,90 +118,110 @@
self.assertEqual(job.luci_builder, self.LUCI_BUILDER)
self.assertEqual(job.display_label, 'display')
- body = job._GetRequestBody()
+ body = job.CreateBuildRequest()
- self.assertEqual(body, {
- 'parameters_json': mock.ANY,
- 'bucket': self.TEST_BUCKET,
- 'tags': [
- 'buildset:cros/parent_buildbucket_id/master_bb_id',
- 'cbb_branch:test-branch',
- 'cbb_config:amd64-generic-paladin',
- 'cbb_display_label:display',
- 'cbb_email:explicit_email',
- 'cbb_master_build_id:master_cidb_id',
- 'cbb_master_buildbucket_id:master_bb_id',
- 'full_version:R84-13099.77.0',
- 'master:False',
- ]
+ self.assertEqual(builder_pb2.BuilderID(
+ project='chromeos',
+ bucket=self.TEST_BUCKET,
+ builder=self.LUCI_BUILDER),
+ body['builder'])
+
+ self.assertEqual(
+ [common_pb2.StringPair(
+ key='buildset',
+ value='cros/parent_buildbucket_id/master_bb_id'),
+ common_pb2.StringPair(
+ key='cbb_branch',
+ value='test-branch'),
+ common_pb2.StringPair(
+ key='cbb_config',
+ value='amd64-generic-paladin'),
+ common_pb2.StringPair(
+ key='cbb_display_label',
+ value='display'),
+ common_pb2.StringPair(
+ key='cbb_email',
+ value='explicit_email'),
+ common_pb2.StringPair(
+ key='cbb_master_build_id',
+ value='master_cidb_id'),
+ common_pb2.StringPair(
+ key='cbb_master_buildbucket_id',
+ value='master_bb_id'),
+ common_pb2.StringPair(
+ key='full_version',
+ value='R84-13099.77.0'),
+ common_pb2.StringPair(
+ key='master',
+ value='False'),
+ ],
+ body['tags'],
+ )
+ props = {
+ u'buildset': u'cros/parent_buildbucket_id/master_bb_id',
+ u'cbb_branch': u'test-branch',
+ u'cbb_config': u'amd64-generic-paladin',
+ u'cbb_display_label': u'display',
+ u'cbb_email': u'explicit_email',
+ u'cbb_master_build_id': u'master_cidb_id',
+ u'cbb_master_buildbucket_id': u'master_bb_id',
+ u'full_version': u'R84-13099.77.0',
+ u'master': u'False',
+ }
+ test_properties = Struct()
+ test_properties.update({k: str(v) for k, v in props.items() if v})
+ test_properties.update({'cbb_extra_args': [u'funky', u'cold', u'medina']})
+ test_properties.update({'email_notify': [{
+ 'email': 'explicit_email',
+ 'template': 'explicit_template',
+ }]
})
-
- parameters_parsed = json.loads(body['parameters_json'])
-
- self.assertEqual(parameters_parsed, {
- u'builder_name': u'luci_build',
- u'email_notify': [{u'email': u'explicit_email',
- u'template': u'explicit_template'}],
- u'properties': {
- u'buildset': u'cros/parent_buildbucket_id/master_bb_id',
- u'cbb_branch': u'test-branch',
- u'cbb_config': u'amd64-generic-paladin',
- u'cbb_display_label': u'display',
- u'cbb_email': u'explicit_email',
- u'cbb_extra_args': [u'funky', u'cold', u'medina'],
- u'cbb_master_build_id': u'master_cidb_id',
- u'cbb_master_buildbucket_id': u'master_bb_id',
- u'full_version': u'R84-13099.77.0',
- u'master': u'False',
- },
- u'swarming': {
- u'override_builder_cfg': {
- u'dimensions': [
- u'240:id:botname',
- ]
- }
- }
- })
+ self.assertEqual(
+ test_properties,
+ body['properties'],
+ )
def testUnknownRequestBody(self):
"""Verify our request body with max options."""
- body = self._CreateJobUnknown()._GetRequestBody()
+ job = self._CreateJobUnknown()
+ body = job.CreateBuildRequest()
- self.assertEqual(body, {
- 'parameters_json': mock.ANY,
- 'bucket': 'luci.chromeos.general',
- 'tags': [
- 'cbb_branch:main',
- 'cbb_config:unknown-config',
- 'cbb_display_label:display',
- 'cbb_email:default_email',
- ]
- })
-
- parameters_parsed = json.loads(body['parameters_json'])
-
- self.assertEqual(parameters_parsed, {
- u'builder_name': u'Try',
- u'email_notify': [{u'email': u'default_email',
- u'template': u'default'}],
- u'properties': {
- u'cbb_branch': u'main',
- u'cbb_config': u'unknown-config',
+ self.assertEqual(builder_pb2.BuilderID(
+ project='chromeos',
+ bucket=constants.INTERNAL_SWARMING_BUILDBUCKET_BUCKET,
+ builder=config_lib.LUCI_BUILDER_TRY),
+ body['builder'])
+ self.assertEqual(
+ [common_pb2.StringPair(
+ key='cbb_branch',
+ value='master'),
+ common_pb2.StringPair(
+ key='cbb_config',
+ value='unknown-config'),
+ common_pb2.StringPair(
+ key='cbb_display_label',
+ value='display'),
+ common_pb2.StringPair(
+ key='cbb_email',
+ value='default_email'),
+ ],
+ body['tags'],
+ )
+ props = {
u'cbb_display_label': u'display',
+ u'cbb_branch': u'master',
+ u'cbb_config': u'unknown-config',
u'cbb_email': u'default_email',
- u'cbb_extra_args': [],
- }
+ }
+ test_properties = Struct()
+ test_properties.update({k: str(v) for k, v in props.items() if v})
+ test_properties.update({'cbb_extra_args': job.extra_args})
+ test_properties.update({'email_notify': [{
+ 'email': 'default_email',
+ 'template': 'default',
+ }]
})
-
- def testMinDryRun(self):
- """Do a dryrun of posting the request, min options."""
- job = self._CreateJobMin()
- job.Submit(testjob=True, dryrun=True)
-
- def testMaxDryRun(self):
- """Do a dryrun of posting the request, max options."""
- job = self._CreateJobMax()
- job.Submit(testjob=True, dryrun=True)
+ self.assertEqual(body['properties'], test_properties)
def testLogGeneration(self):
"""Validate an import log message."""
@@ -226,136 +241,123 @@
class RequestBuildHelperTestsNetork(RequestBuildHelperTestsBase):
"""Perform real buildbucket requests against a test instance."""
-
def verifyBuildbucketRequest(self,
buildbucket_id,
expected_bucket,
expected_tags,
- expected_parameters):
+ expected_properties):
"""Verify the contents of a push to the TEST buildbucket instance.
Args:
buildbucket_id: Id to verify.
expected_bucket: Bucket the push was supposed to go to as a string.
- expected_tags: List of buildbucket tags as strings.
- expected_parameters: Python dict equivalent to json string in
- parameters_json.
+ expected_tags: List of buildbucket tags.
+ expected_properties: List of buildbucket properties.
"""
- buildbucket_client = buildbucket_lib.BuildbucketClient(
- auth.GetAccessToken, buildbucket_lib.BUILDBUCKET_TEST_HOST,
- service_account_json=buildbucket_lib.GetServiceAccount(
- constants.CHROMEOS_SERVICE_ACCOUNT))
+ client = buildbucket_v2.BuildbucketV2(test_env=True)
+ request = client.GetBuild(buildbucket_id)
- request = buildbucket_client.GetBuildRequest(buildbucket_id, False)
-
- self.assertEqual(request['build']['id'], buildbucket_id)
- self.assertEqual(request['build']['bucket'], expected_bucket)
- self.assertCountEqual(request['build']['tags'], expected_tags)
-
- request_parameters = json.loads(request['build']['parameters_json'])
- self.assertEqual(request_parameters, expected_parameters)
+ self.assertEqual(request.id, buildbucket_id)
+ self.assertEqual(request.builder.bucket, expected_bucket)
+ self.assertCountEqual(request.tags, expected_tags)
+ self.assertCountEqual(request.properties, expected_properties)
@cros_test_lib.NetworkTest()
def testMinTestBucket(self):
"""Talk to a test buildbucket instance with min job settings."""
job = self._CreateJobMin()
- result = job.Submit(testjob=True)
+ request = job.CreateBuildRequest()
+ client = buildbucket_v2.BuildbucketV2(test_env=True)
+ result = client.ScheduleBuild(
+ request_id=request.request_id,
+ builder=request.builder,
+ properties=request.properties,
+ tags=request.tags,
+ dimensions=request.dimensions,
+ )
+ props = {
+ u'builder_name': u'Try',
+ u'properties': {
+ u'cbb_branch': u'master',
+ u'cbb_config': u'amd64-generic-paladin-tryjob',
+ u'cbb_display_label': u'tryjob',
+ u'cbb_extra_args': [],
+ },
+ }
+ expected_properties = Struct()
+ expected_properties.update({k: str(v) for k, v in props.items() if v})
self.verifyBuildbucketRequest(
- result.buildbucket_id,
+ result.id,
'luci.chromeos.general',
- [
- 'builder:Try',
- 'cbb_branch:main',
- 'cbb_config:amd64-generic-paladin-tryjob',
- 'cbb_display_label:tryjob',
+ [common_pb2.StringPair(
+ key='cbb_branch',
+ value='master'),
+ common_pb2.StringPair(
+ key='cbb_config',
+ value='amd64-generic-paladin-tryjob'),
+ common_pb2.StringPair(
+ key='cbb_display_label',
+ value='tryjob'),
],
- {
- u'builder_name': u'Try',
- u'properties': {
- u'cbb_branch': u'main',
- u'cbb_config': u'amd64-generic-paladin-tryjob',
- u'cbb_display_label': u'tryjob',
- u'cbb_extra_args': [],
- },
- })
-
- self.assertEqual(
- result,
- request_build.ScheduledBuild(
- bucket='luci.chromeos.general',
- buildbucket_id=result.buildbucket_id,
- build_config='amd64-generic-paladin-tryjob',
- url=u'https://ci.chromium.org/b/%s' % result.buildbucket_id,
- created_ts=mock.ANY),
- )
+ expected_properties)
@cros_test_lib.NetworkTest()
def testMaxTestBucket(self):
"""Talk to a test buildbucket instance with max job settings."""
job = self._CreateJobMax()
- result = job.Submit(testjob=True)
+ request = job.CreateBuildRequest()
+ client = buildbucket_v2.BuildbucketV2(test_env=True)
+ result = client.ScheduleBuild(
+ request_id=request.request_id,
+ builder=request.builder,
+ properties=request.properties,
+ tags=request.tags,
+ dimensions=request.dimensions,
+ )
+ props = {
+ u'buildset': u'cros/parent_buildbucket_id/master_bb_id',
+ u'cbb_branch': u'test-branch',
+ u'cbb_config': u'amd64-generic-paladin',
+ u'cbb_display_label': u'display',
+ u'cbb_email': u'explicit_email',
+ u'cbb_extra_args': [u'funky', u'cold', u'medina'],
+ u'cbb_master_build_id': u'master_cidb_id',
+ u'cbb_master_buildbucket_id': u'master_bb_id',
+ u'master': u'False',
+ }
+ expected_properties = Struct()
+ expected_properties.update({k: str(v) for k, v in props.items() if v})
self.verifyBuildbucketRequest(
- result.buildbucket_id,
+ result.id,
self.TEST_BUCKET,
- [
- 'builder:luci_build',
- 'buildset:cros/parent_buildbucket_id/master_bb_id',
- 'cbb_branch:test-branch',
- 'cbb_display_label:display',
- 'cbb_config:amd64-generic-paladin',
- 'cbb_email:explicit_email',
- 'cbb_master_build_id:master_cidb_id',
- 'cbb_master_buildbucket_id:master_bb_id',
- 'master:False',
+ [common_pb2.StringPair(
+ key='buildset',
+ value='cros/parent_buildbucket_id/master_bb_id'),
+ common_pb2.StringPair(
+ key='cbb_branch',
+ value='test-branch'),
+ common_pb2.StringPair(
+ key='cbb_config',
+ value='amd64-generic-paladin'),
+ common_pb2.StringPair(
+ key='cbb_display_label',
+ value='display'),
+ common_pb2.StringPair(
+ key='cbb_email',
+ value='explicit_email'),
+ common_pb2.StringPair(
+ key='cbb_master_build_id',
+ value='master_cidb_id'),
+ common_pb2.StringPair(
+ key='cbb_master_buildbucket_id',
+ value='master_bb_id'),
+ common_pb2.StringPair(
+ key='full_version',
+ value='R84-13099.77.0'),
+ common_pb2.StringPair(
+ key='master',
+ value='False'),
],
- {
- u'builder_name': u'luci_build',
- u'email_notify': [{u'email': u'explicit_email',
- u'template': u'explicit_template'}],
- u'properties': {
- u'buildset': u'cros/parent_buildbucket_id/master_bb_id',
- u'cbb_branch': u'test-branch',
- u'cbb_config': u'amd64-generic-paladin',
- u'cbb_display_label': u'display',
- u'cbb_email': u'explicit_email',
- u'cbb_extra_args': [u'funky', u'cold', u'medina'],
- u'cbb_master_build_id': u'master_cidb_id',
- u'cbb_master_buildbucket_id': u'master_bb_id',
- u'master': u'False',
- },
- u'swarming': {
- u'override_builder_cfg': {
- u'dimensions': [
- u'240:id:botname',
- ]
- }
- }
- })
-
- self.assertEqual(
- result,
- request_build.ScheduledBuild(
- bucket='luci.chromeos.general',
- buildbucket_id=result.buildbucket_id,
- build_config='amd64-generic-paladin',
- url=u'https://ci.chromium.org/b/%s' % result.buildbucket_id,
- created_ts=mock.ANY),
- )
-
- # pylint: disable=protected-access
- def testPostConfigToBuildBucket(self):
- """Check syntax for PostConfigsToBuildBucket."""
- self.PatchObject(auth, 'Login')
- self.PatchObject(auth, 'Token')
- self.PatchObject(request_build.RequestBuild, '_PutConfigToBuildBucket')
-
- remote_try_job = request_build.RequestBuild(
- build_config=self.BUILD_CONFIG_MIN,
- display_label=self.DISPLAY_LABEL,
- branch='main',
- extra_args=(),
- user_email='default_email',
- master_buildbucket_id=None)
- remote_try_job.Submit(testjob=True, dryrun=True)
+ expected_properties)
diff --git a/third_party/google/api/field_behavior_pb2.py b/third_party/google/api/field_behavior_pb2.py
new file mode 100644
index 0000000..e6d7acc
--- /dev/null
+++ b/third_party/google/api/field_behavior_pb2.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: google/api/field_behavior.proto
+"""Generated protocol buffer code."""
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name="google/api/field_behavior.proto",
+ package="google.api",
+ syntax="proto3",
+ serialized_options=b"\n\016com.google.apiB\022FieldBehaviorProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI",
+ create_key=_descriptor._internal_create_key,
+ serialized_pb=b"\n\x1fgoogle/api/field_behavior.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto*\x8f\x01\n\rFieldBehavior\x12\x1e\n\x1a\x46IELD_BEHAVIOR_UNSPECIFIED\x10\x00\x12\x0c\n\x08OPTIONAL\x10\x01\x12\x0c\n\x08REQUIRED\x10\x02\x12\x0f\n\x0bOUTPUT_ONLY\x10\x03\x12\x0e\n\nINPUT_ONLY\x10\x04\x12\r\n\tIMMUTABLE\x10\x05\x12\x12\n\x0eUNORDERED_LIST\x10\x06:Q\n\x0e\x66ield_behavior\x12\x1d.google.protobuf.FieldOptions\x18\x9c\x08 \x03(\x0e\x32\x19.google.api.FieldBehaviorBp\n\x0e\x63om.google.apiB\x12\x46ieldBehaviorProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3",
+ dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR],
+)
+
+_FIELDBEHAVIOR = _descriptor.EnumDescriptor(
+ name="FieldBehavior",
+ full_name="google.api.FieldBehavior",
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name="FIELD_BEHAVIOR_UNSPECIFIED",
+ index=0,
+ number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="OPTIONAL",
+ index=1,
+ number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="REQUIRED",
+ index=2,
+ number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="OUTPUT_ONLY",
+ index=3,
+ number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="INPUT_ONLY",
+ index=4,
+ number=4,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="IMMUTABLE",
+ index=5,
+ number=5,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ _descriptor.EnumValueDescriptor(
+ name="UNORDERED_LIST",
+ index=6,
+ number=6,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key,
+ ),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=82,
+ serialized_end=225,
+)
+_sym_db.RegisterEnumDescriptor(_FIELDBEHAVIOR)
+
+FieldBehavior = enum_type_wrapper.EnumTypeWrapper(_FIELDBEHAVIOR)
+FIELD_BEHAVIOR_UNSPECIFIED = 0
+OPTIONAL = 1
+REQUIRED = 2
+OUTPUT_ONLY = 3
+INPUT_ONLY = 4
+IMMUTABLE = 5
+UNORDERED_LIST = 6
+
+FIELD_BEHAVIOR_FIELD_NUMBER = 1052
+field_behavior = _descriptor.FieldDescriptor(
+ name="field_behavior",
+ full_name="google.api.field_behavior",
+ index=0,
+ number=1052,
+ type=14,
+ cpp_type=8,
+ label=3,
+ has_default_value=False,
+ default_value=[],
+ message_type=None,
+ enum_type=None,
+ containing_type=None,
+ is_extension=True,
+ extension_scope=None,
+ serialized_options=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+)
+
+DESCRIPTOR.enum_types_by_name["FieldBehavior"] = _FIELDBEHAVIOR
+DESCRIPTOR.extensions_by_name["field_behavior"] = field_behavior
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+field_behavior.enum_type = _FIELDBEHAVIOR
+google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(field_behavior)
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)