| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The LUCI Authors. All rights reserved. | 2 # Copyright 2013 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 """Client tool to trigger tasks or retrieve results from a Swarming server.""" | 6 """Client tool to trigger tasks or retrieve results from a Swarming server.""" |
| 7 | 7 |
| 8 __version__ = '0.8.8' | 8 __version__ = '0.8.9' |
| 9 | 9 |
| 10 import collections | 10 import collections |
| 11 import datetime | 11 import datetime |
| 12 import json | 12 import json |
| 13 import logging | 13 import logging |
| 14 import optparse | 14 import optparse |
| 15 import os | 15 import os |
| 16 import subprocess | 16 import subprocess |
| 17 import sys | 17 import sys |
| 18 import threading | 18 import threading |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 'isolated', | 158 'isolated', |
| 159 'isolatedserver', | 159 'isolatedserver', |
| 160 'namespace', | 160 'namespace', |
| 161 ]) | 161 ]) |
| 162 | 162 |
| 163 | 163 |
| 164 # See ../appengine/swarming/swarming_rpcs.py. | 164 # See ../appengine/swarming/swarming_rpcs.py. |
| 165 TaskProperties = collections.namedtuple( | 165 TaskProperties = collections.namedtuple( |
| 166 'TaskProperties', | 166 'TaskProperties', |
| 167 [ | 167 [ |
| 168 'caches', |
| 168 'cipd_input', | 169 'cipd_input', |
| 169 'command', | 170 'command', |
| 170 'dimensions', | 171 'dimensions', |
| 171 'env', | 172 'env', |
| 172 'execution_timeout_secs', | 173 'execution_timeout_secs', |
| 173 'extra_args', | 174 'extra_args', |
| 174 'grace_period_secs', | 175 'grace_period_secs', |
| 175 'idempotent', | 176 'idempotent', |
| 176 'inputs_ref', | 177 'inputs_ref', |
| 177 'io_timeout_secs', | 178 'io_timeout_secs', |
| (...skipping 702 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 880 | 881 |
| 881 ### Commands. | 882 ### Commands. |
| 882 | 883 |
| 883 | 884 |
| 884 def abort_task(_swarming, _manifest): | 885 def abort_task(_swarming, _manifest): |
| 885 """Given a task manifest that was triggered, aborts its execution.""" | 886 """Given a task manifest that was triggered, aborts its execution.""" |
| 886 # TODO(vadimsh): No supported by the server yet. | 887 # TODO(vadimsh): No supported by the server yet. |
| 887 | 888 |
| 888 | 889 |
| 889 def add_filter_options(parser): | 890 def add_filter_options(parser): |
| 890 parser.filter_group = optparse.OptionGroup(parser, 'Filtering slaves') | 891 parser.filter_group = optparse.OptionGroup(parser, 'Bot selection') |
| 891 parser.filter_group.add_option( | 892 parser.filter_group.add_option( |
| 892 '-d', '--dimension', default=[], action='append', nargs=2, | 893 '-d', '--dimension', default=[], action='append', nargs=2, |
| 893 dest='dimensions', metavar='FOO bar', | 894 dest='dimensions', metavar='FOO bar', |
| 894 help='dimension to filter on') | 895 help='dimension to filter on') |
| 895 parser.add_option_group(parser.filter_group) | 896 parser.add_option_group(parser.filter_group) |
| 896 | 897 |
| 897 | 898 |
| 898 def add_sharding_options(parser): | 899 def add_sharding_options(parser): |
| 899 parser.sharding_group = optparse.OptionGroup(parser, 'Sharding options') | 900 parser.sharding_group = optparse.OptionGroup(parser, 'Sharding options') |
| 900 parser.sharding_group.add_option( | 901 parser.sharding_group.add_option( |
| 901 '--shards', type='int', default=1, | 902 '--shards', type='int', default=1, |
| 902 help='Number of shards to trigger and collect.') | 903 help='Number of shards to trigger and collect.') |
| 903 parser.add_option_group(parser.sharding_group) | 904 parser.add_option_group(parser.sharding_group) |
| 904 | 905 |
| 905 | 906 |
| 906 def add_trigger_options(parser): | 907 def add_trigger_options(parser): |
| 907 """Adds all options to trigger a task on Swarming.""" | 908 """Adds all options to trigger a task on Swarming.""" |
| 908 isolateserver.add_isolate_server_options(parser) | 909 isolateserver.add_isolate_server_options(parser) |
| 909 add_filter_options(parser) | 910 add_filter_options(parser) |
| 910 | 911 |
| 911 parser.task_group = optparse.OptionGroup(parser, 'Task properties') | 912 group = optparse.OptionGroup(parser, 'Task properties') |
| 912 parser.task_group.add_option( | 913 group.add_option( |
| 913 '-s', '--isolated', | 914 '-s', '--isolated', |
| 914 help='Hash of the .isolated to grab from the isolate server') | 915 help='Hash of the .isolated to grab from the isolate server') |
| 915 parser.task_group.add_option( | 916 group.add_option( |
| 916 '-e', '--env', default=[], action='append', nargs=2, metavar='FOO bar', | 917 '-e', '--env', default=[], action='append', nargs=2, metavar='FOO bar', |
| 917 help='Environment variables to set') | 918 help='Environment variables to set') |
| 918 parser.task_group.add_option( | 919 group.add_option( |
| 920 '--idempotent', action='store_true', default=False, |
| 921 help='When set, the server will actively try to find a previous task ' |
| 922 'with the same parameter and return this result instead if possible') |
| 923 group.add_option( |
| 924 '--secret-bytes-path', |
| 925 help='The optional path to a file containing the secret_bytes to use with' |
| 926 'this task.') |
| 927 group.add_option( |
| 928 '--hard-timeout', type='int', default=60*60, |
| 929 help='Seconds to allow the task to complete.') |
| 930 group.add_option( |
| 931 '--io-timeout', type='int', default=20*60, |
| 932 help='Seconds to allow the task to be silent.') |
| 933 group.add_option( |
| 934 '--raw-cmd', action='store_true', default=False, |
| 935 help='When set, the command after -- is used as-is without run_isolated. ' |
| 936 'In this case, no .isolated file is expected.') |
| 937 group.add_option( |
| 938 '--cipd-package', action='append', default=[], |
| 939 help='CIPD packages to install on the Swarming bot. Uses the format: ' |
| 940 'path:package_name:version') |
| 941 group.add_option( |
| 942 '--named-cache', action='append', nargs=2, default=[], |
| 943 help='"<name> <relpath>" items to keep a persistent bot managed cache') |
| 944 group.add_option( |
| 945 '--service-account', |
| 946 help='Name of a service account to run the task as. Only literal "bot" ' |
| 947 'string can be specified currently (to run the task under bot\'s ' |
| 948 'account). Don\'t use task service accounts if not given ' |
| 949 '(default).') |
| 950 group.add_option( |
| 951 '-o', '--output', action='append', default=[], |
| 952 help='A list of files to return in addition to those written to' |
| 953 '$(ISOLATED_OUTDIR). An error will occur if a file specified by' |
| 954 'this option is also written directly to $(ISOLATED_OUTDIR).') |
| 955 parser.add_option_group(group) |
| 956 |
| 957 group = optparse.OptionGroup(parser, 'Task request') |
| 958 group.add_option( |
| 919 '--priority', type='int', default=100, | 959 '--priority', type='int', default=100, |
| 920 help='The lower value, the more important the task is') | 960 help='The lower value, the more important the task is') |
| 921 parser.task_group.add_option( | 961 group.add_option( |
| 922 '-T', '--task-name', | 962 '-T', '--task-name', |
| 923 help='Display name of the task. Defaults to ' | 963 help='Display name of the task. Defaults to ' |
| 924 '<base_name>/<dimensions>/<isolated hash>/<timestamp> if an ' | 964 '<base_name>/<dimensions>/<isolated hash>/<timestamp> if an ' |
| 925 'isolated file is provided, if a hash is provided, it defaults to ' | 965 'isolated file is provided, if a hash is provided, it defaults to ' |
| 926 '<user>/<dimensions>/<isolated hash>/<timestamp>') | 966 '<user>/<dimensions>/<isolated hash>/<timestamp>') |
| 927 parser.task_group.add_option( | 967 group.add_option( |
| 928 '--tags', action='append', default=[], | 968 '--tags', action='append', default=[], |
| 929 help='Tags to assign to the task.') | 969 help='Tags to assign to the task.') |
| 930 parser.task_group.add_option( | 970 group.add_option( |
| 931 '--user', default='', | 971 '--user', default='', |
| 932 help='User associated with the task. Defaults to authenticated user on ' | 972 help='User associated with the task. Defaults to authenticated user on ' |
| 933 'the server.') | 973 'the server.') |
| 934 parser.task_group.add_option( | 974 group.add_option( |
| 935 '--idempotent', action='store_true', default=False, | |
| 936 help='When set, the server will actively try to find a previous task ' | |
| 937 'with the same parameter and return this result instead if possible') | |
| 938 parser.task_group.add_option( | |
| 939 '--secret-bytes-path', | |
| 940 help='The optional path to a file containing the secret_bytes to use with' | |
| 941 'this task.') | |
| 942 parser.task_group.add_option( | |
| 943 '--expiration', type='int', default=6*60*60, | 975 '--expiration', type='int', default=6*60*60, |
| 944 help='Seconds to allow the task to be pending for a bot to run before ' | 976 help='Seconds to allow the task to be pending for a bot to run before ' |
| 945 'this task request expires.') | 977 'this task request expires.') |
| 946 parser.task_group.add_option( | 978 group.add_option( |
| 947 '--deadline', type='int', dest='expiration', | 979 '--deadline', type='int', dest='expiration', |
| 948 help=optparse.SUPPRESS_HELP) | 980 help=optparse.SUPPRESS_HELP) |
| 949 parser.task_group.add_option( | 981 parser.add_option_group(group) |
| 950 '--hard-timeout', type='int', default=60*60, | |
| 951 help='Seconds to allow the task to complete.') | |
| 952 parser.task_group.add_option( | |
| 953 '--io-timeout', type='int', default=20*60, | |
| 954 help='Seconds to allow the task to be silent.') | |
| 955 parser.task_group.add_option( | |
| 956 '--raw-cmd', action='store_true', default=False, | |
| 957 help='When set, the command after -- is used as-is without run_isolated. ' | |
| 958 'In this case, no .isolated file is expected.') | |
| 959 parser.task_group.add_option( | |
| 960 '--cipd-package', action='append', default=[], | |
| 961 help='CIPD packages to install on the Swarming bot. Uses the format: ' | |
| 962 'path:package_name:version') | |
| 963 parser.task_group.add_option( | |
| 964 '--service-account', | |
| 965 help='Name of a service account to run the task as. Only literal "bot" ' | |
| 966 'string can be specified currently (to run the task under bot\'s ' | |
| 967 'account). Don\'t use task service accounts if not given ' | |
| 968 '(default).') | |
| 969 parser.task_group.add_option( | |
| 970 '-o', '--output', action='append', default=[], | |
| 971 help='A list of files to return in addition to those written to' | |
| 972 '$(ISOLATED_OUTDIR). An error will occur if a file specified by' | |
| 973 'this option is also written directly to $(ISOLATED_OUTDIR).') | |
| 974 parser.add_option_group(parser.task_group) | |
| 975 | 982 |
| 976 | 983 |
| 977 def process_trigger_options(parser, options, args): | 984 def process_trigger_options(parser, options, args): |
| 978 """Processes trigger options and does preparatory steps. | 985 """Processes trigger options and does preparatory steps. |
| 979 | 986 |
| 980 Uploads files to isolate server and generates service account tokens if | 987 Uploads files to isolate server and generates service account tokens if |
| 981 necessary. | 988 necessary. |
| 982 """ | 989 """ |
| 983 options.dimensions = dict(options.dimensions) | 990 options.dimensions = dict(options.dimensions) |
| 984 options.env = dict(options.env) | 991 options.env = dict(options.env) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1022 cipd_input = CipdInput( | 1029 cipd_input = CipdInput( |
| 1023 client_package=None, | 1030 client_package=None, |
| 1024 packages=cipd_packages, | 1031 packages=cipd_packages, |
| 1025 server=None) | 1032 server=None) |
| 1026 | 1033 |
| 1027 secret_bytes = None | 1034 secret_bytes = None |
| 1028 if options.secret_bytes_path: | 1035 if options.secret_bytes_path: |
| 1029 with open(options.secret_bytes_path, 'r') as f: | 1036 with open(options.secret_bytes_path, 'r') as f: |
| 1030 secret_bytes = f.read().encode('base64') | 1037 secret_bytes = f.read().encode('base64') |
| 1031 | 1038 |
| 1039 caches = [ |
| 1040 {u'name': unicode(i[0]), u'path': unicode(i[1])} |
| 1041 for i in options.named_cache |
| 1042 ] |
| 1032 # If inputs_ref.isolated is used, command is actually extra_args. | 1043 # If inputs_ref.isolated is used, command is actually extra_args. |
| 1033 # Otherwise it's an actual command to run. | 1044 # Otherwise it's an actual command to run. |
| 1034 isolated_input = inputs_ref and inputs_ref.isolated | 1045 isolated_input = inputs_ref and inputs_ref.isolated |
| 1035 properties = TaskProperties( | 1046 properties = TaskProperties( |
| 1047 caches=caches, |
| 1036 cipd_input=cipd_input, | 1048 cipd_input=cipd_input, |
| 1037 command=None if isolated_input else command, | 1049 command=None if isolated_input else command, |
| 1038 dimensions=options.dimensions, | 1050 dimensions=options.dimensions, |
| 1039 env=options.env, | 1051 env=options.env, |
| 1040 execution_timeout_secs=options.hard_timeout, | 1052 execution_timeout_secs=options.hard_timeout, |
| 1041 extra_args=command if isolated_input else None, | 1053 extra_args=command if isolated_input else None, |
| 1042 grace_period_secs=30, | 1054 grace_period_secs=30, |
| 1043 idempotent=options.idempotent, | 1055 idempotent=options.idempotent, |
| 1044 inputs_ref=inputs_ref, | 1056 inputs_ref=inputs_ref, |
| 1045 io_timeout_secs=options.io_timeout, | 1057 io_timeout_secs=options.io_timeout, |
| (...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1655 dispatcher = subcommand.CommandDispatcher(__name__) | 1667 dispatcher = subcommand.CommandDispatcher(__name__) |
| 1656 return dispatcher.execute(OptionParserSwarming(version=__version__), args) | 1668 return dispatcher.execute(OptionParserSwarming(version=__version__), args) |
| 1657 | 1669 |
| 1658 | 1670 |
| 1659 if __name__ == '__main__': | 1671 if __name__ == '__main__': |
| 1660 subprocess42.inhibit_os_error_reporting() | 1672 subprocess42.inhibit_os_error_reporting() |
| 1661 fix_encoding.fix_encoding() | 1673 fix_encoding.fix_encoding() |
| 1662 tools.disable_buffering() | 1674 tools.disable_buffering() |
| 1663 colorama.init() | 1675 colorama.init() |
| 1664 sys.exit(main(sys.argv[1:])) | 1676 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |