diff options
author | 2017-05-22 08:35:36 -0400 | |
---|---|---|
committer | 2017-05-22 13:08:25 +0000 | |
commit | f94514b0ff8eccb2eaef8c77bee8c5f462b83b90 (patch) | |
tree | 208fa3043a2858cfc052c6af8a26a04e68868626 /infra/bots/recipe_modules/swarming | |
parent | 66f6b1fb48c82505397e8b787f7abc780e8f6029 (diff) |
[recipes] Copy file, isolate, swarming, swarming_client from build.git
Rename swarming -> skia_swarming.
Some required heavy modification to remove other dependencies on modules
in build.git.
Expected changes:
- RECIPE_MODULE[build::<module>] -> RECIPE_MODULE[skia::<module>]
- No more runit; directly run through Python.
Bug: skia:6628
Change-Id: I1b1370ed387966222ce10731771dbde9020cf542
Reviewed-on: https://skia-review.googlesource.com/17448
Commit-Queue: Eric Boren <borenet@google.com>
Reviewed-by: Ravi Mistry <rmistry@google.com>
Diffstat (limited to 'infra/bots/recipe_modules/swarming')
19 files changed, 4157 insertions, 679 deletions
diff --git a/infra/bots/recipe_modules/swarming/OWNERS b/infra/bots/recipe_modules/swarming/OWNERS new file mode 100644 index 0000000000..c14640d2a4 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/OWNERS @@ -0,0 +1,3 @@ +maruel@chromium.org +tansell@chromium.org +vadimsh@chromium.org diff --git a/infra/bots/recipe_modules/swarming/__init__.py b/infra/bots/recipe_modules/swarming/__init__.py index fbd0c006cf..98b20ffd81 100644 --- a/infra/bots/recipe_modules/swarming/__init__.py +++ b/infra/bots/recipe_modules/swarming/__init__.py @@ -1,19 +1,33 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2014 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. + +# TODO(borenet): This module was copied from build.git and heavily modified to +# remove dependencies on other modules in build.git. It belongs in a different +# repo. Remove this once it has been moved. + + DEPS = [ - 'build/file', - 'build/isolate', - 'build/swarming', - 'build/swarming_client', - 'depot_tools/depot_tools', + 'isolate', 'recipe_engine/context', 'recipe_engine/json', 'recipe_engine/path', + 'recipe_engine/platform', 'recipe_engine/properties', 'recipe_engine/python', 'recipe_engine/raw_io', 'recipe_engine/step', - 'run', + 'swarming_client', ] + +from recipe_engine.recipe_api import Property + +PROPERTIES = { + 'show_shards_in_collect_step': Property(default=False, kind=bool), + 'show_isolated_out_in_collect_step': Property(default=True, kind=bool), +} + + +# TODO(phajdan.jr): provide coverage (http://crbug.com/693058). +DISABLE_STRICT_COVERAGE = True diff --git a/infra/bots/recipe_modules/swarming/api.py b/infra/bots/recipe_modules/swarming/api.py index 35dddcf507..47e2c84158 100644 --- a/infra/bots/recipe_modules/swarming/api.py +++ b/infra/bots/recipe_modules/swarming/api.py @@ -1,220 +1,905 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2014 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. +import datetime +import functools +import hashlib +import logging +import os.path +from recipe_engine import config_types from recipe_engine import recipe_api -import shlex +from recipe_engine import util as recipe_util +import state -DEFAULT_TASK_EXPIRATION = 20*60*60 -DEFAULT_TASK_TIMEOUT = 4*60*60 -DEFAULT_IO_TIMEOUT = 40*60 -MILO_LOG_LINK = 'https://luci-milo.appspot.com/swarming/task/%s' +# TODO(borenet): This module was copied from build.git and heavily modified to +# remove dependencies on other modules in build.git. It belongs in a different +# repo. Remove this once it has been moved. -class SkiaSwarmingApi(recipe_api.RecipeApi): - """Provides steps to run Skia tasks on swarming bots.""" +# Minimally supported version of swarming.py script (reported by --version). +MINIMAL_SWARMING_VERSION = (0, 8, 6) + + +def text_for_task(task): + lines = [] + + if task.dimensions.get('id'): # pragma: no cover + lines.append('Bot id: %r' % task.dimensions['id']) + if task.dimensions.get('os'): + lines.append('Run on OS: %r' % task.dimensions['os']) + + return '<br/>'.join(lines) + + +def parse_time(value): + """Converts serialized time from the API to datetime.datetime.""" + # When microseconds are 0, the '.123456' suffix is elided. This means the + # serialized format is not consistent, which confuses the hell out of python. + # TODO(maruel): Remove third format once we enforce version >=0.8.2. + for fmt in ('%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S'): + try: + return datetime.datetime.strptime(value, fmt) + except ValueError: # pragma: no cover + pass + raise ValueError('Failed to parse %s' % value) # pragma: no cover + + +class ReadOnlyDict(dict): + def __setitem__(self, key, value): + raise TypeError('ReadOnlyDict is immutable') + + +class SwarmingApi(recipe_api.RecipeApi): + """Recipe module to use swarming.py tool to run tasks on Swarming. + + General usage: + 1. Tweak default task parameters applied to all swarming tasks (such as + default_dimensions and default_priority). + 2. Isolate some test using 'isolate' recipe module. Get isolated hash as + a result of that process. + 3. Create a task configuration using 'task(...)' method, providing + isolated hash obtained previously. + 4. Tweak the task parameters. This step is optional. + 5. Launch the task on swarming by calling 'trigger_task(...)'. + 6. Continue doing useful work locally while the task is running concurrently + on swarming. + 7. Wait for task to finish and collect its result (exit code, logs) + by calling 'collect_task(...)'. + + See also example.py for concrete code. + """ + + State = state.State + + ############################################################################# + # The below are helper functions to help transition between the old and new # + # swarming result formats. TODO(martiniss): remove these # + ############################################################################# + + def _is_expired(self, shard): + # FIXME: We really should only have one format for enums. We want to move to + # strings, currently have numbers. + return ( + shard.get('state') == self.State.EXPIRED or + shard.get('state') == 'EXPIRED') + + def _is_timed_out(self, shard): + # FIXME: We really should only have one format for enums. We want to move to + # strings, currently have numbers. + return ( + shard.get('state') == self.State.TIMED_OUT or + shard.get('state') == 'TIMED_OUT') + + def _get_exit_code(self, shard): + if shard.get('exit_code'): + return shard.get('exit_code') # pragma: no cover + lst = shard.get('exit_codes', []) + return str(lst[0]) if lst else None + + def __init__(self, **kwargs): + super(SwarmingApi, self).__init__(**kwargs) + # All tests default to a x86-64 bot running with no GPU. This simplifies + # management so that new tests are not executed on exotic bots by accidents + # even if misconfigured. + self._default_dimensions = { + 'cpu': 'x86-64', + 'gpu': 'none', + } + # Expirations are set to mildly good values and will be tightened soon. + self._default_expiration = 60*60 + self._default_env = {} + self._default_hard_timeout = 60*60 + self._default_idempotent = False + self._default_io_timeout = 20*60 + # The default priority is extremely low and should be increased dependending + # on the type of task. + self._default_priority = 200 + self._default_tags = set() + self._default_user = None + self._pending_tasks = set() + self._show_isolated_out_in_collect_step = True + self._show_shards_in_collect_step = False + self._swarming_server = 'https://chromium-swarm.appspot.com' + self._verbose = False + + @recipe_util.returns_placeholder + def summary(self): + return self.m.json.output() + + @property + def swarming_server(self): + """URL of Swarming server to use, default is a production one.""" + return self._swarming_server + + @swarming_server.setter + def swarming_server(self, value): + """Changes URL of Swarming server to use.""" + self._swarming_server = value @property - def swarming_temp_dir(self): - """Path where artifacts like isolate file and json output will be stored.""" - return self.m.path['start_dir'].join('swarming_temp_dir') + def verbose(self): + """True to run swarming scripts with verbose output.""" + return self._verbose + + @verbose.setter + def verbose(self, value): + """Enables or disables verbose output in swarming scripts.""" + assert isinstance(value, bool), value + self._verbose = value @property - def tasks_output_dir(self): - """Directory where the outputs of the swarming tasks will be stored.""" - return self.swarming_temp_dir.join('outputs') - - def isolated_file_path(self, task_name): - """Get the path to the given task's .isolated file.""" - return self.swarming_temp_dir.join('skia-task-%s.isolated' % task_name) - - def setup(self, luci_go_dir, swarming_rev=None): - """Performs setup steps for swarming.""" - self.m.swarming_client.checkout(revision=swarming_rev) - self.m.swarming.check_client_version(step_test_data=(0, 8, 6)) - self.setup_go_isolate(luci_go_dir) - self.m.swarming.add_default_tag('allow_milo:1') - - # TODO(rmistry): Remove once the Go binaries are moved to recipes or buildbot. - def setup_go_isolate(self, luci_go_dir): - """Generates and puts in place the isolate Go binary.""" - depot_tools_path = self.m.depot_tools.package_repo_resource() - env = {'PATH': self.m.path.pathsep.join([ - str(depot_tools_path), '%(PATH)s'])} - with self.m.context(env=env): - self.m.step('download luci-go linux', - ['download_from_google_storage', '--no_resume', - '--platform=linux*', '--no_auth', - '--bucket', 'chromium-luci', - '-d', luci_go_dir.join('linux64')]) - self.m.step('download luci-go mac', - ['download_from_google_storage', '--no_resume', - '--platform=darwin', '--no_auth', - '--bucket', 'chromium-luci', - '-d', luci_go_dir.join('mac64')]) - self.m.step('download luci-go win', - ['download_from_google_storage', '--no_resume', - '--platform=win32', '--no_auth', '--bucket', - 'chromium-luci', - '-d', luci_go_dir.join('win64')]) - # Copy binaries to the expected location. - dest = self.m.path['start_dir'].join('luci-go') - self.m.run.rmtree(dest) - self.m.file.copytree('Copy Go binary', - source=luci_go_dir, - dest=dest) - - def create_isolated_gen_json(self, isolate_path, base_dir, os_type, - task_name, extra_variables, blacklist=None): - """Creates an isolated.gen.json file (used by the isolate recipe module). + def default_expiration(self): + """Number of seconds that the server will wait to find a bot able to run the + task. - Args: - isolate_path: path obj. Path to the isolate file. - base_dir: path obj. Dir that is the base of all paths in the isolate file. - os_type: str. The OS type to use when archiving the isolate file. - Eg: linux. - task_name: str. The isolated.gen.json file will be suffixed by this str. - extra_variables: dict of str to str. The extra vars to pass to isolate. - Eg: {'SLAVE_NUM': '1', 'MASTER': 'ChromiumPerfFYI'} - blacklist: list of regular expressions indicating which files/directories - not to archive. - """ - self.m.file.makedirs('swarming tmp dir', self.swarming_temp_dir) - isolated_path = self.isolated_file_path(task_name) - isolate_args = [ - '--isolate', isolate_path, - '--isolated', isolated_path, - '--config-variable', 'OS', os_type, - ] - if blacklist: - for b in blacklist: - isolate_args.extend(['--blacklist', b]) - for k, v in extra_variables.iteritems(): - isolate_args.extend(['--extra-variable', k, v]) - isolated_gen_dict = { - 'version': 1, - 'dir': base_dir, - 'args': isolate_args, - } - isolated_gen_json = self.swarming_temp_dir.join( - '%s.isolated.gen.json' % task_name) - self.m.file.write( - 'Write %s.isolated.gen.json' % task_name, - isolated_gen_json, - self.m.json.dumps(isolated_gen_dict, indent=4), - ) + If not bot runs the task by this number of seconds, the task is canceled as + EXPIRED. - def batcharchive(self, targets): - """Calls batcharchive on the skia.isolated.gen.json file. + This value can be changed per individual task. + """ + return self._default_expiration - Args: - targets: list of str. The suffixes of the isolated.gen.json files to - archive. + @default_expiration.setter + def default_expiration(self, value): + assert 30 <= value <= 24*60*60, value + self._default_expiration = value - Returns: - list of tuples containing (task_name, swarming_hash). + @property + def default_hard_timeout(self): + """Number of seconds in which the task must complete. + + If the task takes more than this amount of time, the process is assumed to + be hung. It forcibly killed via SIGTERM then SIGKILL after a grace period + (default: 30s). Then the task is marked as TIMED_OUT. + + This value can be changed per individual task. + """ + return self._default_hard_timeout + + @default_hard_timeout.setter + def default_hard_timeout(self, value): + assert 30 <= value <= 6*60*60, value + self._default_hard_timeout = value + + @property + def default_io_timeout(self): + """Number of seconds at which interval the task must write to stdout or + stderr. + + If the task takes more than this amount of time between writes to stdout or + stderr, the process is assumed to be hung. It forcibly killed via SIGTERM + then SIGKILL after a grace period (default: 30s). Then the task is marked as + TIMED_OUT. + + This value can be changed per individual task. + """ + return self._default_io_timeout + + @default_io_timeout.setter + def default_io_timeout(self, value): + assert 30 <= value <= 6*60*60, value + self._default_io_timeout = value + + @property + def default_idempotent(self): + """Bool to specify if task deduplication can be done. + + When set, the server will search for another task that ran in the last days + that had the exact same properties. If it finds one, the task will not be + run at all, the previous results will be returned as-is. + + For more infos, see: + https://github.com/luci/luci-py/blob/master/appengine/swarming/doc/User-Guide.md#task-idempotency + + This value can be changed per individual task. + """ + return self._default_idempotent + + @default_idempotent.setter + def default_idempotent(self, value): + assert isinstance(value, bool), value + self._default_idempotent = value + + @property + def default_user(self): + """String to represent who triggered the task. + + The user should be an email address when someone requested testing via + pre-commit or manual testing. + + This value can be changed per individual task. + """ + return self._default_user + + @default_user.setter + def default_user(self, value): + assert value is None or isinstance(value, basestring), value + self._default_user = value + + @property + def default_dimensions(self): + """Returns a copy of the default Swarming dimensions to run task on. + + The dimensions are what is used to filter which bots are able to run the + task successfully. This is particularly useful to discern between OS + versions, type of CPU, GPU card or VM, or preallocated pool. + + Example: + {'cpu': 'x86-64', 'os': 'Windows-XP-SP3'} + + This value can be changed per individual task. + """ + return ReadOnlyDict(self._default_dimensions) + + def set_default_dimension(self, key, value): + assert isinstance(key, basestring), key + assert isinstance(value, basestring) or value is None, value + if value is None: + self._default_dimensions.pop(key, None) + else: + self._default_dimensions[key] = value # pragma: no cover + + @property + def default_env(self): + """Returns a copy of the default environment variable to run tasks with. + + By default the environment variable is not modified. Additional environment + variables can be specified for each task. + + This value can be changed per individual task. + """ + return ReadOnlyDict(self._default_env) + + def set_default_env(self, key, value): + assert isinstance(key, basestring), key + assert isinstance(value, basestring), value + self._default_env[key] = value + + @property + def default_priority(self): + """Swarming task priority for tasks triggered from the recipe. + + Priority ranges from 1 to 255. The lower the value, the most important the + task is and will preempty any task with a lower priority. + + This value can be changed per individual task. + """ + return self._default_priority + + @default_priority.setter + def default_priority(self, value): + assert 1 <= value <= 255 + self._default_priority = value + + def add_default_tag(self, tag): + """Adds a tag to the Swarming tasks triggered. + + Tags are used for maintenance, they can be used to calculate the number of + tasks run for a day to calculate the cost of a type of type (CQ, ASAN, etc). + + Tags can be added per individual task. + """ + assert ':' in tag, tag + self._default_tags.add(tag) + + @property + def show_isolated_out_in_collect_step(self): + """Show the shard's isolated out link in each collect step.""" + return self._show_isolated_out_in_collect_step + + @show_isolated_out_in_collect_step.setter + def show_isolated_out_in_collect_step(self, value): + self._show_isolated_out_in_collect_step = value + + @property + def show_shards_in_collect_step(self): + """Show the shard link in each collect step.""" + return self._show_shards_in_collect_step + + @show_shards_in_collect_step.setter + def show_shards_in_collect_step(self, value): + self._show_shards_in_collect_step = value + + @staticmethod + def prefered_os_dimension(platform): + """Given a platform name returns the prefered Swarming OS dimension. + + Platform name is usually provided by 'platform' recipe module, it's one + of 'win', 'linux', 'mac'. This function returns more concrete Swarming OS + dimension that represent this platform on Swarming by default. + + Recipes are free to use other OS dimension if there's a need for it. For + example WinXP try bot recipe may explicitly specify 'Windows-XP-SP3' + dimension. """ - return self.m.isolate.isolate_tests( - verbose=True, # To avoid no output timeouts. - build_dir=self.swarming_temp_dir, - targets=targets).presentation.properties['swarm_hashes'].items() + return { + 'linux': 'Ubuntu-14.04', + 'mac': 'Mac-10.9', + 'win': 'Windows-7-SP1', + }[platform] + + def task(self, title, isolated_hash, ignore_task_failure=False, shards=1, + task_output_dir=None, extra_args=None, idempotent=None, + cipd_packages=None, build_properties=None, merge=None): + """Returns a new SwarmingTask instance to run an isolated executable on + Swarming. + + For google test executables, use gtest_task() instead. - def trigger_swarming_tasks( - self, swarm_hashes, dimensions, idempotent=False, store_output=True, - extra_args=None, expiration=None, hard_timeout=None, io_timeout=None, - cipd_packages=None): - """Triggers swarming tasks using swarm hashes. + At the time of this writting, this code is used by V8, Skia and iOS. + + The return value can be customized if necessary (see SwarmingTask class + below). Pass it to 'trigger_task' to launch it on swarming. Later pass the + same instance to 'collect_task' to wait for the task to finish and fetch its + results. Args: - swarm_hashes: list of str. List of swarm hashes from the isolate server. - dimensions: dict of str to str. The dimensions to run the task on. - Eg: {'os': 'Ubuntu', 'gpu': '10de', 'pool': 'Skia'} - idempotent: bool. Whether or not to de-duplicate tasks. - store_output: bool. Whether task output should be stored. - extra_args: list of str. Extra arguments to pass to the task. - expiration: int. Task will expire if not picked up within this time. - DEFAULT_TASK_EXPIRATION is used if this argument is None. - hard_timeout: int. Task will timeout if not completed within this time. - DEFAULT_TASK_TIMEOUT is used if this argument is None. - io_timeout: int. Task will timeout if there is no output within this time. - DEFAULT_IO_TIMEOUT is used if this argument is None. - cipd_packages: CIPD packages which these tasks depend on. + title: name of the test, used as part of a task ID. + isolated_hash: hash of isolated test on isolate server, the test should + be already isolated there, see 'isolate' recipe module. + ignore_task_failure: whether to ignore the test failure of swarming + tasks. By default, this is set to False. + shards: if defined, the number of shards to use for the task. By default + this value is either 1 or based on the title. + task_output_dir: if defined, the directory where task results are placed. + The caller is responsible for removing this folder when finished. + extra_args: list of command line arguments to pass to isolated tasks. + idempotent: whether this task is considered idempotent. Defaults + to self.default_idempotent if not specified. + cipd_packages: list of 3-tuples corresponding to CIPD packages needed for + the task: ('path', 'package_name', 'version'), defined as follows: + path: Path relative to the Swarming root dir in which to install + the package. + package_name: Name of the package to install, + eg. "infra/tools/authutil/${platform}" + version: Version of the package, either a package instance ID, + ref, or tag key/value pair. + build_properties: An optional dict containing various build properties. + These are typically but not necessarily the properties emitted by + bot_update. + merge: An optional dict containing: + "script": path to a script to call to post process and merge the + collected outputs from the tasks. The script should take one + named (but required) parameter, '-o' (for output), that represents + the path that the merged results should be written to, and accept + N additional paths to result files to merge. The merged results + should be in the JSON Results File Format + (https://www.chromium.org/developers/the-json-test-results-format) + and may optionally contain a top level "links" field that + may contain a dict mapping link text to URLs, for a set of + links that will be included in the buildbot output. + "args": an optional list of additional arguments to pass to the + above script. + """ + if idempotent is None: + idempotent = self.default_idempotent + return SwarmingTask( + title=title, + isolated_hash=isolated_hash, + dimensions=self._default_dimensions, + env=self._default_env, + priority=self.default_priority, + shards=shards, + buildername=self.m.properties.get('buildername'), + buildnumber=self.m.properties.get('buildnumber'), + user=self.default_user, + expiration=self.default_expiration, + io_timeout=self.default_io_timeout, + hard_timeout=self.default_hard_timeout, + idempotent=idempotent, + ignore_task_failure=ignore_task_failure, + extra_args=extra_args, + collect_step=self._default_collect_step, + task_output_dir=task_output_dir, + cipd_packages=cipd_packages, + build_properties=build_properties, + merge=merge) - Returns: - List of swarming.SwarmingTask instances. - """ - swarming_tasks = [] - for task_name, swarm_hash in swarm_hashes: - swarming_task = self.m.swarming.task( - title=task_name, - cipd_packages=cipd_packages, - isolated_hash=swarm_hash) - if store_output: - swarming_task.task_output_dir = self.tasks_output_dir.join(task_name) - swarming_task.dimensions = dimensions - swarming_task.idempotent = idempotent - swarming_task.priority = 90 - swarming_task.expiration = ( - expiration if expiration else DEFAULT_TASK_EXPIRATION) - swarming_task.hard_timeout = ( - hard_timeout if hard_timeout else DEFAULT_TASK_TIMEOUT) - swarming_task.io_timeout = ( - io_timeout if io_timeout else DEFAULT_IO_TIMEOUT) - if extra_args: - swarming_task.extra_args = extra_args - revision = self.m.properties.get('revision') - if revision: - swarming_task.tags.add('revision:%s' % revision) - swarming_tasks.append(swarming_task) - step_results = self.m.swarming.trigger(swarming_tasks) - for step_result in step_results: - self._add_log_links(step_result, step_result.json.output) - return swarming_tasks - - def collect_swarming_task(self, swarming_task): - """Collects the specified swarming task. + def check_client_version(self, step_test_data=None): + """Yields steps to verify compatibility with swarming_client version.""" + return self.m.swarming_client.ensure_script_version( + 'swarming.py', MINIMAL_SWARMING_VERSION, step_test_data) + + def trigger_task(self, task, **kwargs): + """Triggers one task. + + It the task is sharded, will trigger all shards. This steps justs posts + the task and immediately returns. Use 'collect_task' to wait for a task to + finish and grab its result. + + Behaves as a regular recipe step: returns StepData with step results + on success or raises StepFailure if step fails. Args: - swarming_task: An instance of swarming.SwarmingTask. + task: SwarmingTask instance. + kwargs: passed to recipe step constructor as-is. """ + assert isinstance(task, SwarmingTask) + assert task.task_name not in self._pending_tasks, ( + 'Triggered same task twice: %s' % task.task_name) + assert 'os' in task.dimensions, task.dimensions + self._pending_tasks.add(task.task_name) + + # Trigger parameters. + args = [ + 'trigger', + '--swarming', self.swarming_server, + '--isolate-server', self.m.isolate.isolate_server, + '--priority', str(task.priority), + '--shards', str(task.shards), + '--task-name', task.task_name, + '--dump-json', self.m.json.output(), + '--expiration', str(task.expiration), + '--io-timeout', str(task.io_timeout), + '--hard-timeout', str(task.hard_timeout), + ] + for name, value in sorted(task.dimensions.iteritems()): + assert isinstance(value, basestring), value + args.extend(['--dimension', name, value]) + for name, value in sorted(task.env.iteritems()): + assert isinstance(value, basestring), value + args.extend(['--env', name, value]) + + # Default tags. + tags = set(task.tags) + tags.update(self._default_tags) + tags.add('data:' + task.isolated_hash) + tags.add('name:' + task.title.split(' ')[0]) + mastername = self.m.properties.get('mastername') + if mastername: # pragma: no cover + tags.add('master:' + mastername) + if task.buildername: # pragma: no cover + tags.add('buildername:' + task.buildername) + if task.buildnumber: # pragma: no cover + tags.add('buildnumber:%s' % task.buildnumber) + if task.dimensions.get('os'): + tags.add('os:' + task.dimensions['os']) + if self.m.properties.get('bot_id'): # pragma: no cover + tags.add('slavename:%s' % self.m.properties['bot_id']) + tags.add('stepname:%s' % self.get_step_name('', task)) + rietveld = self.m.properties.get('rietveld') + issue = self.m.properties.get('issue') + patchset = self.m.properties.get('patchset') + if rietveld and issue and patchset: + # The expected format is strict to the usage of buildbot properties on the + # Chromium Try Server. Fix if necessary. + tags.add('rietveld:%s/%s/#ps%s' % (rietveld, issue, patchset)) + for tag in sorted(tags): + assert ':' in tag, tag + args.extend(['--tag', tag]) + + if self.verbose: + args.append('--verbose') + if task.idempotent: + args.append('--idempotent') + if task.user: + args.extend(['--user', task.user]) + + if task.cipd_packages: + for path, pkg, version in task.cipd_packages: + args.extend(['--cipd-package', '%s:%s:%s' % (path, pkg, version)]) + + # What isolated command to trigger. + args.extend(('--isolated', task.isolated_hash)) + + # Additional command line args for isolated command. + if task.extra_args: # pragma: no cover + args.append('--') + args.extend(task.extra_args) + + # The step can fail only on infra failures, so mark it as 'infra_step'. try: - rv = self.m.swarming.collect_task(swarming_task) - except self.m.step.StepFailure as e: # pragma: no cover - step_result = self.m.step.active_result - # Change step result to Infra failure if the swarming task failed due to - # expiration, time outs, bot crashes or task cancelations. - # Infra failures have step.EXCEPTION. - states_infra_failure = ( - self.m.swarming.State.EXPIRED, self.m.swarming.State.TIMED_OUT, - self.m.swarming.State.BOT_DIED, self.m.swarming.State.CANCELED) - summary = step_result.swarming.summary - if summary['shards'][0]['state'] in states_infra_failure: - step_result.presentation.status = self.m.step.EXCEPTION - raise self.m.step.InfraFailure(e.name, step_result) - raise + return self.m.python( + name=self.get_step_name('trigger', task), + script=self.m.swarming_client.path.join('swarming.py'), + args=args, + step_test_data=functools.partial( + self._gen_trigger_step_test_data, task), + infra_step=True, + **kwargs) finally: + # Store trigger output with the |task|, print links to triggered shards. step_result = self.m.step.active_result - # Add log link. - self._add_log_links(step_result, step_result.swarming.summary) - return rv - - def _add_log_links(self, step_result, summary): - """Add Milo log links to all shards in the step.""" - ids = [] - shards = summary.get('shards') - if shards: - for shard in shards: - ids.append(shard['id']) + step_result.presentation.step_text += text_for_task(task) + + if step_result.presentation != self.m.step.FAILURE: + task._trigger_output = step_result.json.output + links = step_result.presentation.links + for index in xrange(task.shards): + url = task.get_shard_view_url(index) + if url: + links['shard #%d' % index] = url + assert not hasattr(step_result, 'swarming_task') + step_result.swarming_task = task + + def collect_task(self, task, **kwargs): + """Waits for a single triggered task to finish. + + If the task is sharded, will wait for all shards to finish. Behaves as + a regular recipe step: returns StepData with step results on success or + raises StepFailure if task fails. + + Args: + task: SwarmingTask instance, previously triggered with 'trigger' method. + kwargs: passed to recipe step constructor as-is. + """ + # TODO(vadimsh): Raise InfraFailure on Swarming failures. + assert isinstance(task, SwarmingTask) + assert task.task_name in self._pending_tasks, ( + 'Trying to collect a task that was not triggered: %s' % + task.task_name) + self._pending_tasks.remove(task.task_name) + + try: + return task.collect_step(task, **kwargs) + finally: + try: + self.m.step.active_result.swarming_task = task + except Exception: # pragma: no cover + # If we don't have an active_result, something failed very early, + # so we eat this exception and let that one propagate. + pass + + def trigger(self, tasks, **kwargs): # pragma: no cover + """Batch version of 'trigger_task'. + + Deprecated, to be removed soon. Use 'trigger_task' in a loop instead, + properly handling exceptions. This method doesn't handle trigger failures + well (it aborts on a first failure). + """ + return [self.trigger_task(t, **kwargs) for t in tasks] + + def collect(self, tasks, **kwargs): # pragma: no cover + """Batch version of 'collect_task'. + + Deprecated, to be removed soon. Use 'collect_task' in a loop instead, + properly handling exceptions. This method doesn't handle collect failures + well (it aborts on a first failure). + """ + return [self.collect_task(t, **kwargs) for t in tasks] + + # To keep compatibility with some build_internal code. To be removed as well. + collect_each = collect + + @staticmethod + def _display_pending(summary_json, step_presentation): + """Shows max pending time in seconds across all shards if it exceeds 10s.""" + pending_times = [ + (parse_time(shard['started_ts']) - + parse_time(shard['created_ts'])).total_seconds() + for shard in summary_json.get('shards', []) if shard.get('started_ts') + ] + max_pending = max(pending_times) if pending_times else 0 + + # Only display annotation when pending more than 10 seconds to reduce noise. + if max_pending > 10: + step_presentation.step_text += '<br>swarming pending %ds' % max_pending + + def _default_collect_step( + self, task, merged_test_output=None, + step_test_data=None, + **kwargs): + """Produces a step that collects a result of an arbitrary task.""" + task_output_dir = task.task_output_dir or self.m.raw_io.output_dir() + + # If we don't already have a Placeholder, wrap the task_output_dir in one + # so we can read out of it later w/ step_result.raw_io.output_dir. + if not isinstance(task_output_dir, recipe_util.Placeholder): + task_output_dir = self.m.raw_io.output_dir(leak_to=task_output_dir) + + task_args = [ + '-o', merged_test_output or self.m.json.output(), + '--task-output-dir', task_output_dir, + ] + + merge_script = (task.merge.get('script') + or self.resource('noop_merge.py')) + merge_args = (task.merge.get('args') or []) + + task_args.extend([ + '--merge-script', merge_script, + '--merge-additional-args', self.m.json.dumps(merge_args), + ]) + + if task.build_properties: # pragma: no cover + properties = dict(task.build_properties) + properties.update(self.m.properties) + task_args.extend([ + '--build-properties', self.m.json.dumps(properties), + ]) + + task_args.append('--') + # Arguments for the actual 'collect' command. + collect_cmd = [ + 'python', + '-u', + self.m.swarming_client.path.join('swarming.py'), + ] + collect_cmd.extend(self.get_collect_cmd_args(task)) + collect_cmd.extend([ + '--task-summary-json', self.summary(), + ]) + + task_args.extend(collect_cmd) + + allowed_return_codes = {0} + if task.ignore_task_failure: # pragma: no cover + allowed_return_codes = 'any' + + # The call to collect_task emits two JSON files: + # 1) a task summary JSON emitted by swarming + # 2) a gtest results JSON emitted by the task + # This builds an instance of StepTestData that covers both. + step_test_data = step_test_data or ( + self.test_api.canned_summary_output(task.shards) + + self.m.json.test_api.output({})) + + try: + with self.m.context(cwd=self.m.path['start_dir']): + return self.m.python( + name=self.get_step_name('', task), + script=self.resource('collect_task.py'), + args=task_args, + ok_ret=allowed_return_codes, + step_test_data=lambda: step_test_data, + **kwargs) + finally: + step_result = None + try: + step_result = self.m.step.active_result + step_result.presentation.step_text = text_for_task(task) + summary_json = step_result.swarming.summary + self._handle_summary_json(task, summary_json, step_result) + + links = {} + if hasattr(step_result, 'json') and hasattr(step_result.json, 'output'): + links = step_result.json.output.get('links', {}) + for k, v in links.iteritems(): # pragma: no cover + step_result.presentation.links[k] = v + except Exception as e: + if step_result: + step_result.presentation.logs['no_results_exc'] = [str(e)] + + def get_step_name(self, prefix, task): + """SwarmingTask -> name of a step of a waterfall. + + Will take a task title (+ step name prefix) and append OS dimension to it. + + Args: + prefix: prefix to append to task title, like 'trigger'. + task: SwarmingTask instance. + + Returns: + '[<prefix>] <task title> on <OS>' + """ + prefix = '[%s] ' % prefix if prefix else '' + task_os = task.dimensions['os'] + + bot_os = self.prefered_os_dimension(self.m.platform.name) + suffix = ('' if ( + task_os == bot_os or task_os.lower() == self.m.platform.name.lower()) + else ' on %s' % task_os) + # Note: properly detecting dimensions of the bot the recipe is running + # on is somewhat non-trivial. It is not safe to assume it uses default + # or preferred dimensions for its OS. For example, the version of the OS + # can differ. + return ''.join((prefix, task.title, suffix)) + + def _handle_summary_json(self, task, summary, step_result): + # We store this now, and add links to all shards first, before failing the + # build. Format is tuple of (error message, shard that failed) + infra_failures = [] + links = step_result.presentation.links + for index, shard in enumerate(summary['shards']): + url = task.get_shard_view_url(index) + display_text = 'shard #%d' % index + + if not shard or shard.get('internal_failure'): # pragma: no cover + display_text = ( + 'shard #%d had an internal swarming failure' % index) + infra_failures.append((index, 'Internal swarming failure')) + elif self._is_expired(shard): + display_text = ( + 'shard #%d expired, not enough capacity' % index) + infra_failures.append(( + index, 'There isn\'t enough capacity to run your test')) + elif self._is_timed_out(shard): + display_text = ( + 'shard #%d timed out, took too much time to complete' % index) + elif self._get_exit_code(shard) != '0': # pragma: no cover + display_text = 'shard #%d (failed)' % index + + if self.show_isolated_out_in_collect_step: + isolated_out = shard.get('isolated_out') + if isolated_out: + link_name = 'shard #%d isolated out' % index + links[link_name] = isolated_out['view_url'] + + if url and self.show_shards_in_collect_step: + links[display_text] = url + + self._display_pending(summary, step_result.presentation) + + if infra_failures: + template = 'Shard #%s failed: %s' + + # Done so that raising an InfraFailure doesn't cause an error. + # TODO(martiniss): Remove this hack. Requires recipe engine change + step_result._retcode = 2 + step_result.presentation.status = self.m.step.EXCEPTION + raise recipe_api.InfraFailure( + '\n'.join(template % f for f in infra_failures), result=step_result) + + def get_collect_cmd_args(self, task): + """SwarmingTask -> argument list for 'swarming.py' command.""" + args = [ + 'collect', + '--swarming', self.swarming_server, + '--decorate', + '--print-status-updates', + ] + if self.verbose: + args.append('--verbose') + args.extend(('--json', self.m.json.input(task.trigger_output))) + return args + + def _gen_trigger_step_test_data(self, task): + """Generates an expected value of --dump-json in 'trigger' step. + + Used when running recipes to generate test expectations. + """ + # Suffixes of shard subtask names. + subtasks = [] + if task.shards == 1: + subtasks = [''] else: - for _, task in summary.get('tasks', {}).iteritems(): - ids.append(task['task_id']) - for idx, task_id in enumerate(ids): - link = MILO_LOG_LINK % task_id - k = 'view steps on Milo' - if len(ids) > 1: # pragma: nocover - k += ' (shard index %d, %d total)' % (idx, len(ids)) - step_result.presentation.links[k] = link + subtasks = [':%d:%d' % (task.shards, i) for i in range(task.shards)] + return self.m.json.test_api.output({ + 'base_task_name': task.task_name, + 'tasks': { + '%s%s' % (task.task_name, suffix): { + 'task_id': '1%02d00' % i, + 'shard_index': i, + 'view_url': '%s/user/task/1%02d00' % (self.swarming_server, i), + } for i, suffix in enumerate(subtasks) + }, + }) + + +class SwarmingTask(object): + """Definition of a task to run on swarming.""" + def __init__(self, title, isolated_hash, ignore_task_failure, dimensions, + env, priority, shards, buildername, buildnumber, expiration, + user, io_timeout, hard_timeout, idempotent, extra_args, + collect_step, task_output_dir, cipd_packages=None, + build_properties=None, merge=None): + """Configuration of a swarming task. + + Args: + title: display name of the task, hints to what task is doing. Usually + corresponds to a name of a test executable. Doesn't have to be unique. + isolated_hash: hash of isolated file that describes all files needed to + run the task as well as command line to launch. See 'isolate' recipe + module. + ignore_task_failure: whether to ignore the test failure of swarming + tasks. + cipd_packages: list of 3-tuples corresponding to CIPD packages needed for + the task: ('path', 'package_name', 'version'), defined as follows: + path: Path relative to the Swarming root dir in which to install + the package. + package_name: Name of the package to install, + eg. "infra/tools/authutil/${platform}" + version: Version of the package, either a package instance ID, + ref, or tag key/value pair. + collect_step: callback that will be called to collect and processes + results of task execution, signature is collect_step(task, **kwargs). + dimensions: key-value mapping with swarming dimensions that specify + on what Swarming slaves task can run. One important dimension is 'os', + which defines platform flavor to run the task on. See Swarming doc. + env: key-value mapping with additional environment variables to add to + environment before launching the task executable. + priority: integer [0, 255] that defines how urgent the task is. + Lower value corresponds to higher priority. Swarming service executes + tasks with higher priority first. + shards: how many concurrent shards to run, makes sense only for + isolated tests based on gtest. Swarming uses GTEST_SHARD_INDEX + and GTEST_TOTAL_SHARDS environment variables to tell the executable + what shard to run. + buildername: buildbot builder this task was triggered from. + buildnumber: build number of a build this task was triggered from. + expiration: number of schedule until the task shouldn't even be run if it + hadn't started yet. + user: user that requested this task, if applicable. + io_timeout: number of seconds that the task is allowed to not emit any + stdout bytes, after which it is forcibly killed. + hard_timeout: number of seconds for which the task is allowed to run, + after which it is forcibly killed. + idempotent: True if the results from a previous task can be reused. E.g. + this task has no side-effects. + extra_args: list of command line arguments to pass to isolated tasks. + task_output_dir: if defined, the directory where task results are placed + during the collect step. + build_properties: An optional dict containing various build properties. + These are typically but not necessarily the properties emitted by + bot_update. + merge: An optional dict containing: + "script": path to a script to call to post process and merge the + collected outputs from the tasks. + "args": an optional list of additional arguments to pass to the + above script. + """ + self._trigger_output = None + self.build_properties = build_properties + self.buildername = buildername + self.buildnumber = buildnumber + self.cipd_packages = cipd_packages + self.collect_step = collect_step + self.dimensions = dimensions.copy() + self.env = env.copy() + self.expiration = expiration + self.extra_args = tuple(extra_args or []) + self.hard_timeout = hard_timeout + self.idempotent = idempotent + self.ignore_task_failure = ignore_task_failure + self.io_timeout = io_timeout + self.isolated_hash = isolated_hash + self.merge = merge or {} + self.priority = priority + self.shards = shards + self.tags = set() + self.task_output_dir = task_output_dir + self.title = title + self.user = user + + @property + def task_name(self): + """Name of this task, derived from its other properties. + + The task name is purely to make sense of the task and is not used in any + other way. + """ + out = '%s/%s/%s' % ( + self.title, self.dimensions['os'], self.isolated_hash[:10]) + if self.buildername: # pragma: no cover + out += '/%s/%s' % (self.buildername, self.buildnumber or -1) + return out + + @property + def trigger_output(self): + """JSON results of 'trigger' step or None if not triggered.""" + return self._trigger_output + + def get_shard_view_url(self, index): + """Returns URL of HTML page with shard details or None if not available. + + Works only after the task has been successfully triggered. + """ + if self._trigger_output and self._trigger_output.get('tasks'): + for shard_dict in self._trigger_output['tasks'].itervalues(): + if shard_dict['shard_index'] == index: + return shard_dict['view_url'] diff --git a/infra/bots/recipe_modules/swarming/example.expected/test.json b/infra/bots/recipe_modules/swarming/example.expected/basic.json index f59d6fa45f..7a2448dd56 100644 --- a/infra/bots/recipe_modules/swarming/example.expected/test.json +++ b/infra/bots/recipe_modules/swarming/example.expected/basic.json @@ -17,7 +17,7 @@ "retry", "fetch", "origin", - "abc123" + "master" ], "cwd": "[START_DIR]/swarming.client", "env": { @@ -100,201 +100,63 @@ }, { "cmd": [ - "download_from_google_storage", - "--no_resume", - "--platform=linux*", - "--no_auth", - "--bucket", - "chromium-luci", - "-d", - "lmydirimydirnmydirumydirxmydir6mydir4" - ], - "env": { - "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" - }, - "name": "download luci-go linux" - }, - { - "cmd": [ - "download_from_google_storage", - "--no_resume", - "--platform=darwin", - "--no_auth", - "--bucket", - "chromium-luci", - "-d", - "mmydiramydircmydir6mydir4" - ], - "env": { - "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" - }, - "name": "download luci-go mac" - }, - { - "cmd": [ - "download_from_google_storage", - "--no_resume", - "--platform=win32", - "--no_auth", - "--bucket", - "chromium-luci", - "-d", - "wmydirimydirnmydir6mydir4" - ], - "env": { - "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" - }, - "name": "download luci-go win" - }, - { - "cmd": [ - "python", - "-u", - "RECIPE_MODULE[build::file]/resources/fileutil.py", - "rmtree", - "[START_DIR]/luci-go" - ], - "env": { - "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts" - }, - "infra_step": true, - "name": "rmtree luci-go" - }, - { - "cmd": [ "python", "-u", - "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n", - "mydir", - "[START_DIR]/luci-go", - "0" - ], - "name": "Copy Go binary" - }, - { - "cmd": [ - "python", - "-u", - "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n if os.path.exists(path):\n print \"%s exists but is not a dir\" % path\n sys.exit(1)\n os.makedirs(path, mode)\n", - "[START_DIR]/swarming_temp_dir", - "511" - ], - "name": "makedirs swarming tmp dir", - "~followup_annotations": [ - "@@@STEP_LOG_LINE@python.inline@@@@", - "@@@STEP_LOG_LINE@python.inline@import sys, os@@@", - "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@", - "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@", - "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@", - "@@@STEP_LOG_LINE@python.inline@ if os.path.exists(path):@@@", - "@@@STEP_LOG_LINE@python.inline@ print \"%s exists but is not a dir\" % path@@@", - "@@@STEP_LOG_LINE@python.inline@ sys.exit(1)@@@", - "@@@STEP_LOG_LINE@python.inline@ os.makedirs(path, mode)@@@", - "@@@STEP_LOG_END@python.inline@@@" - ] - }, - { - "cmd": [ - "python", - "-u", - "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n", - "{\n \"args\": [\n \"--isolate\", \n \"isolate_path\", \n \"--isolated\", \n \"[START_DIR]/swarming_temp_dir/skia-task-task.isolated\", \n \"--config-variable\", \n \"OS\", \n \"linux\", \n \"--blacklist\", \n \"*.pyc\", \n \"--extra-variable\", \n \"myvar\", \n \"myval\"\n ], \n \"dir\": \"isolate_dir\", \n \"version\": 1\n}", - "[START_DIR]/swarming_temp_dir/task.isolated.gen.json" + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" ], - "name": "Write task.isolated.gen.json" + "name": "archive for win", + "stdout": "/path/to/tmp/" }, { "cmd": [ "python", "-u", - "RECIPE_MODULE[build::isolate]/resources/isolate.py", - "[START_DIR]/swarming.client", - "batcharchive", - "--dump-json", - "/path/to/tmp/json", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", "--isolate-server", - "https://isolateserver.appspot.com", - "--verbose", - "[START_DIR]/swarming_temp_dir/task-0.isolated.gen.json", - "[START_DIR]/swarming_temp_dir/task-1.isolated.gen.json", - "[START_DIR]/swarming_temp_dir/task-2.isolated.gen.json", - "[START_DIR]/swarming_temp_dir/task-3.isolated.gen.json", - "[START_DIR]/swarming_temp_dir/task-4.isolated.gen.json" + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "linux", + "--verbose" ], - "name": "isolate tests", - "~followup_annotations": [ - "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-0\": \"[dummy hash for task-0]\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"task-1\": \"[dummy hash for task-1]\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"task-2\": \"[dummy hash for task-2]\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"task-3\": \"[dummy hash for task-3]\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"task-4\": \"[dummy hash for task-4]\"@@@", - "@@@STEP_LOG_LINE@json.output@}@@@", - "@@@STEP_LOG_END@json.output@@@", - "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"task-0\": \"[dummy hash for task-0]\", \"task-1\": \"[dummy hash for task-1]\", \"task-2\": \"[dummy hash for task-2]\", \"task-3\": \"[dummy hash for task-3]\", \"task-4\": \"[dummy hash for task-4]\"}@@@" - ] + "name": "archive for linux", + "stdout": "/path/to/tmp/" }, { "cmd": [ "python", "-u", - "[START_DIR]/swarming.client/swarming.py", - "trigger", - "--swarming", - "https://chromium-swarm.appspot.com", - "--isolate-server", - "https://isolateserver.appspot.com", - "--priority", - "90", - "--shards", - "1", - "--task-name", - "task-4/Linux/[dummy has", - "--dump-json", - "/path/to/tmp/json", - "--expiration", - "72000", - "--io-timeout", - "2400", - "--hard-timeout", - "14400", - "--dimension", - "os", - "Linux", - "--tag", - "allow_milo:1", - "--tag", - "data:[dummy hash for task-4]", - "--tag", - "name:task-4", - "--tag", - "os:Linux", - "--tag", - "revision:abc123", - "--tag", - "stepname:task-4", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", "--isolated", - "[dummy hash for task-4]", - "--", - "--extra" + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "mac", + "--verbose" ], - "infra_step": true, - "name": "[trigger] task-4", - "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'@@@", - "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"task-4/Linux/[dummy has\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-4/Linux/[dummy has\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", - "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@", - "@@@STEP_LOG_LINE@json.output@ }@@@", - "@@@STEP_LOG_LINE@json.output@ }@@@", - "@@@STEP_LOG_LINE@json.output@}@@@", - "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@" - ] + "name": "archive for mac", + "stdout": "/path/to/tmp/" }, { "cmd": [ @@ -303,59 +165,72 @@ "[START_DIR]/swarming.client/swarming.py", "trigger", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--isolate-server", - "https://isolateserver.appspot.com", + "https://isolateserver-dev.appspot.com", "--priority", - "90", + "30", "--shards", "1", "--task-name", - "task-2/Linux/[dummy has", + "hello_world/Windows-7-SP1/hash_for_w", "--dump-json", "/path/to/tmp/json", "--expiration", - "72000", + "3600", "--io-timeout", - "2400", + "1200", "--hard-timeout", - "14400", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", "--dimension", "os", - "Linux", + "Windows-7-SP1", + "--env", + "TESTING", + "1", "--tag", - "allow_milo:1", + "data:hash_for_win", "--tag", - "data:[dummy hash for task-2]", + "master:tryserver", "--tag", - "name:task-2", + "name:hello_world", "--tag", - "os:Linux", + "os:Windows-7-SP1", "--tag", - "revision:abc123", + "os:win", "--tag", - "stepname:task-2", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", "--isolated", - "[dummy hash for task-2]", - "--", - "--extra" + "hash_for_win" ], "infra_step": true, - "name": "[trigger] task-2", + "name": "[trigger] hello_world on Windows-7-SP1", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'@@@", + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"task-2/Linux/[dummy has\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-2/Linux/[dummy has\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@}@@@", "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@" + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" ] }, { @@ -365,59 +240,78 @@ "[START_DIR]/swarming.client/swarming.py", "trigger", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--isolate-server", - "https://isolateserver.appspot.com", + "https://isolateserver-dev.appspot.com", "--priority", - "90", + "30", "--shards", - "1", + "2", "--task-name", - "task-3/Linux/[dummy has", + "hello_world/Ubuntu-14.04/hash_for_l", "--dump-json", "/path/to/tmp/json", "--expiration", - "72000", + "3600", "--io-timeout", - "2400", + "1200", "--hard-timeout", - "14400", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", "--dimension", "os", - "Linux", + "Ubuntu-14.04", + "--env", + "TESTING", + "1", "--tag", - "allow_milo:1", + "data:hash_for_linux", "--tag", - "data:[dummy hash for task-3]", + "master:tryserver", "--tag", - "name:task-3", + "name:hello_world", "--tag", - "os:Linux", + "os:Ubuntu-14.04", "--tag", - "revision:abc123", + "os:linux", "--tag", - "stepname:task-3", + "stepname:hello_world", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", "--isolated", - "[dummy hash for task-3]", - "--", - "--extra" + "hash_for_linux" ], "infra_step": true, - "name": "[trigger] task-3", + "name": "[trigger] hello_world", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'@@@", + "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@", "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"task-3/Linux/[dummy has\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Ubuntu-14.04/hash_for_l\", @@@", "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-3/Linux/[dummy has\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Ubuntu-14.04/hash_for_l:2:0\": {@@@", "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }, @@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Ubuntu-14.04/hash_for_l:2:1\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 1, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10100\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10100\"@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@}@@@", "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@" + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@", + "@@@STEP_LINK@shard #1@https://chromium-swarm-dev.appspot.com/user/task/10100@@@" ] }, { @@ -427,139 +321,92 @@ "[START_DIR]/swarming.client/swarming.py", "trigger", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--isolate-server", - "https://isolateserver.appspot.com", + "https://isolateserver-dev.appspot.com", "--priority", - "90", + "30", "--shards", "1", "--task-name", - "task-0/Linux/[dummy has", + "hello_world/Mac-10.9/hash_for_m", "--dump-json", "/path/to/tmp/json", "--expiration", - "72000", + "3600", "--io-timeout", - "2400", + "1200", "--hard-timeout", - "14400", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", "--dimension", "os", - "Linux", + "Mac-10.9", + "--env", + "TESTING", + "1", "--tag", - "allow_milo:1", + "data:hash_for_mac", "--tag", - "data:[dummy hash for task-0]", + "master:tryserver", "--tag", - "name:task-0", + "name:hello_world", "--tag", - "os:Linux", + "os:Mac-10.9", "--tag", - "revision:abc123", + "os:mac", "--tag", - "stepname:task-0", + "stepname:hello_world on Mac-10.9", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", "--isolated", - "[dummy hash for task-0]", - "--", - "--extra" + "hash_for_mac" ], "infra_step": true, - "name": "[trigger] task-0", + "name": "[trigger] hello_world on Mac-10.9", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'@@@", + "@@@STEP_TEXT@Run on OS: 'Mac-10.9'@@@", "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"task-0/Linux/[dummy has\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Mac-10.9/hash_for_m\", @@@", "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-0/Linux/[dummy has\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Mac-10.9/hash_for_m\": {@@@", "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@ }@@@", "@@@STEP_LOG_LINE@json.output@}@@@", "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@" + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" ] }, { "cmd": [ - "python", - "-u", - "[START_DIR]/swarming.client/swarming.py", - "trigger", - "--swarming", - "https://chromium-swarm.appspot.com", - "--isolate-server", - "https://isolateserver.appspot.com", - "--priority", - "90", - "--shards", - "1", - "--task-name", - "task-1/Linux/[dummy has", - "--dump-json", - "/path/to/tmp/json", - "--expiration", - "72000", - "--io-timeout", - "2400", - "--hard-timeout", - "14400", - "--dimension", - "os", - "Linux", - "--tag", - "allow_milo:1", - "--tag", - "data:[dummy hash for task-1]", - "--tag", - "name:task-1", - "--tag", - "os:Linux", - "--tag", - "revision:abc123", - "--tag", - "stepname:task-1", - "--isolated", - "[dummy hash for task-1]", - "--", - "--extra" + "echo", + "running something locally" ], - "infra_step": true, - "name": "[trigger] task-1", - "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'@@@", - "@@@STEP_LOG_LINE@json.output@{@@@", - "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"task-1/Linux/[dummy has\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"task-1/Linux/[dummy has\": {@@@", - "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", - "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", - "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@", - "@@@STEP_LOG_LINE@json.output@ }@@@", - "@@@STEP_LOG_LINE@json.output@ }@@@", - "@@@STEP_LOG_LINE@json.output@}@@@", - "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@" - ] + "name": "local step" }, { "cmd": [ "python", "-u", - "RECIPE_PACKAGE_REPO[build]/scripts/tools/runit.py", - "--show-path", - "--", - "python", - "RECIPE_MODULE[build::swarming]/resources/collect_task.py", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", "-o", "/path/to/tmp/json", "--task-output-dir", - "[START_DIR]/swarming_temp_dir/outputs/task-4", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", "--merge-script", - "RECIPE_MODULE[build::swarming]/resources/noop_merge.py", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", "--merge-additional-args", "[]", "--", @@ -568,17 +415,18 @@ "[START_DIR]/swarming.client/swarming.py", "collect", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--decorate", "--print-status-updates", + "--verbose", "--json", - "{\"base_task_name\": \"task-4/Linux/[dummy has\", \"tasks\": {\"task-4/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", "--task-summary-json", "/path/to/tmp/json" ], - "name": "task-4", + "name": "hello_world on Windows-7-SP1", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@", + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", "@@@STEP_LOG_LINE@json.output@{}@@@", "@@@STEP_LOG_END@json.output@@@", "@@@STEP_LOG_LINE@swarming.summary@{@@@", @@ -622,25 +470,20 @@ "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", "@@@STEP_LOG_LINE@swarming.summary@}@@@", "@@@STEP_LOG_END@swarming.summary@@@", - "@@@STEP_LINK@shard #0 isolated out@blah@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@" + "@@@STEP_LINK@shard #0 isolated out@blah@@@" ] }, { "cmd": [ "python", "-u", - "RECIPE_PACKAGE_REPO[build]/scripts/tools/runit.py", - "--show-path", - "--", - "python", - "RECIPE_MODULE[build::swarming]/resources/collect_task.py", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", "-o", "/path/to/tmp/json", "--task-output-dir", - "[START_DIR]/swarming_temp_dir/outputs/task-2", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", "--merge-script", - "RECIPE_MODULE[build::swarming]/resources/noop_merge.py", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", "--merge-additional-args", "[]", "--", @@ -649,17 +492,18 @@ "[START_DIR]/swarming.client/swarming.py", "collect", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--decorate", "--print-status-updates", + "--verbose", "--json", - "{\"base_task_name\": \"task-2/Linux/[dummy has\", \"tasks\": {\"task-2/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}", + "{\"base_task_name\": \"hello_world/Ubuntu-14.04/hash_for_l\", \"tasks\": {\"hello_world/Ubuntu-14.04/hash_for_l:2:0\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}, \"hello_world/Ubuntu-14.04/hash_for_l:2:1\": {\"shard_index\": 1, \"task_id\": \"10100\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10100\"}}}", "--task-summary-json", "/path/to/tmp/json" ], - "name": "task-2", + "name": "hello_world", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@", + "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@", "@@@STEP_LOG_LINE@json.output@{}@@@", "@@@STEP_LOG_END@json.output@@@", "@@@STEP_LOG_LINE@swarming.summary@{@@@", @@ -699,52 +543,7 @@ "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 112, @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": 1, @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", - "@@@STEP_LOG_LINE@swarming.summary@ }@@@", - "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", - "@@@STEP_LOG_LINE@swarming.summary@}@@@", - "@@@STEP_LOG_END@swarming.summary@@@", - "@@@STEP_LINK@shard #0 isolated out@blah@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@" - ] - }, - { - "cmd": [ - "python", - "-u", - "RECIPE_PACKAGE_REPO[build]/scripts/tools/runit.py", - "--show-path", - "--", - "python", - "RECIPE_MODULE[build::swarming]/resources/collect_task.py", - "-o", - "/path/to/tmp/json", - "--task-output-dir", - "[START_DIR]/swarming_temp_dir/outputs/task-3", - "--merge-script", - "RECIPE_MODULE[build::swarming]/resources/noop_merge.py", - "--merge-additional-args", - "[]", - "--", - "python", - "-u", - "[START_DIR]/swarming.client/swarming.py", - "collect", - "--swarming", - "https://chromium-swarm.appspot.com", - "--decorate", - "--print-status-updates", - "--json", - "{\"base_task_name\": \"task-3/Linux/[dummy has\", \"tasks\": {\"task-3/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}", - "--task-summary-json", - "/path/to/tmp/json" - ], - "name": "task-3", - "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@", - "@@@STEP_LOG_LINE@json.output@{}@@@", - "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LOG_LINE@swarming.summary@{@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", "@@@STEP_LOG_LINE@swarming.summary@ {@@@", "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": null, @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", @@ -759,7 +558,7 @@ "@@@STEP_LOG_LINE@swarming.summary@ 0@@@", "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0000\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0100\", @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": {@@@", "@@@STEP_LOG_LINE@swarming.summary@ \"isolated\": \"abc123\", @@@", @@ -785,24 +584,20 @@ "@@@STEP_LOG_LINE@swarming.summary@}@@@", "@@@STEP_LOG_END@swarming.summary@@@", "@@@STEP_LINK@shard #0 isolated out@blah@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@" + "@@@STEP_LINK@shard #1 isolated out@blah@@@" ] }, { "cmd": [ "python", "-u", - "RECIPE_PACKAGE_REPO[build]/scripts/tools/runit.py", - "--show-path", - "--", - "python", - "RECIPE_MODULE[build::swarming]/resources/collect_task.py", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", "-o", "/path/to/tmp/json", "--task-output-dir", - "[START_DIR]/swarming_temp_dir/outputs/task-0", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", "--merge-script", - "RECIPE_MODULE[build::swarming]/resources/noop_merge.py", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", "--merge-additional-args", "[]", "--", @@ -811,17 +606,18 @@ "[START_DIR]/swarming.client/swarming.py", "collect", "--swarming", - "https://chromium-swarm.appspot.com", + "https://chromium-swarm-dev.appspot.com", "--decorate", "--print-status-updates", + "--verbose", "--json", - "{\"base_task_name\": \"task-0/Linux/[dummy has\", \"tasks\": {\"task-0/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}", + "{\"base_task_name\": \"hello_world/Mac-10.9/hash_for_m\", \"tasks\": {\"hello_world/Mac-10.9/hash_for_m\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", "--task-summary-json", "/path/to/tmp/json" ], - "name": "task-0", + "name": "hello_world on Mac-10.9", "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@", + "@@@STEP_TEXT@Run on OS: 'Mac-10.9'<br>swarming pending 71s@@@", "@@@STEP_LOG_LINE@json.output@{}@@@", "@@@STEP_LOG_END@json.output@@@", "@@@STEP_LOG_LINE@swarming.summary@{@@@", @@ -865,90 +661,19 @@ "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", "@@@STEP_LOG_LINE@swarming.summary@}@@@", "@@@STEP_LOG_END@swarming.summary@@@", - "@@@STEP_LINK@shard #0 isolated out@blah@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@" + "@@@STEP_LINK@shard #0 isolated out@blah@@@" ] }, { "cmd": [ "python", "-u", - "RECIPE_PACKAGE_REPO[build]/scripts/tools/runit.py", - "--show-path", - "--", - "python", - "RECIPE_MODULE[build::swarming]/resources/collect_task.py", - "-o", - "/path/to/tmp/json", - "--task-output-dir", - "[START_DIR]/swarming_temp_dir/outputs/task-1", - "--merge-script", - "RECIPE_MODULE[build::swarming]/resources/noop_merge.py", - "--merge-additional-args", - "[]", - "--", - "python", - "-u", - "[START_DIR]/swarming.client/swarming.py", - "collect", - "--swarming", - "https://chromium-swarm.appspot.com", - "--decorate", - "--print-status-updates", - "--json", - "{\"base_task_name\": \"task-1/Linux/[dummy has\", \"tasks\": {\"task-1/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}", - "--task-summary-json", - "/path/to/tmp/json" + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" ], - "name": "task-1", - "~followup_annotations": [ - "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@", - "@@@STEP_LOG_LINE@json.output@{}@@@", - "@@@STEP_LOG_END@json.output@@@", - "@@@STEP_LOG_LINE@swarming.summary@{@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", - "@@@STEP_LOG_LINE@swarming.summary@ {@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": null, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": [@@@", - "@@@STEP_LOG_LINE@swarming.summary@ 5.7, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ 31.5@@@", - "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [@@@", - "@@@STEP_LOG_LINE@swarming.summary@ 0, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ 0@@@", - "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0000\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": {@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"isolated\": \"abc123\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"namespace\": \"default-gzip\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", - "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"Heart beat succeeded on win32.\\n\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"Foo\"@@@", - "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"outputs_ref\": {@@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", - "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 112, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": 1, @@@", - "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", - "@@@STEP_LOG_LINE@swarming.summary@ }@@@", - "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", - "@@@STEP_LOG_LINE@swarming.summary@}@@@", - "@@@STEP_LOG_END@swarming.summary@@@", - "@@@STEP_LINK@shard #0 isolated out@blah@@@", - "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@" - ] + "infra_step": true, + "name": "rmtree remove temp dir" }, { "name": "$result", diff --git a/infra/bots/recipe_modules/swarming/example.expected/show_isolated_out_in_collect_step.json b/infra/bots/recipe_modules/swarming/example.expected/show_isolated_out_in_collect_step.json new file mode 100644 index 0000000000..d356498c03 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/show_isolated_out_in_collect_step.json @@ -0,0 +1,297 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "rietveld:https://codereview.chromium.org/123/#ps1001", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 5.7, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 31.5@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0000\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated\": \"abc123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"namespace\": \"default-gzip\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Heart beat succeeded on win32.\\n\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Foo\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs_ref\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 112, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": 1, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/show_shards_in_collect_step.json b/infra/bots/recipe_modules/swarming/example.expected/show_shards_in_collect_step.json new file mode 100644 index 0000000000..ddd8dd029d --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/show_shards_in_collect_step.json @@ -0,0 +1,299 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "rietveld:https://codereview.chromium.org/123/#ps1001", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 5.7, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 31.5@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0000\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated\": \"abc123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"namespace\": \"default-gzip\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Heart beat succeeded on win32.\\n\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Foo\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs_ref\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 112, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": 1, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@", + "@@@STEP_LINK@shard #0 isolated out@blah@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_new.json b/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_new.json new file mode 100644 index 0000000000..132e5b23a2 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_new.json @@ -0,0 +1,281 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0100\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": \"EXPIRED\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@", + "@@@STEP_LOG_LINE@no_results_exc@Infra Failure in Shard #0 failed: There isn't enough capacity to run your test@@@", + "@@@STEP_LOG_END@no_results_exc@@@", + "@@@STEP_EXCEPTION@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_old.json b/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_old.json new file mode 100644 index 0000000000..190094d84d --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/swarming_expired_old.json @@ -0,0 +1,281 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0100\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 48, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@", + "@@@STEP_LOG_LINE@no_results_exc@Infra Failure in Shard #0 failed: There isn't enough capacity to run your test@@@", + "@@@STEP_LOG_END@no_results_exc@@@", + "@@@STEP_EXCEPTION@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_new.json b/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_new.json new file mode 100644 index 0000000000..469aa3cb1c --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_new.json @@ -0,0 +1,278 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0100\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": \"TIMED_OUT\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_old.json b/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_old.json new file mode 100644 index 0000000000..e8fc09ee47 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/swarming_timeout_old.json @@ -0,0 +1,278 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0100\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 64, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.expected/trybot.json b/infra/bots/recipe_modules/swarming/example.expected/trybot.json new file mode 100644 index 0000000000..741efcc340 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/example.expected/trybot.json @@ -0,0 +1,298 @@ +[ + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py", + "--path", + "[START_DIR]/swarming.client", + "--url", + "https://chromium.googlesource.com/external/swarming.client.git" + ], + "name": "git setup (swarming_client)" + }, + { + "cmd": [ + "git", + "retry", + "fetch", + "origin", + "master" + ], + "cwd": "[START_DIR]/swarming.client", + "env": { + "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s" + }, + "infra_step": true, + "name": "git fetch (swarming_client)" + }, + { + "cmd": [ + "git", + "checkout", + "-f", + "FETCH_HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git checkout (swarming_client)" + }, + { + "cmd": [ + "git", + "rev-parse", + "HEAD" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "read revision", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@" + ] + }, + { + "cmd": [ + "git", + "clean", + "-f", + "-d", + "-x" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "git clean (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "sync" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule sync (swarming_client)" + }, + { + "cmd": [ + "git", + "submodule", + "update", + "--init", + "--recursive" + ], + "cwd": "[START_DIR]/swarming.client", + "infra_step": true, + "name": "submodule update (swarming_client)" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "--version" + ], + "name": "swarming.py --version", + "stdout": "/path/to/tmp/", + "~followup_annotations": [ + "@@@STEP_TEXT@0.8.6@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/isolate.py", + "archive", + "--isolate", + "[START_DIR]/swarming.client/example/payload/hello_world.isolate", + "--isolated", + "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--config-variable", + "OS", + "win", + "--verbose" + ], + "name": "archive for win", + "stdout": "/path/to/tmp/" + }, + { + "cmd": [ + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "trigger", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--isolate-server", + "https://isolateserver-dev.appspot.com", + "--priority", + "30", + "--shards", + "1", + "--task-name", + "hello_world/Windows-7-SP1/hash_for_w", + "--dump-json", + "/path/to/tmp/json", + "--expiration", + "3600", + "--io-timeout", + "1200", + "--hard-timeout", + "3600", + "--dimension", + "cpu", + "x86-64", + "--dimension", + "gpu", + "none", + "--dimension", + "os", + "Windows-7-SP1", + "--env", + "TESTING", + "1", + "--tag", + "data:hash_for_win", + "--tag", + "master:tryserver", + "--tag", + "name:hello_world", + "--tag", + "os:Windows-7-SP1", + "--tag", + "os:win", + "--tag", + "rietveld:https://codereview.chromium.org/123/#ps1001", + "--tag", + "stepname:hello_world on Windows-7-SP1", + "--verbose", + "--idempotent", + "--user", + "joe", + "--cipd-package", + "bin:super/awesome/pkg:git_revision:deadbeef", + "--isolated", + "hash_for_win" + ], + "infra_step": true, + "name": "[trigger] hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@", + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tasks\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"hello_world/Windows-7-SP1/hash_for_w\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"shard_index\": 0, @@@", + "@@@STEP_LOG_LINE@json.output@ \"task_id\": \"10000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@" + ] + }, + { + "cmd": [ + "echo", + "running something locally" + ], + "name": "local step" + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::swarming]/resources/collect_task.py", + "-o", + "/path/to/tmp/json", + "--task-output-dir", + "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir", + "--merge-script", + "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py", + "--merge-additional-args", + "[]", + "--", + "python", + "-u", + "[START_DIR]/swarming.client/swarming.py", + "collect", + "--swarming", + "https://chromium-swarm-dev.appspot.com", + "--decorate", + "--print-status-updates", + "--verbose", + "--json", + "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}", + "--task-summary-json", + "/path/to/tmp/json" + ], + "name": "hello_world on Windows-7-SP1", + "~followup_annotations": [ + "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@", + "@@@STEP_LOG_LINE@json.output@{}@@@", + "@@@STEP_LOG_END@json.output@@@", + "@@@STEP_LOG_LINE@swarming.summary@{@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"shards\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"abandoned_ts\": null, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"bot_id\": \"vm30\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"created_ts\": \"2014-09-25T01:41:00.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"durations\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 5.7, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 31.5@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"exit_codes\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ 0@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"id\": \"148aa78d7aa0000\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"internal_failure\": false, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated_out\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolated\": \"abc123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"namespace\": \"default-gzip\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"modified_ts\": \"2014-09-25 01:42:00\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs\": [@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Heart beat succeeded on win32.\\n\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"Foo\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ], @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"outputs_ref\": {@@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"view_url\": \"blah\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"started_ts\": \"2014-09-25T01:42:11.123\", @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"state\": 112, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"try_number\": 1, @@@", + "@@@STEP_LOG_LINE@swarming.summary@ \"user\": \"unknown\"@@@", + "@@@STEP_LOG_LINE@swarming.summary@ }@@@", + "@@@STEP_LOG_LINE@swarming.summary@ ]@@@", + "@@@STEP_LOG_LINE@swarming.summary@}@@@", + "@@@STEP_LOG_END@swarming.summary@@@", + "@@@STEP_LINK@shard #0 isolated out@blah@@@" + ] + }, + { + "cmd": [ + "python", + "-u", + "RECIPE_MODULE[skia::file]/resources/fileutil.py", + "rmtree", + "[TMP_BASE]/hello_isolated_world_tmp_1" + ], + "infra_step": true, + "name": "rmtree remove temp dir" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +]
\ No newline at end of file diff --git a/infra/bots/recipe_modules/swarming/example.py b/infra/bots/recipe_modules/swarming/example.py index b248410c5b..f61576a24d 100644 --- a/infra/bots/recipe_modules/swarming/example.py +++ b/infra/bots/recipe_modules/swarming/example.py @@ -1,31 +1,242 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2014 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. +# TODO(borenet): This module was copied from build.git and heavily modified to +# remove dependencies on other modules in build.git. It belongs in a different +# repo. Remove this once it has been moved. + + +import json + DEPS = [ + 'file', + 'isolate', + 'recipe_engine/json', 'recipe_engine/path', 'recipe_engine/properties', + 'recipe_engine/python', + 'recipe_engine/raw_io', 'recipe_engine/step', 'swarming', + 'swarming_client', ] +from recipe_engine.recipe_api import Property + +PROPERTIES = { + 'platforms': Property(default=('win',)), + 'show_isolated_out_in_collect_step': Property(default=True), + 'show_shards_in_collect_step': Property(default=False), + 'gtest_task': Property(default=False), + #'isolated_script_task': Property(default=False), + 'merge': Property(default=None), +} + +def RunSteps(api, platforms, show_isolated_out_in_collect_step, + show_shards_in_collect_step, gtest_task, merge): + # Checkout swarming client. + api.swarming_client.checkout('master') + + # Ensure swarming_client version is fresh enough. + api.swarming.check_client_version(step_test_data=(0, 8, 6)) + + # Configure isolate & swarming modules (this is optional). + api.isolate.isolate_server = 'https://isolateserver-dev.appspot.com' + api.swarming.swarming_server = 'https://chromium-swarm-dev.appspot.com' + api.swarming.add_default_tag('master:tryserver') + api.swarming.default_expiration = 60*60 + api.swarming.default_hard_timeout = 60*60 + api.swarming.default_io_timeout = 20*60 + api.swarming.default_idempotent = True + api.swarming.default_priority = 30 + api.swarming.default_user = 'joe' + api.swarming.set_default_env('TESTING', '1') + api.swarming.verbose = True + + api.swarming.set_default_dimension('inexistent', None) + + api.swarming.show_shards_in_collect_step = show_shards_in_collect_step + api.swarming.show_isolated_out_in_collect_step = ( + show_isolated_out_in_collect_step) + + try: + # Testing ReadOnlyDict.__setattr__() coverage. + api.swarming.default_dimensions['invalid'] = 'foo' + except TypeError: + pass + try: + api.swarming.default_env['invalid'] = 'foo' + except TypeError: + pass + + # Create a temp dir to put *.isolated files into. + temp_dir = api.path.mkdtemp('hello_isolated_world') + + # Prepare a bunch of swarming tasks to run hello_world on multiple platforms. + tasks = [] + for platform in platforms: + # Isolate example hello_world.isolate from swarming client repo. + # TODO(vadimsh): Add a thin wrapper around isolate.py to 'isolate' module? + step_result = api.python( + 'archive for %s' % platform, + api.swarming_client.path.join('isolate.py'), + [ + 'archive', + '--isolate', api.swarming_client.path.join( + 'example', 'payload', 'hello_world.isolate'), + '--isolated', temp_dir.join('hello_world.isolated'), + '--isolate-server', api.isolate.isolate_server, + '--config-variable', 'OS', platform, + '--verbose', + ], stdout=api.raw_io.output_text()) + # TODO(vadimsh): Pass result from isolate.py though --output-json option. + isolated_hash = step_result.stdout.split()[0].strip() + + # Create a task to run the isolated file on swarming, set OS dimension. + # Also generate code coverage for multi-shard case by triggering multiple + # shards on Linux. + task = api.swarming.task('hello_world', isolated_hash, + task_output_dir=temp_dir.join('task_output_dir')) + task.dimensions['os'] = api.swarming.prefered_os_dimension(platform) + task.shards = 2 if platform == 'linux' else 1 + task.tags.add('os:' + platform) + if api.swarming_client.get_script_version('swarming.py') >= (0, 8, 6): + task.cipd_packages = [ + ('bin', 'super/awesome/pkg', 'git_revision:deadbeef')] + tasks.append(task) + + # Launch all tasks. + for task in tasks: + step_result = api.swarming.trigger_task(task) + assert step_result.swarming_task in tasks + + # Recipe can do something useful here locally while tasks are + # running on swarming. + api.step('local step', ['echo', 'running something locally']) + + # Wait for all tasks to complete. + for task in tasks: + step_result = api.swarming.collect_task(task) + data = step_result.swarming.summary + + state = data['shards'][0]['state'] + if api.swarming.State.COMPLETED == state: + state_name = api.swarming.State.to_string(state) + assert 'Completed' == state_name, state_name + assert step_result.swarming_task in tasks -def RunSteps(api): - api.swarming.setup('mydir', swarming_rev='abc123') - api.swarming.create_isolated_gen_json( - 'isolate_path', 'isolate_dir', 'linux', 'task', {'myvar': 'myval'}, - blacklist=['*.pyc']) - tasks_to_hashes = api.swarming.batcharchive(targets=[ - 'task-%s' % num for num in range(5)]) - tasks = api.swarming.trigger_swarming_tasks( - tasks_to_hashes, dimensions={'os': 'Linux'}, extra_args=['--extra']) - for t in tasks: - api.swarming.collect_swarming_task(t) + # Cleanup. + api.file.rmtree('remove temp dir', temp_dir) def GenTests(api): yield ( - api.test('test') + - api.properties(revision='abc123') - ) + api.test('basic') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.step_data( + 'archive for linux', + stdout=api.raw_io.output_text( + 'hash_for_linux hello_world.isolated')) + + api.step_data( + 'archive for mac', + stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')) + + api.properties(platforms=('win', 'linux', 'mac'))) + + yield ( + api.test('trybot') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.properties( + rietveld='https://codereview.chromium.org', + issue='123', + patchset='1001')) + + yield ( + api.test('show_shards_in_collect_step') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.properties( + rietveld='https://codereview.chromium.org', + issue='123', + patchset='1001', + show_shards_in_collect_step=True)) + + yield ( + api.test('show_isolated_out_in_collect_step') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.properties( + rietveld='https://codereview.chromium.org', + issue='123', + patchset='1001', + show_isolated_out_in_collect_step=False)) + + data = { + 'shards': [ + { + '': '', + } + ] + } + + data = { + 'shards': [ + { + 'abandoned_ts': '2014-09-25T01:41:00.123', + 'bot_id': 'vm30', + 'completed_ts': None, + 'created_ts': '2014-09-25T01:41:00.123', + 'durations': None, + 'exit_codes': [], + 'failure': False, + 'id': '148aa78d7aa0100', + 'internal_failure': False, + 'isolated_out': None, + 'modified_ts': '2014-09-25 01:42:00', + 'name': 'heartbeat-canary-2014-09-25_01:41:55-os=Windows', + 'outputs': [], + 'started_ts': '2014-09-25T01:42:11.123', + 'state': 0x30, # EXPIRED (old) + 'try_number': None, + 'user': 'unknown', + } + ], + } + + yield ( + api.test('swarming_expired_old') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data))) + + data['shards'][0]['state'] = 'EXPIRED' + yield ( + api.test('swarming_expired_new') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data))) + + data['shards'][0]['state'] = 0x40 # TIMED_OUT (old) + yield ( + api.test('swarming_timeout_old') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data))) + + data['shards'][0]['state'] = 'TIMED_OUT' # TIMED_OUT (old) + yield ( + api.test('swarming_timeout_new') + + api.step_data( + 'archive for win', + stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) + + api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data))) diff --git a/infra/bots/recipe_modules/swarming/resources/collect_task.py b/infra/bots/recipe_modules/swarming/resources/collect_task.py new file mode 100755 index 0000000000..cb4f15e96a --- /dev/null +++ b/infra/bots/recipe_modules/swarming/resources/collect_task.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# Copyright 2017 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. + +import argparse +import json +import logging +import os +import subprocess +import sys + + +def collect_task( + collect_cmd, merge_script, build_properties, merge_arguments, + task_output_dir, output_json): + """Collect and merge the results of a task. + + This is a relatively thin wrapper script around a `swarming.py collect` + command and a subsequent results merge to ensure that the recipe system + treats them as a single step. The results merge can either be the default + one provided by results_merger or a python script provided as merge_script. + + Args: + collect_cmd: The `swarming.py collect` command to run. Should not contain + a --task-output-dir argument. + merge_script: A merge/postprocessing script that should be run to + merge the results. This script will be invoked as + + <merge_script> \ + [--build-properties <string JSON>] \ + [merge arguments...] \ + --summary-json <summary json> \ + -o <merged json path> \ + <shard json>... + + where the merge arguments are the contents of merge_arguments_json. + build_properties: A string containing build information to + pass to the merge script in JSON form. + merge_arguments: A string containing additional arguments to pass to + the merge script in JSON form. + task_output_dir: A path to a directory in which swarming will write the + output of the task, including a summary JSON and all of the individual + shard results. + output_json: A path to a JSON file to which the merged results should be + written. The merged results should be in the JSON Results File Format + (https://www.chromium.org/developers/the-json-test-results-format) + and may optionally contain a top level "links" field that may contain a + dict mapping link text to URLs, for a set of links that will be included + in the buildbot output. + Returns: + The exit code of collect_cmd or merge_cmd. + """ + logging.debug('Using task_output_dir: %r', task_output_dir) + if os.path.exists(task_output_dir): + logging.warn('task_output_dir %r already exists!', task_output_dir) + existing_contents = [] + try: + for p in os.listdir(task_output_dir): + existing_contents.append(os.path.join(task_output_dir, p)) + except (OSError, IOError) as e: + logging.error('Error while examining existing task_output_dir: %s', e) + + logging.warn('task_output_dir existing content: %r', existing_contents) + + collect_cmd.extend(['--task-output-dir', task_output_dir]) + + logging.info('collect_cmd: %s', ' '.join(collect_cmd)) + collect_result = subprocess.call(collect_cmd) + if collect_result != 0: + logging.warn('collect_cmd had non-zero return code: %s', collect_result) + + task_output_dir_contents = [] + try: + task_output_dir_contents.extend( + os.path.join(task_output_dir, p) + for p in os.listdir(task_output_dir)) + except (OSError, IOError) as e: + logging.error('Error while processing task_output_dir: %s', e) + + logging.debug('Contents of task_output_dir: %r', task_output_dir_contents) + if not task_output_dir_contents: + logging.warn( + 'No files found in task_output_dir: %r', + task_output_dir) + + task_output_subdirs = ( + p for p in task_output_dir_contents + if os.path.isdir(p)) + shard_json_files = [ + os.path.join(subdir, 'output.json') + for subdir in task_output_subdirs] + extant_shard_json_files = [ + f for f in shard_json_files if os.path.exists(f)] + + if shard_json_files != extant_shard_json_files: + logging.warn( + 'Expected output.json file missing: %r\nFound: %r\nExpected: %r\n', + set(shard_json_files) - set(extant_shard_json_files), + extant_shard_json_files, + shard_json_files) + + if not extant_shard_json_files: + logging.warn( + 'No shard json files found in task_output_dir: %r\nFound %r', + task_output_dir, task_output_dir_contents) + + logging.debug('Found shard_json_files: %r', shard_json_files) + + summary_json_file = os.path.join(task_output_dir, 'summary.json') + + merge_result = 0 + + merge_cmd = [sys.executable, merge_script] + if build_properties: + merge_cmd.extend(('--build-properties', build_properties)) + if os.path.exists(summary_json_file): + merge_cmd.extend(('--summary-json', summary_json_file)) + else: + logging.warn('Summary json file missing: %r', summary_json_file) + if merge_arguments: + merge_cmd.extend(json.loads(merge_arguments)) + merge_cmd.extend(('-o', output_json)) + merge_cmd.extend(extant_shard_json_files) + + logging.info('merge_cmd: %s', ' '.join(merge_cmd)) + merge_result = subprocess.call(merge_cmd) + if merge_result != 0: + logging.warn('merge_cmd had non-zero return code: %s', merge_result) + + if not os.path.exists(output_json): + logging.warn( + 'merge_cmd did not create output_json file: %r', output_json) + + return collect_result or merge_result + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--build-properties') + parser.add_argument('--merge-additional-args') + parser.add_argument('--merge-script', required=True) + parser.add_argument('--task-output-dir', required=True) + parser.add_argument('-o', '--output-json', required=True) + parser.add_argument('--verbose', action='store_true') + parser.add_argument('collect_cmd', nargs='+') + + args = parser.parse_args() + if args.verbose: + logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + + return collect_task( + args.collect_cmd, + args.merge_script, args.build_properties, args.merge_additional_args, + args.task_output_dir, args.output_json) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/infra/bots/recipe_modules/swarming/resources/noop_merge.py b/infra/bots/recipe_modules/swarming/resources/noop_merge.py new file mode 100755 index 0000000000..740e0d3b32 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/resources/noop_merge.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright 2017 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. + +import argparse +import json +import shutil +import sys + + +def noop_merge(output_json, jsons_to_merge): + """Use the first supplied JSON as the output JSON. + + Primarily intended for unsharded tasks. + + Args: + output_json: A path to a JSON file to which the results should be written. + jsons_to_merge: A list of paths to JSON files. + """ + if len(jsons_to_merge) > 1: + print >> sys.stderr, ( + 'Multiple JSONs provided: %s' % ','.join(jsons_to_merge)) + return 1 + if jsons_to_merge: + shutil.copyfile(jsons_to_merge[0], output_json) + else: + with open(output_json, 'w') as f: + json.dump({}, f) + return 0 + + +def main(raw_args): + parser = argparse.ArgumentParser() + parser.add_argument('--build-properties', help=argparse.SUPPRESS) + parser.add_argument('--summary-json', help=argparse.SUPPRESS) + parser.add_argument('-o', '--output-json', required=True) + parser.add_argument('jsons_to_merge', nargs='*') + + args = parser.parse_args(raw_args) + + return noop_merge(args.output_json, args.jsons_to_merge) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/infra/bots/recipe_modules/swarming/resources/results_merger.py b/infra/bots/recipe_modules/swarming/resources/results_merger.py new file mode 100755 index 0000000000..3ea045345e --- /dev/null +++ b/infra/bots/recipe_modules/swarming/resources/results_merger.py @@ -0,0 +1,278 @@ +# Copyright 2016 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. + +import copy +import json +import sys + +# These fields must appear in the test result output +REQUIRED = { + 'interrupted', + 'num_failures_by_type', + 'seconds_since_epoch', + 'tests', + } + +# These fields are optional, but must have the same value on all shards +OPTIONAL_MATCHING = ( + 'builder_name', + 'build_number', + 'chromium_revision', + 'has_pretty_patch', + 'has_wdiff', + 'path_delimiter', + 'pixel_tests_enabled', + 'random_order_seed', + ) + +OPTIONAL_IGNORED = ( + 'layout_tests_dir', + ) + +# These fields are optional and will be summed together +OPTIONAL_COUNTS = ( + 'fixable', + 'num_flaky', + 'num_passes', + 'num_regressions', + 'skipped', + 'skips', + ) + + +class MergeException(Exception): + pass + + +def merge_test_results(shard_results_list): + """ Merge list of results. + + Args: + shard_results_list: list of results to merge. All the results must have the + same format. Supported format are simplified JSON format & Chromium JSON + test results format version 3 (see + https://www.chromium.org/developers/the-json-test-results-format) + + Returns: + a dictionary that represent the merged results. Its format follow the same + format of all results in |shard_results_list|. + """ + if not shard_results_list: + return {} + + if 'seconds_since_epoch' in shard_results_list[0]: + return _merge_json_test_result_format(shard_results_list) + else: + return _merge_simplified_json_format(shard_results_list) + + +def _merge_simplified_json_format(shard_results_list): + # This code is specialized to the "simplified" JSON format that used to be + # the standard for recipes. + + # These are the only keys we pay attention to in the output JSON. + merged_results = { + 'successes': [], + 'failures': [], + 'valid': True, + } + + for result_json in shard_results_list: + successes = result_json.get('successes', []) + failures = result_json.get('failures', []) + valid = result_json.get('valid', True) + + if (not isinstance(successes, list) or not isinstance(failures, list) or + not isinstance(valid, bool)): + raise MergeException( + 'Unexpected value type in %s' % result_json) # pragma: no cover + + merged_results['successes'].extend(successes) + merged_results['failures'].extend(failures) + merged_results['valid'] = merged_results['valid'] and valid + return merged_results + + +def _merge_json_test_result_format(shard_results_list): + # This code is specialized to the Chromium JSON test results format version 3: + # https://www.chromium.org/developers/the-json-test-results-format + + # These are required fields for the JSON test result format version 3. + merged_results = { + 'tests': {}, + 'interrupted': False, + 'version': 3, + 'seconds_since_epoch': float('inf'), + 'num_failures_by_type': { + } + } + + # To make sure that we don't mutate existing shard_results_list. + shard_results_list = copy.deepcopy(shard_results_list) + for result_json in shard_results_list: + # TODO(tansell): check whether this deepcopy is actually neccessary. + result_json = copy.deepcopy(result_json) + + # Check the version first + version = result_json.pop('version', -1) + if version != 3: + raise MergeException( # pragma: no cover (covered by + # results_merger_unittest). + 'Unsupported version %s. Only version 3 is supported' % version) + + # Check the results for each shard have the required keys + missing = REQUIRED - set(result_json) + if missing: + raise MergeException( # pragma: no cover (covered by + # results_merger_unittest). + 'Invalid json test results (missing %s)' % missing) + + # Curry merge_values for this result_json. + merge = lambda key, merge_func: merge_value( + result_json, merged_results, key, merge_func) + + # Traverse the result_json's test trie & merged_results's test tries in + # DFS order & add the n to merged['tests']. + merge('tests', merge_tries) + + # If any were interrupted, we are interrupted. + merge('interrupted', lambda x,y: x|y) + + # Use the earliest seconds_since_epoch value + merge('seconds_since_epoch', min) + + # Sum the number of failure types + merge('num_failures_by_type', sum_dicts) + + # Optional values must match + for optional_key in OPTIONAL_MATCHING: + if optional_key not in result_json: + continue + + if optional_key not in merged_results: + # Set this value to None, then blindly copy over it. + merged_results[optional_key] = None + merge(optional_key, lambda src, dst: src) + else: + merge(optional_key, ensure_match) + + # Optional values ignored + for optional_key in OPTIONAL_IGNORED: + if optional_key in result_json: + merged_results[optional_key] = result_json.pop( + # pragma: no cover (covered by + # results_merger_unittest). + optional_key) + + # Sum optional value counts + for count_key in OPTIONAL_COUNTS: + if count_key in result_json: # pragma: no cover + # TODO(mcgreevy): add coverage. + merged_results.setdefault(count_key, 0) + merge(count_key, lambda a, b: a+b) + + if result_json: + raise MergeException( # pragma: no cover (covered by + # results_merger_unittest). + 'Unmergable values %s' % result_json.keys()) + + return merged_results + + +def merge_tries(source, dest): + """ Merges test tries. + + This is intended for use as a merge_func parameter to merge_value. + + Args: + source: A result json test trie. + dest: A json test trie merge destination. + """ + # merge_tries merges source into dest by performing a lock-step depth-first + # traversal of dest and source. + # pending_nodes contains a list of all sub-tries which have been reached but + # need further merging. + # Each element consists of a trie prefix, and a sub-trie from each of dest + # and source which is reached via that prefix. + pending_nodes = [('', dest, source)] + while pending_nodes: + prefix, dest_node, curr_node = pending_nodes.pop() + for k, v in curr_node.iteritems(): + if k in dest_node: + if not isinstance(v, dict): + raise MergeException( + "%s:%s: %r not mergable, curr_node: %r\ndest_node: %r" % ( + prefix, k, v, curr_node, dest_node)) + pending_nodes.append(("%s:%s" % (prefix, k), dest_node[k], v)) + else: + dest_node[k] = v + return dest + + +def ensure_match(source, dest): + """ Returns source if it matches dest. + + This is intended for use as a merge_func parameter to merge_value. + + Raises: + MergeException if source != dest + """ + if source != dest: + raise MergeException( # pragma: no cover (covered by + # results_merger_unittest). + "Values don't match: %s, %s" % (source, dest)) + return source + + +def sum_dicts(source, dest): + """ Adds values from source to corresponding values in dest. + + This is intended for use as a merge_func parameter to merge_value. + """ + for k, v in source.iteritems(): + dest.setdefault(k, 0) + dest[k] += v + + return dest + + +def merge_value(source, dest, key, merge_func): + """ Merges a value from source to dest. + + The value is deleted from source. + + Args: + source: A dictionary from which to pull a value, identified by key. + dest: The dictionary into to which the value is to be merged. + key: The key which identifies the value to be merged. + merge_func(src, dst): A function which merges its src into dst, + and returns the result. May modify dst. May raise a MergeException. + + Raises: + MergeException if the values can not be merged. + """ + try: + dest[key] = merge_func(source[key], dest[key]) + except MergeException as e: + e.message = "MergeFailure for %s\n%s" % (key, e.message) + e.args = tuple([e.message] + list(e.args[1:])) + raise + del source[key] + + +def main(files): + if len(files) < 2: + sys.stderr.write("Not enough JSON files to merge.\n") + return 1 + sys.stderr.write('Starting with %s\n' % files[0]) + result = json.load(open(files[0])) + for f in files[1:]: + sys.stderr.write('Merging %s\n' % f) + result = merge_test_results([result, json.load(open(f))]) + print json.dumps(result) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py b/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py new file mode 100755 index 0000000000..ca3abcf004 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# Copyright 2017 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. + +import argparse +import json +import os +import shutil +import sys +import tempfile +import traceback + +from common import gtest_utils +from slave import annotation_utils +from slave import slave_utils + + +MISSING_SHARDS_MSG = r"""Missing results from the following shard(s): %s + +This can happen in following cases: + * Test failed to start (missing *.dll/*.so dependency for example) + * Test crashed or hung + * Task expired because there are not enough bots available and are all used + * Swarming service experienced problems + +Please examine logs to figure out what happened. +""" + + +def emit_warning(title, log=None): + print '@@@STEP_WARNINGS@@@' + print title + if log: + slave_utils.WriteLogLines(title, log.split('\n')) + + +def merge_shard_results(summary_json, jsons_to_merge): + """Reads JSON test output from all shards and combines them into one. + + Returns dict with merged test output on success or None on failure. Emits + annotations. + """ + # summary.json is produced by swarming.py itself. We are mostly interested + # in the number of shards. + try: + with open(summary_json) as f: + summary = json.load(f) + except (IOError, ValueError): + emit_warning( + 'summary.json is missing or can not be read', + 'Something is seriously wrong with swarming_client/ or the bot.') + return None + + # Merge all JSON files together. Keep track of missing shards. + merged = { + 'all_tests': set(), + 'disabled_tests': set(), + 'global_tags': set(), + 'missing_shards': [], + 'per_iteration_data': [], + 'swarming_summary': summary, + } + for index, result in enumerate(summary['shards']): + if result is not None: + # Author note: this code path doesn't trigger convert_to_old_format() in + # client/swarming.py, which means the state enum is saved in its string + # name form, not in the number form. + state = result.get('state') + if state == u'BOT_DIED': + emit_warning('Shard #%d had a Swarming internal failure' % index) + elif state == u'EXPIRED': + emit_warning('There wasn\'t enough capacity to run your test') + elif state == u'TIMED_OUT': + emit_warning( + 'Test runtime exceeded allocated time', + 'Either it ran for too long (hard timeout) or it didn\'t produce ' + 'I/O for an extended period of time (I/O timeout)') + elif state == u'COMPLETED': + json_data, err_msg = load_shard_json(index, jsons_to_merge) + if json_data: + # Set-like fields. + for key in ('all_tests', 'disabled_tests', 'global_tags'): + merged[key].update(json_data.get(key), []) + + # 'per_iteration_data' is a list of dicts. Dicts should be merged + # together, not the 'per_iteration_data' list itself. + merged['per_iteration_data'] = merge_list_of_dicts( + merged['per_iteration_data'], + json_data.get('per_iteration_data', [])) + continue + else: + emit_warning('Task ran but no result was found: %s' % err_msg) + else: + emit_warning('Invalid Swarming task state: %s' % state) + merged['missing_shards'].append(index) + + # If some shards are missing, make it known. Continue parsing anyway. Step + # should be red anyway, since swarming.py return non-zero exit code in that + # case. + if merged['missing_shards']: + as_str = ', '.join(map(str, merged['missing_shards'])) + emit_warning( + 'some shards did not complete: %s' % as_str, + MISSING_SHARDS_MSG % as_str) + # Not all tests run, combined JSON summary can not be trusted. + merged['global_tags'].add('UNRELIABLE_RESULTS') + + # Convert to jsonish dict. + for key in ('all_tests', 'disabled_tests', 'global_tags'): + merged[key] = sorted(merged[key]) + return merged + + +OUTPUT_JSON_SIZE_LIMIT = 100 * 1024 * 1024 # 100 MB + + +def load_shard_json(index, jsons_to_merge): + """Reads JSON output of the specified shard. + + Args: + output_dir: The directory in which to look for the JSON output to load. + index: The index of the shard to load data for. + + Returns: A tuple containing: + * The contents of path, deserialized into a python object. + * An error string. + (exactly one of the tuple elements will be non-None). + """ + # 'output.json' is set in swarming/api.py, gtest_task method. + matching_json_files = [ + j for j in jsons_to_merge + if (os.path.basename(j) == 'output.json' + and os.path.basename(os.path.dirname(j)) == str(index))] + + if not matching_json_files: + print >> sys.stderr, 'shard %s test output missing' % index + return (None, 'shard %s test output was missing' % index) + elif len(matching_json_files) > 1: + print >> sys.stderr, 'duplicate test output for shard %s' % index + return (None, 'shard %s test output was duplicated' % index) + + path = matching_json_files[0] + + try: + filesize = os.stat(path).st_size + if filesize > OUTPUT_JSON_SIZE_LIMIT: + print >> sys.stderr, 'output.json is %d bytes. Max size is %d' % ( + filesize, OUTPUT_JSON_SIZE_LIMIT) + return (None, 'shard %s test output exceeded the size limit' % index) + + with open(path) as f: + return (json.load(f), None) + except (IOError, ValueError, OSError) as e: + print >> sys.stderr, 'Missing or invalid gtest JSON file: %s' % path + print >> sys.stderr, '%s: %s' % (type(e).__name__, e) + + return (None, 'shard %s test output was missing or invalid' % index) + + +def merge_list_of_dicts(left, right): + """Merges dicts left[0] with right[0], left[1] with right[1], etc.""" + output = [] + for i in xrange(max(len(left), len(right))): + left_dict = left[i] if i < len(left) else {} + right_dict = right[i] if i < len(right) else {} + merged_dict = left_dict.copy() + merged_dict.update(right_dict) + output.append(merged_dict) + return output + + +def standard_gtest_merge( + output_json, summary_json, jsons_to_merge): + + output = merge_shard_results(summary_json, jsons_to_merge) + with open(output_json, 'wb') as f: + json.dump(output, f) + + return 0 + + +def main(raw_args): + + parser = argparse.ArgumentParser() + parser.add_argument('--build-properties') + parser.add_argument('--summary-json') + parser.add_argument('-o', '--output-json', required=True) + parser.add_argument('jsons_to_merge', nargs='*') + + args = parser.parse_args(raw_args) + + return standard_gtest_merge( + args.output_json, args.summary_json, args.jsons_to_merge) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py b/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py new file mode 100755 index 0000000000..e3c860f433 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# Copyright 2017 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. + +import argparse +import json +import sys + +import results_merger + + +def StandardIsolatedScriptMerge(output_json, jsons_to_merge): + """Merge the contents of one or more results JSONs into a single JSON. + + Args: + output_json: A path to a JSON file to which the merged results should be + written. + jsons_to_merge: A list of paths to JSON files that should be merged. + """ + shard_results_list = [] + for j in jsons_to_merge: + with open(j) as f: + shard_results_list.append(json.load(f)) + merged_results = results_merger.merge_test_results(shard_results_list) + + with open(output_json, 'w') as f: + json.dump(merged_results, f) + + return 0 + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output-json', required=True) + parser.add_argument('--build-properties', help=argparse.SUPPRESS) + parser.add_argument('--summary-json', help=argparse.SUPPRESS) + parser.add_argument('jsons_to_merge', nargs='*') + + args = parser.parse_args() + return StandardIsolatedScriptMerge(args.output_json, args.jsons_to_merge) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/infra/bots/recipe_modules/swarming/state.py b/infra/bots/recipe_modules/swarming/state.py new file mode 100644 index 0000000000..3834b901c0 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/state.py @@ -0,0 +1,46 @@ +# Copyright 2017 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. + + +# TODO(borenet): This module was copied from build.git and heavily modified to +# remove dependencies on other modules in build.git. It belongs in a different +# repo. Remove this once it has been moved. + + +class State(object): + """Copied from appengine/swarming/server/task_result.py. + + KEEP IN SYNC. + + Used to parse the 'state' value in task result. + """ + RUNNING = 0x10 # 16 + PENDING = 0x20 # 32 + EXPIRED = 0x30 # 48 + TIMED_OUT = 0x40 # 64 + BOT_DIED = 0x50 # 80 + CANCELED = 0x60 # 96 + COMPLETED = 0x70 # 112 + + STATES = ( + RUNNING, PENDING, EXPIRED, TIMED_OUT, BOT_DIED, CANCELED, COMPLETED) + STATES_RUNNING = (RUNNING, PENDING) + STATES_NOT_RUNNING = (EXPIRED, TIMED_OUT, BOT_DIED, CANCELED, COMPLETED) + STATES_DONE = (TIMED_OUT, COMPLETED) + STATES_ABANDONED = (EXPIRED, BOT_DIED, CANCELED) + + _NAMES = { + RUNNING: 'Running', + PENDING: 'Pending', + EXPIRED: 'Expired', + TIMED_OUT: 'Execution timed out', + BOT_DIED: 'Bot died', + CANCELED: 'User canceled', + COMPLETED: 'Completed', + } + + @classmethod + def to_string(cls, state): + """Returns a user-readable string representing a State.""" + return cls._NAMES[state] diff --git a/infra/bots/recipe_modules/swarming/test_api.py b/infra/bots/recipe_modules/swarming/test_api.py new file mode 100644 index 0000000000..3066fd6189 --- /dev/null +++ b/infra/bots/recipe_modules/swarming/test_api.py @@ -0,0 +1,56 @@ +# Copyright 2017 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. + + +# TODO(borenet): This module was copied from build.git and heavily modified to +# remove dependencies on other modules in build.git. It belongs in a different +# repo. Remove this once it has been moved. + + +from recipe_engine import recipe_test_api + +import state + +class SwarmingTestApi(recipe_test_api.RecipeTestApi): + + @recipe_test_api.placeholder_step_data + def summary(self, data): + return self.m.json.output(data) + + def canned_summary_output( + self, shards=1, failure=False, internal_failure=False): + return self.summary({ + 'shards': [ + { + 'abandoned_ts': None, + 'bot_id': 'vm30', + 'completed_ts': '2014-09-25T01:42:00.123', + 'created_ts': '2014-09-25T01:41:00.123', + 'durations': [5.7, 31.5], + 'exit_codes': [0, 0], + 'failure': failure, + 'id': '148aa78d7aa%02d00' % i, + 'internal_failure': internal_failure, + 'isolated_out': { + 'isolated': 'abc123', + 'isolatedserver': 'https://isolateserver.appspot.com', + 'namespace': 'default-gzip', + 'view_url': 'blah', + }, + 'modified_ts': '2014-09-25 01:42:00', + 'name': 'heartbeat-canary-2014-09-25_01:41:55-os=Windows', + 'outputs': [ + 'Heart beat succeeded on win32.\n', + 'Foo', + ], + 'outputs_ref': { + 'view_url': 'blah', + }, + 'started_ts': '2014-09-25T01:42:11.123', + 'state': state.State.COMPLETED, + 'try_number': 1, + 'user': 'unknown', + } for i in xrange(shards) + ], + }) |