blob: dd2eb4b75f459f6db7e7c8a70e542548e47ddb5e [file] [log] [blame]
#!/usr/bin/env python
# coding=utf-8
# Copyright 2017 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
"""Reproduce one or multiple tasks from one Swarming server on another one."""
import argparse
import json
import logging
import os
import subprocess
import sys
import tempfile
import urllib
CLIENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(
__file__.decode(sys.getfilesystemencoding()))))
def capture_swarming(server, cmd, *args):
cmd = [
sys.executable,
os.path.join(CLIENT_DIR, 'swarming.py'), cmd, '-S', server,
]
cmd.extend(args)
logging.info('%s', ' '.join(args))
return subprocess.check_output(cmd)
def query_swarming(server, *args):
return json.loads(capture_swarming(server, 'query', '--limit', '0', *args))
def get_all_by_tags(server, tags):
# For example:
# master:tryserver.chromium.win
# buildername:win_chromium_rel_ng
# buildnumber:516399
url = 'tasks/list?' + '&'.join('tags=' + urllib.quote(t) for t in tags)
tasks = query_swarming(server, url)['items']
print('Found %d original tasks' % len(tasks))
return [t['task_id'] for t in tasks]
def reproduce(server_old, server_new, task_id):
request = query_swarming(server_old, 'task/%s/request' % task_id)
logging.debug('%s', json.dumps(request, indent=2, sort_keys=True))
prop = request['properties']
inputs = prop['inputs_ref']
name = request['name']
if prop.get('secret_bytes'):
print >> sys.stderr, 'Can\'t reproduce task %s with secrets' % task_id
sys.exit(1)
cmd = [
'trigger', '--tag', 'testing:1',
'-I', inputs['isolatedserver'],
'--namespace', inputs['namespace'],
'-s', inputs['isolated'],
'--task-name', name,
'--priority', '10',
'--expiration', request['expiration_secs'],
'--hard-timeout', prop['execution_timeout_secs'],
'--io-timeout', prop['io_timeout_secs'],
]
if request.get('service_account'):
cmd.extend(('--service-account', request['service_account']))
for d in prop['dimensions']:
cmd.extend(('-d', d['key'], d['value']))
for d in prop.get('env', []):
cmd.extend(('--env', d['key'], d['value']))
for d in prop.get('env_prefix', []):
cmd.extend(('--env-prefix', d['key'], d['value']))
for o in prop.get('outputs', []):
cmd.extend(('--output', o))
for d in prop.get('caches', []):
cmd.extend(('--named-cache', d['name'], d['path']))
for p in prop.get('cipd_input', {}).get('packages', []):
# server and client_package in cipd_input are not reused.
cmd.extend((
'--cipd-package',
'%s:%s:%s' % (p['path'], p['package_name'], p['version'])))
h, tmp = tempfile.mkstemp(prefix='reproduce', suffix='.json')
os.close(h)
try:
cmd.extend(('--dump-json', tmp))
if prop['extra_args']:
cmd.append('--')
cmd.extend(prop['extra_args'])
if prop.get('command'):
cmd.append('--raw-cmd')
cmd.append('--')
cmd.extend(prop['command'])
capture_swarming(server_new, *cmd)
with open(tmp) as f:
return json.load(f)['tasks'].items()[0][1]['task_id'], name
finally:
os.remove(tmp)
def cut(n):
if len(n) <= 80:
return n
return n[:79] + u'…'
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--old', required=True, help='Old Swarming server to take the task from')
parser.add_argument(
'--new',
help='New Swarming server to run the task on; if not specified, --old '
'is used')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--task-id', action='append', default=[], help='Task IDs to reproduce')
group.add_argument(
'--tags', action='append', default=[],
help='Tags to query tasks to reproduce')
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.ERROR)
if args.tags:
args.task_id = get_all_by_tags(args.old, args.tags)
if len(args.task_id) == 1:
task_id, name = reproduce(args.old, args.new or args.old, args.task_id[0])
print(u'%s %s' % (task_id, cut(name)))
return 0
for i, task_id in enumerate(args.task_id):
task_id, name = reproduce(args.old, args.new or args.old, task_id)
print(u'%d/%d: %s %s' % (i+1, len(args.task_id), task_id, cut(name)))
return 0
if __name__ == '__main__':
sys.exit(main())