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 |