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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
792 client_manager = cipd.get_client( | 796 client_manager = cipd.get_client( |
793 service_url, client_package_name, client_version, cache_dir, | 797 service_url, client_package_name, client_version, cache_dir, |
794 timeout=timeoutfn()) | 798 timeout=timeoutfn()) |
795 | 799 |
796 with client_manager as client: | 800 with client_manager as client: |
797 get_client_duration = time.time() - get_client_start | 801 get_client_duration = time.time() - get_client_start |
798 | 802 |
799 package_pins = [] | 803 package_pins = [] |
800 if packages: | 804 if packages: |
801 package_pins = _install_packages( | 805 package_pins = _install_packages( |
802 run_dir, cipd_cache_dir, client, packages, timeoutfn()) | 806 run_dir, cipd_cache_dir, client, packages, timeoutfn(), isolate_cache) |
803 | 807 |
804 file_path.make_tree_files_read_only(run_dir) | 808 file_path.make_tree_files_read_only(run_dir) |
805 | 809 |
806 total_duration = time.time() - start | 810 total_duration = time.time() - start |
807 logging.info( | 811 logging.info( |
808 'Installing CIPD client and packages took %d seconds', total_duration) | 812 'Installing CIPD client and packages took %d seconds', total_duration) |
809 | 813 |
810 yield CipdInfo( | 814 yield CipdInfo( |
811 client=client, | 815 client=client, |
812 cache_dir=cipd_cache_dir, | 816 cache_dir=cipd_cache_dir, |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
998 if options.json: | 1002 if options.json: |
999 options.json = unicode(os.path.abspath(options.json)) | 1003 options.json = unicode(os.path.abspath(options.json)) |
1000 | 1004 |
1001 cipd.validate_cipd_options(parser, options) | 1005 cipd.validate_cipd_options(parser, options) |
1002 | 1006 |
1003 install_packages_fn = noop_install_packages | 1007 install_packages_fn = noop_install_packages |
1004 if options.cipd_enabled: | 1008 if options.cipd_enabled: |
1005 install_packages_fn = lambda run_dir: install_client_and_packages( | 1009 install_packages_fn = lambda run_dir: install_client_and_packages( |
1006 run_dir, cipd.parse_package_args(options.cipd_packages), | 1010 run_dir, cipd.parse_package_args(options.cipd_packages), |
1007 options.cipd_server, options.cipd_client_package, | 1011 options.cipd_server, options.cipd_client_package, |
1008 options.cipd_client_version, cache_dir=options.cipd_cache) | 1012 options.cipd_client_version, cache_dir=options.cipd_cache, |
| 1013 isolate_cache=isolate_cache) |
1009 | 1014 |
1010 @contextlib.contextmanager | 1015 @contextlib.contextmanager |
1011 def init_named_caches(run_dir): | 1016 def init_named_caches(run_dir): |
1012 # WARNING: this function depends on "options" variable defined in the outer | 1017 # WARNING: this function depends on "options" variable defined in the outer |
1013 # function. | 1018 # function. |
1014 with named_cache_manager.open(): | 1019 with named_cache_manager.open(): |
1015 named_cache_manager.create_symlinks(run_dir, options.named_caches) | 1020 named_cache_manager.create_symlinks(run_dir, options.named_caches) |
1016 try: | 1021 try: |
1017 yield | 1022 yield |
1018 finally: | 1023 finally: |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1060 return 1 | 1065 return 1 |
1061 | 1066 |
1062 | 1067 |
1063 if __name__ == '__main__': | 1068 if __name__ == '__main__': |
1064 subprocess42.inhibit_os_error_reporting() | 1069 subprocess42.inhibit_os_error_reporting() |
1065 # Ensure that we are always running with the correct encoding. | 1070 # Ensure that we are always running with the correct encoding. |
1066 fix_encoding.fix_encoding() | 1071 fix_encoding.fix_encoding() |
1067 file_path.enable_symlink() | 1072 file_path.enable_symlink() |
1068 | 1073 |
1069 sys.exit(main(sys.argv[1:])) | 1074 sys.exit(main(sys.argv[1:])) |
OLD | NEW |