blob: ce052f61f6238d6838cf46130b7151fdcff10e3b [file] [log] [blame]
# Copyright 2021 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.
DRONE_TIMEOUT_SECS = 3600 * 3 # 3 hours.
import attr
from google.protobuf import json_format
from recipe_engine import recipe_api
from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
# Builder names use full platform name instead of short names. We need to
# map short names to full platform names to be able to identify the drone
# used to run the subshards.
PLATFORM_TO_NAME = {'win': 'Windows', 'linux': 'Linux', 'mac': 'Mac'}
@attr.s
class SubbuildResult(object):
"""Subbuild result metadata."""
builder = attr.ib(type=str)
build_id = attr.ib(type=str)
url = attr.ib(type=str, default=None)
build_proto = attr.ib(type=build_pb2.Build, default=None)
class ShardUtilApi(recipe_api.RecipeApi):
"""Utilities to shard tasks."""
def schedule(self, builds):
"""Schedules one subbuild per build."""
# Dependencies get here as a frozen dict we need to force them back
# to list of dicts.
results = {}
for build in builds:
task_name = build.get('name')
drone_properties = self.m.properties.thaw()
del drone_properties['builds']
drone_properties['build'] = build
# Copy parent bot dimensions.
drone_dimensions = build.get('drone_dimensions', [])
task_dimensions = {}
task_dimensions = []
for d in drone_dimensions:
k, v = d.split('=')
task_dimensions.append(common_pb2.RequestedDimension(key=k, value=v))
platform_name = PLATFORM_TO_NAME.get(self.m.platform.name)
# Override recipe.
drone_properties['recipe'] = 'engine_v2/builder'
if self.m.led.launched_by_led:
# If coming from led Launch sub-build using led.
builder_name='%s Engine Drone' % platform_name
parent = self.m.buildbucket.build.builder
led_data = self.m.led(
"get-builder",
"luci.%s.%s:%s" % (parent.project, parent.bucket, builder_name),
)
edit_args = []
for k, v in drone_properties.items():
edit_args.extend(["-p", "%s=%s" % (k, self.m.json.dumps(v))])
# led reduces the priority of tasks by 10 from their values in
# buildbucket which we do not want.
# TODO(crbug.com/1138533) Add an option to led to handle this.
led_data.result.buildbucket.bbagent_args.build.infra.swarming.priority -= 10
led_data = led_data.then("edit", *edit_args)
led_data = led_data.then("edit", "-name", task_name)
led_data = led_data.then("edit", "-r", 'engine_v2/builder')
led_data = self.m.led.inject_input_recipes(led_data)
launch_res = led_data.then("launch", "-modernize")
task_id = launch_res.launch_result.task_id
build_url = "https://ci.chromium.org/swarming/task/%s?server=%s" % (
task_id,
launch_res.launch_result.swarming_hostname,
)
results[task_name] = SubbuildResult(
builder=task_name, build_id=task_id, url=build_url
)
return results
def collect(self, task_ids, presentation):
"""Waits for a list of builds to complete.
Args:
task_ids((list(str|TaskRequestMetadata)): The tasks metadata used to
wait for completion.
presentation(StepPresentation): Used to add logs and logs to UI.
Returns: A list of SubBuildResult, one per task.
"""
swarming_results = self.m.swarming.collect(
"collect", task_ids, output_dir=self.m.path["cleanup"]
)
builds = {}
for result in swarming_results:
task_id = result.id
# Led launch ensures this file is present in the task root dir.
build_proto_path = result.output_dir.join("build.proto.json")
build_proto_json = self.m.file.read_text(
"read build.proto.json", build_proto_path
)
build_proto = build_pb2.Build()
log_name = '%s-build.proto.json' % result.id
presentation.logs[log_name] = build_proto_json.splitlines()
json_format.Parse(build_proto_json, build_proto)
builds[task_id] = SubbuildResult(
builder=build_proto.builder.builder,
build_id=task_id,
build_proto=build_proto,
)
return builds