| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2012 The LUCI Authors. All rights reserved. | 2 # Copyright 2012 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 """Runs a command with optional isolated input/output. | 6 """Runs a command with optional isolated input/output. |
| 7 | 7 |
| 8 Despite name "run_isolated", can run a generic non-isolated command specified as | 8 Despite name "run_isolated", can run a generic non-isolated command specified as |
| 9 args. | 9 args. |
| 10 | 10 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 RUN_ISOLATED_LOG_FILE = 'run_isolated.log' | 86 RUN_ISOLATED_LOG_FILE = 'run_isolated.log' |
| 87 | 87 |
| 88 | 88 |
| 89 # The name of the log to use for the run_test_cases.py command | 89 # The name of the log to use for the run_test_cases.py command |
| 90 RUN_TEST_CASES_LOG = 'run_test_cases.log' | 90 RUN_TEST_CASES_LOG = 'run_test_cases.log' |
| 91 | 91 |
| 92 | 92 |
| 93 # Use short names for temporary directories. This is driven by Windows, which | 93 # Use short names for temporary directories. This is driven by Windows, which |
| 94 # imposes a relatively short maximum path length of 260 characters, often | 94 # imposes a relatively short maximum path length of 260 characters, often |
| 95 # referred to as MAX_PATH. It is relatively easy to create files with longer | 95 # referred to as MAX_PATH. It is relatively easy to create files with longer |
| 96 # path length. A use case is with recursive depedency treesV like npm packages. | 96 # path length. A use case is with recursive dependency trees like npm packages. |
| 97 # | 97 # |
| 98 # It is recommended to start the script with a `root_dir` as short as | 98 # It is recommended to start the script with a `root_dir` as short as |
| 99 # possible. | 99 # possible. |
| 100 # - ir stands for isolated_run | 100 # - ir stands for isolated_run |
| 101 # - io stands for isolated_out | 101 # - io stands for isolated_out |
| 102 # - it stands for isolated_tmp | 102 # - it stands for isolated_tmp |
| 103 ISOLATED_RUN_DIR = u'ir' | 103 ISOLATED_RUN_DIR = u'ir' |
| 104 ISOLATED_OUT_DIR = u'io' | 104 ISOLATED_OUT_DIR = u'io' |
| 105 ISOLATED_TMP_DIR = u'it' | 105 ISOLATED_TMP_DIR = u'it' |
| 106 | 106 |
| 107 | 107 |
| 108 def get_as_zip_package(executable=True): | 108 def get_as_zip_package(executable=True): |
| 109 """Returns ZipPackage with this module and all its dependencies. | 109 """Returns ZipPackage with this module and all its dependencies. |
| 110 | 110 |
| 111 If |executable| is True will store run_isolated.py as __main__.py so that | 111 If |executable| is True will store run_isolated.py as __main__.py so that |
| 112 zip package is directly executable be python. | 112 zip package is directly executable be python. |
| 113 """ | 113 """ |
| 114 # Building a zip package when running from another zip package is | 114 # Building a zip package when running from another zip package is |
| 115 # unsupported and probably unneeded. | 115 # unsupported and probably unneeded. |
| 116 assert not zip_package.is_zipped_module(sys.modules[__name__]) | 116 assert not zip_package.is_zipped_module(sys.modules[__name__]) |
| 117 assert THIS_FILE_PATH | 117 assert THIS_FILE_PATH |
| 118 assert BASE_DIR | 118 assert BASE_DIR |
| 119 package = zip_package.ZipPackage(root=BASE_DIR) | 119 package = zip_package.ZipPackage(root=BASE_DIR) |
| 120 package.add_python_file(THIS_FILE_PATH, '__main__.py' if executable else None) | 120 package.add_python_file(THIS_FILE_PATH, '__main__.py' if executable else None) |
| 121 package.add_python_file(os.path.join(BASE_DIR, 'isolate_storage.py')) | 121 package.add_python_file(os.path.join(BASE_DIR, 'isolate_storage.py')) |
| 122 package.add_python_file(os.path.join(BASE_DIR, 'isolated_format.py')) | 122 package.add_python_file(os.path.join(BASE_DIR, 'isolated_format.py')) |
| 123 package.add_python_file(os.path.join(BASE_DIR, 'isolateserver.py')) | 123 package.add_python_file(os.path.join(BASE_DIR, 'isolateserver.py')) |
| 124 package.add_python_file(os.path.join(BASE_DIR, 'isolate.py')) |
| 125 package.add_python_file(os.path.join(BASE_DIR, 'isolate_format.py')) |
| 124 package.add_python_file(os.path.join(BASE_DIR, 'auth.py')) | 126 package.add_python_file(os.path.join(BASE_DIR, 'auth.py')) |
| 125 package.add_python_file(os.path.join(BASE_DIR, 'cipd.py')) | 127 package.add_python_file(os.path.join(BASE_DIR, 'cipd.py')) |
| 126 package.add_python_file(os.path.join(BASE_DIR, 'named_cache.py')) | 128 package.add_python_file(os.path.join(BASE_DIR, 'named_cache.py')) |
| 129 package.add_python_file(os.path.join(BASE_DIR, 'run_isolated.py')) |
| 127 package.add_directory(os.path.join(BASE_DIR, 'libs')) | 130 package.add_directory(os.path.join(BASE_DIR, 'libs')) |
| 128 package.add_directory(os.path.join(BASE_DIR, 'third_party')) | 131 package.add_directory(os.path.join(BASE_DIR, 'third_party')) |
| 129 package.add_directory(os.path.join(BASE_DIR, 'utils')) | 132 package.add_directory(os.path.join(BASE_DIR, 'utils')) |
| 130 return package | 133 return package |
| 131 | 134 |
| 132 | 135 |
| 133 def make_temp_dir(prefix, root_dir): | 136 def make_temp_dir(prefix, root_dir): |
| 134 """Returns a new unique temporary directory.""" | 137 """Returns a new unique temporary directory.""" |
| 135 return unicode(tempfile.mkdtemp(prefix=prefix, dir=root_dir)) | 138 return unicode(tempfile.mkdtemp(prefix=prefix, dir=root_dir)) |
| 136 | 139 |
| (...skipping 542 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 679 'pins', # dict with installed cipd pins to return to the server | 682 'pins', # dict with installed cipd pins to return to the server |
| 680 ]) | 683 ]) |
| 681 | 684 |
| 682 | 685 |
| 683 @contextlib.contextmanager | 686 @contextlib.contextmanager |
| 684 def noop_install_packages(_run_dir): | 687 def noop_install_packages(_run_dir): |
| 685 """Placeholder for 'install_client_and_packages' if cipd is disabled.""" | 688 """Placeholder for 'install_client_and_packages' if cipd is disabled.""" |
| 686 yield None | 689 yield None |
| 687 | 690 |
| 688 | 691 |
| 689 def _install_packages(run_dir, cipd_cache_dir, client, packages, timeout): | 692 def _install_packages(run_dir, cipd_cache_dir, client, packages, timeout, |
| 693 isolate_cache=None): |
| 690 """Calls 'cipd ensure' for packages. | 694 """Calls 'cipd ensure' for packages. |
| 691 | 695 |
| 692 Args: | 696 Args: |
| 693 run_dir (str): root of installation. | 697 run_dir (str): root of installation. |
| 694 cipd_cache_dir (str): the directory to use for the cipd package cache. | 698 cipd_cache_dir (str): the directory to use for the cipd package cache. |
| 695 client (CipdClient): the cipd client to use | 699 client (CipdClient): the cipd client to use |
| 696 packages: packages to install, list [(path, package_name, version), ...]. | 700 packages: packages to install, list [(path, package_name, version), ...]. |
| 697 timeout: max duration in seconds that this function can take. | 701 timeout: max duration in seconds that this function can take. |
| 698 | 702 |
| 699 Returns: list of pinned packages. Looks like [ | 703 Returns: list of pinned packages. Looks like [ |
| (...skipping 22 matching lines...) Expand all Loading... |
| 722 by_path[path].append((name, version, i)) | 726 by_path[path].append((name, version, i)) |
| 723 | 727 |
| 724 pins = client.ensure( | 728 pins = client.ensure( |
| 725 run_dir, | 729 run_dir, |
| 726 { | 730 { |
| 727 subdir: [(name, vers) for name, vers, _ in pkgs] | 731 subdir: [(name, vers) for name, vers, _ in pkgs] |
| 728 for subdir, pkgs in by_path.iteritems() | 732 for subdir, pkgs in by_path.iteritems() |
| 729 }, | 733 }, |
| 730 cache_dir=cipd_cache_dir, | 734 cache_dir=cipd_cache_dir, |
| 731 timeout=timeout, | 735 timeout=timeout, |
| 736 isolate_cache=isolate_cache, |
| 732 ) | 737 ) |
| 733 | 738 |
| 734 for subdir, pin_list in sorted(pins.iteritems()): | 739 for subdir, pin_list in sorted(pins.iteritems()): |
| 735 this_subdir = by_path[subdir] | 740 this_subdir = by_path[subdir] |
| 736 for i, (name, version) in enumerate(pin_list): | 741 for i, (name, version) in enumerate(pin_list): |
| 737 insert_pin(subdir, name, version, this_subdir[i][2]) | 742 insert_pin(subdir, name, version, this_subdir[i][2]) |
| 738 | 743 |
| 739 assert None not in package_pins | 744 assert None not in package_pins |
| 740 | 745 |
| 741 return package_pins | 746 return package_pins |
| 742 | 747 |
| 743 | 748 |
| 744 @contextlib.contextmanager | 749 @contextlib.contextmanager |
| 745 def install_client_and_packages( | 750 def install_client_and_packages( |
| 746 run_dir, packages, service_url, client_package_name, | 751 run_dir, packages, service_url, client_package_name, |
| 747 client_version, cache_dir, timeout=None): | 752 client_version, cache_dir, timeout=None, isolate_cache=None): |
| 748 """Bootstraps CIPD client and installs CIPD packages. | 753 """Bootstraps CIPD client and installs CIPD packages. |
| 749 | 754 |
| 750 Yields CipdClient, stats, client info and pins (as single CipdInfo object). | 755 Yields CipdClient, stats, client info and pins (as single CipdInfo object). |
| 751 | 756 |
| 752 Pins and the CIPD client info are in the form of: | 757 Pins and the CIPD client info are in the form of: |
| 753 [ | 758 [ |
| 754 { | 759 { |
| 755 "path": path, "package_name": package_name, "version": version, | 760 "path": path, "package_name": package_name, "version": version, |
| 756 }, | 761 }, |
| 757 ... | 762 ... |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 792 client_manager = cipd.get_client( | 797 client_manager = cipd.get_client( |
| 793 service_url, client_package_name, client_version, cache_dir, | 798 service_url, client_package_name, client_version, cache_dir, |
| 794 timeout=timeoutfn()) | 799 timeout=timeoutfn()) |
| 795 | 800 |
| 796 with client_manager as client: | 801 with client_manager as client: |
| 797 get_client_duration = time.time() - get_client_start | 802 get_client_duration = time.time() - get_client_start |
| 798 | 803 |
| 799 package_pins = [] | 804 package_pins = [] |
| 800 if packages: | 805 if packages: |
| 801 package_pins = _install_packages( | 806 package_pins = _install_packages( |
| 802 run_dir, cipd_cache_dir, client, packages, timeoutfn()) | 807 run_dir, cipd_cache_dir, client, packages, timeoutfn(), isolate_cache) |
| 803 | 808 |
| 804 file_path.make_tree_files_read_only(run_dir) | 809 file_path.make_tree_files_read_only(run_dir) |
| 805 | 810 |
| 806 total_duration = time.time() - start | 811 total_duration = time.time() - start |
| 807 logging.info( | 812 logging.info( |
| 808 'Installing CIPD client and packages took %d seconds', total_duration) | 813 'Installing CIPD client and packages took %d seconds', total_duration) |
| 809 | 814 |
| 810 yield CipdInfo( | 815 yield CipdInfo( |
| 811 client=client, | 816 client=client, |
| 812 cache_dir=cipd_cache_dir, | 817 cache_dir=cipd_cache_dir, |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 989 if options.json: | 994 if options.json: |
| 990 options.json = unicode(os.path.abspath(options.json)) | 995 options.json = unicode(os.path.abspath(options.json)) |
| 991 | 996 |
| 992 cipd.validate_cipd_options(parser, options) | 997 cipd.validate_cipd_options(parser, options) |
| 993 | 998 |
| 994 install_packages_fn = noop_install_packages | 999 install_packages_fn = noop_install_packages |
| 995 if options.cipd_enabled: | 1000 if options.cipd_enabled: |
| 996 install_packages_fn = lambda run_dir: install_client_and_packages( | 1001 install_packages_fn = lambda run_dir: install_client_and_packages( |
| 997 run_dir, cipd.parse_package_args(options.cipd_packages), | 1002 run_dir, cipd.parse_package_args(options.cipd_packages), |
| 998 options.cipd_server, options.cipd_client_package, | 1003 options.cipd_server, options.cipd_client_package, |
| 999 options.cipd_client_version, cache_dir=options.cipd_cache) | 1004 options.cipd_client_version, cache_dir=options.cipd_cache, |
| 1005 isolate_cache=isolate_cache) |
| 1000 | 1006 |
| 1001 @contextlib.contextmanager | 1007 @contextlib.contextmanager |
| 1002 def init_named_caches(run_dir): | 1008 def init_named_caches(run_dir): |
| 1003 # WARNING: this function depends on "options" variable defined in the outer | 1009 # WARNING: this function depends on "options" variable defined in the outer |
| 1004 # function. | 1010 # function. |
| 1005 with named_cache_manager.open(): | 1011 with named_cache_manager.open(): |
| 1006 named_cache_manager.create_symlinks(run_dir, options.named_caches) | 1012 named_cache_manager.create_symlinks(run_dir, options.named_caches) |
| 1007 try: | 1013 try: |
| 1008 yield | 1014 yield |
| 1009 finally: | 1015 finally: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1051 return 1 | 1057 return 1 |
| 1052 | 1058 |
| 1053 | 1059 |
| 1054 if __name__ == '__main__': | 1060 if __name__ == '__main__': |
| 1055 subprocess42.inhibit_os_error_reporting() | 1061 subprocess42.inhibit_os_error_reporting() |
| 1056 # Ensure that we are always running with the correct encoding. | 1062 # Ensure that we are always running with the correct encoding. |
| 1057 fix_encoding.fix_encoding() | 1063 fix_encoding.fix_encoding() |
| 1058 file_path.enable_symlink() | 1064 file_path.enable_symlink() |
| 1059 | 1065 |
| 1060 sys.exit(main(sys.argv[1:])) | 1066 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |