| 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_format.py')) |
| 124 package.add_python_file(os.path.join(BASE_DIR, 'auth.py')) | 125 package.add_python_file(os.path.join(BASE_DIR, 'auth.py')) |
| 125 package.add_python_file(os.path.join(BASE_DIR, 'cipd.py')) | 126 package.add_python_file(os.path.join(BASE_DIR, 'cipd.py')) |
| 126 package.add_python_file(os.path.join(BASE_DIR, 'named_cache.py')) | 127 package.add_python_file(os.path.join(BASE_DIR, 'named_cache.py')) |
| 128 package.add_python_file(os.path.join(BASE_DIR, 'run_isolated.py')) |
| 127 package.add_directory(os.path.join(BASE_DIR, 'libs')) | 129 package.add_directory(os.path.join(BASE_DIR, 'libs')) |
| 128 package.add_directory(os.path.join(BASE_DIR, 'third_party')) | 130 package.add_directory(os.path.join(BASE_DIR, 'third_party')) |
| 129 package.add_directory(os.path.join(BASE_DIR, 'utils')) | 131 package.add_directory(os.path.join(BASE_DIR, 'utils')) |
| 130 return package | 132 return package |
| 131 | 133 |
| 132 | 134 |
| 133 def make_temp_dir(prefix, root_dir): | 135 def make_temp_dir(prefix, root_dir): |
| 134 """Returns a new unique temporary directory.""" | 136 """Returns a new unique temporary directory.""" |
| 135 return unicode(tempfile.mkdtemp(prefix=prefix, dir=root_dir)) | 137 return unicode(tempfile.mkdtemp(prefix=prefix, dir=root_dir)) |
| 136 | 138 |
| (...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 | 681 'pins', # dict with installed cipd pins to return to the server |
| 680 ]) | 682 ]) |
| 681 | 683 |
| 682 | 684 |
| 683 @contextlib.contextmanager | 685 @contextlib.contextmanager |
| 684 def noop_install_packages(_run_dir): | 686 def noop_install_packages(_run_dir): |
| 685 """Placeholder for 'install_client_and_packages' if cipd is disabled.""" | 687 """Placeholder for 'install_client_and_packages' if cipd is disabled.""" |
| 686 yield None | 688 yield None |
| 687 | 689 |
| 688 | 690 |
| 689 def _install_packages(run_dir, cipd_cache_dir, client, packages, timeout): | 691 def _install_packages(run_dir, cipd_cache_dir, client, packages, timeout, |
| 692 isolate_cache=None): |
| 690 """Calls 'cipd ensure' for packages. | 693 """Calls 'cipd ensure' for packages. |
| 691 | 694 |
| 692 Args: | 695 Args: |
| 693 run_dir (str): root of installation. | 696 run_dir (str): root of installation. |
| 694 cipd_cache_dir (str): the directory to use for the cipd package cache. | 697 cipd_cache_dir (str): the directory to use for the cipd package cache. |
| 695 client (CipdClient): the cipd client to use | 698 client (CipdClient): the cipd client to use |
| 696 packages: packages to install, list [(path, package_name, version), ...]. | 699 packages: packages to install, list [(path, package_name, version), ...]. |
| 697 timeout: max duration in seconds that this function can take. | 700 timeout: max duration in seconds that this function can take. |
| 698 | 701 |
| 699 Returns: list of pinned packages. Looks like [ | 702 Returns: list of pinned packages. Looks like [ |
| (...skipping 22 matching lines...) Expand all Loading... |
| 722 by_path[path].append((name, version, i)) | 725 by_path[path].append((name, version, i)) |
| 723 | 726 |
| 724 pins = client.ensure( | 727 pins = client.ensure( |
| 725 run_dir, | 728 run_dir, |
| 726 { | 729 { |
| 727 subdir: [(name, vers) for name, vers, _ in pkgs] | 730 subdir: [(name, vers) for name, vers, _ in pkgs] |
| 728 for subdir, pkgs in by_path.iteritems() | 731 for subdir, pkgs in by_path.iteritems() |
| 729 }, | 732 }, |
| 730 cache_dir=cipd_cache_dir, | 733 cache_dir=cipd_cache_dir, |
| 731 timeout=timeout, | 734 timeout=timeout, |
| 735 isolate_cache=isolate_cache, |
| 732 ) | 736 ) |
| 733 | 737 |
| 734 for subdir, pin_list in sorted(pins.iteritems()): | 738 for subdir, pin_list in sorted(pins.iteritems()): |
| 735 this_subdir = by_path[subdir] | 739 this_subdir = by_path[subdir] |
| 736 for i, (name, version) in enumerate(pin_list): | 740 for i, (name, version) in enumerate(pin_list): |
| 737 insert_pin(subdir, name, version, this_subdir[i][2]) | 741 insert_pin(subdir, name, version, this_subdir[i][2]) |
| 738 | 742 |
| 739 assert None not in package_pins | 743 assert None not in package_pins |
| 740 | 744 |
| 741 return package_pins | 745 return package_pins |
| 742 | 746 |
| 743 | 747 |
| 744 @contextlib.contextmanager | 748 @contextlib.contextmanager |
| 745 def install_client_and_packages( | 749 def install_client_and_packages( |
| 746 run_dir, packages, service_url, client_package_name, | 750 run_dir, packages, service_url, client_package_name, |
| 747 client_version, cache_dir, timeout=None): | 751 client_version, cache_dir, timeout=None, isolate_cache=None): |
| 748 """Bootstraps CIPD client and installs CIPD packages. | 752 """Bootstraps CIPD client and installs CIPD packages. |
| 749 | 753 |
| 750 Yields CipdClient, stats, client info and pins (as single CipdInfo object). | 754 Yields CipdClient, stats, client info and pins (as single CipdInfo object). |
| 751 | 755 |
| 752 Pins and the CIPD client info are in the form of: | 756 Pins and the CIPD client info are in the form of: |
| 753 [ | 757 [ |
| 754 { | 758 { |
| 755 "path": path, "package_name": package_name, "version": version, | 759 "path": path, "package_name": package_name, "version": version, |
| 756 }, | 760 }, |
| 757 ... | 761 ... |
| (...skipping 20 matching lines...) Expand all Loading... |
| 778 cache_dir (str): where to keep cache of cipd clients, packages and tags. | 782 cache_dir (str): where to keep cache of cipd clients, packages and tags. |
| 779 timeout: max duration in seconds that this function can take. | 783 timeout: max duration in seconds that this function can take. |
| 780 """ | 784 """ |
| 781 assert cache_dir | 785 assert cache_dir |
| 782 | 786 |
| 783 timeoutfn = tools.sliding_timeout(timeout) | 787 timeoutfn = tools.sliding_timeout(timeout) |
| 784 start = time.time() | 788 start = time.time() |
| 785 | 789 |
| 786 cache_dir = os.path.abspath(cache_dir) | 790 cache_dir = os.path.abspath(cache_dir) |
| 787 cipd_cache_dir = os.path.join(cache_dir, 'cache') # tag and instance caches | 791 cipd_cache_dir = os.path.join(cache_dir, 'cache') # tag and instance caches |
| 792 file_path.ensure_tree(unicode(cipd_cache_dir)) |
| 793 |
| 788 run_dir = os.path.abspath(run_dir) | 794 run_dir = os.path.abspath(run_dir) |
| 789 packages = packages or [] | 795 packages = packages or [] |
| 790 | 796 |
| 791 get_client_start = time.time() | 797 get_client_start = time.time() |
| 792 client_manager = cipd.get_client( | 798 client_manager = cipd.get_client( |
| 793 service_url, client_package_name, client_version, cache_dir, | 799 service_url, client_package_name, client_version, cache_dir, |
| 794 timeout=timeoutfn()) | 800 timeout=timeoutfn()) |
| 795 | 801 |
| 796 with client_manager as client: | 802 with client_manager as client: |
| 797 get_client_duration = time.time() - get_client_start | 803 get_client_duration = time.time() - get_client_start |
| 798 | 804 |
| 799 package_pins = [] | 805 package_pins = [] |
| 800 if packages: | 806 if packages: |
| 801 package_pins = _install_packages( | 807 package_pins = _install_packages( |
| 802 run_dir, cipd_cache_dir, client, packages, timeoutfn()) | 808 run_dir, cipd_cache_dir, client, packages, timeoutfn(), isolate_cache) |
| 803 | 809 |
| 804 file_path.make_tree_files_read_only(run_dir) | 810 file_path.make_tree_files_read_only(run_dir) |
| 805 | 811 |
| 806 total_duration = time.time() - start | 812 total_duration = time.time() - start |
| 807 logging.info( | 813 logging.info( |
| 808 'Installing CIPD client and packages took %d seconds', total_duration) | 814 'Installing CIPD client and packages took %d seconds', total_duration) |
| 809 | 815 |
| 810 yield CipdInfo( | 816 yield CipdInfo( |
| 811 client=client, | 817 client=client, |
| 812 cache_dir=cipd_cache_dir, | 818 cache_dir=cipd_cache_dir, |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 998 if options.json: | 1004 if options.json: |
| 999 options.json = unicode(os.path.abspath(options.json)) | 1005 options.json = unicode(os.path.abspath(options.json)) |
| 1000 | 1006 |
| 1001 cipd.validate_cipd_options(parser, options) | 1007 cipd.validate_cipd_options(parser, options) |
| 1002 | 1008 |
| 1003 install_packages_fn = noop_install_packages | 1009 install_packages_fn = noop_install_packages |
| 1004 if options.cipd_enabled: | 1010 if options.cipd_enabled: |
| 1005 install_packages_fn = lambda run_dir: install_client_and_packages( | 1011 install_packages_fn = lambda run_dir: install_client_and_packages( |
| 1006 run_dir, cipd.parse_package_args(options.cipd_packages), | 1012 run_dir, cipd.parse_package_args(options.cipd_packages), |
| 1007 options.cipd_server, options.cipd_client_package, | 1013 options.cipd_server, options.cipd_client_package, |
| 1008 options.cipd_client_version, cache_dir=options.cipd_cache) | 1014 options.cipd_client_version, cache_dir=options.cipd_cache, |
| 1015 isolate_cache=isolate_cache) |
| 1009 | 1016 |
| 1010 @contextlib.contextmanager | 1017 @contextlib.contextmanager |
| 1011 def init_named_caches(run_dir): | 1018 def init_named_caches(run_dir): |
| 1012 # WARNING: this function depends on "options" variable defined in the outer | 1019 # WARNING: this function depends on "options" variable defined in the outer |
| 1013 # function. | 1020 # function. |
| 1014 with named_cache_manager.open(): | 1021 with named_cache_manager.open(): |
| 1015 named_cache_manager.create_symlinks(run_dir, options.named_caches) | 1022 named_cache_manager.create_symlinks(run_dir, options.named_caches) |
| 1016 try: | 1023 try: |
| 1017 yield | 1024 yield |
| 1018 finally: | 1025 finally: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1060 return 1 | 1067 return 1 |
| 1061 | 1068 |
| 1062 | 1069 |
| 1063 if __name__ == '__main__': | 1070 if __name__ == '__main__': |
| 1064 subprocess42.inhibit_os_error_reporting() | 1071 subprocess42.inhibit_os_error_reporting() |
| 1065 # Ensure that we are always running with the correct encoding. | 1072 # Ensure that we are always running with the correct encoding. |
| 1066 fix_encoding.fix_encoding() | 1073 fix_encoding.fix_encoding() |
| 1067 file_path.enable_symlink() | 1074 file_path.enable_symlink() |
| 1068 | 1075 |
| 1069 sys.exit(main(sys.argv[1:])) | 1076 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |