Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Side by Side Diff: gsutil.py

Issue 1346213003: gsutil: Parallel-safe, specify target, add clean. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Explicitly create base directory. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/gsutil_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Run a pinned gsutil.""" 6 """Run a pinned gsutil."""
7 7
8 8
9 import argparse 9 import argparse
10 import base64 10 import base64
11 import contextlib
11 import hashlib 12 import hashlib
12 import json 13 import json
13 import os 14 import os
14 import shutil 15 import shutil
15 import subprocess 16 import subprocess
16 import sys 17 import sys
18 import tempfile
19 import time
17 import urllib2 20 import urllib2
18 import zipfile 21 import zipfile
19 22
20 23
21 GSUTIL_URL = 'https://storage.googleapis.com/pub/' 24 GSUTIL_URL = 'https://storage.googleapis.com/pub/'
22 API_URL = 'https://www.googleapis.com/storage/v1/b/pub/o/' 25 API_URL = 'https://www.googleapis.com/storage/v1/b/pub/o/'
23 26
24 THIS_DIR = os.path.dirname(os.path.abspath(__file__)) 27 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
25 DEFAULT_BIN_DIR = os.path.join(THIS_DIR, 'external_bin', 'gsutil') 28 DEFAULT_BIN_DIR = os.path.join(THIS_DIR, 'external_bin', 'gsutil')
26 DEFAULT_FALLBACK_GSUTIL = os.path.join( 29 DEFAULT_FALLBACK_GSUTIL = os.path.join(
27 THIS_DIR, 'third_party', 'gsutil', 'gsutil') 30 THIS_DIR, 'third_party', 'gsutil', 'gsutil')
28 31
29
30 class InvalidGsutilError(Exception): 32 class InvalidGsutilError(Exception):
31 pass 33 pass
32 34
33 35
34 def download_gsutil(version, target_dir): 36 def download_gsutil(version, target_dir):
35 """Downloads gsutil into the target_dir.""" 37 """Downloads gsutil into the target_dir."""
36 filename = 'gsutil_%s.zip' % version 38 filename = 'gsutil_%s.zip' % version
37 target_filename = os.path.join(target_dir, filename) 39 target_filename = os.path.join(target_dir, filename)
38 40
39 # Check if the target exists already. 41 # Check if the target exists already.
(...skipping 26 matching lines...) Expand all
66 f.write(buf) 68 f.write(buf)
67 return target_filename 69 return target_filename
68 70
69 71
70 def check_gsutil(gsutil_bin): 72 def check_gsutil(gsutil_bin):
71 """Run gsutil version and make sure it runs.""" 73 """Run gsutil version and make sure it runs."""
72 return subprocess.call( 74 return subprocess.call(
73 [sys.executable, gsutil_bin, 'version'], 75 [sys.executable, gsutil_bin, 'version'],
74 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) == 0 76 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) == 0
75 77
76 def ensure_gsutil(version, target): 78 @contextlib.contextmanager
79 def temporary_directory(base):
80 tmpdir = tempfile.mkdtemp(prefix='gsutil_py', dir=base)
81 try:
82 yield tmpdir
83 finally:
84 if os.path.isdir(tmpdir):
85 shutil.rmtree(tmpdir)
86
87 def ensure_gsutil(version, target, clean):
77 bin_dir = os.path.join(target, 'gsutil_%s' % version) 88 bin_dir = os.path.join(target, 'gsutil_%s' % version)
78 gsutil_bin = os.path.join(bin_dir, 'gsutil', 'gsutil') 89 gsutil_bin = os.path.join(bin_dir, 'gsutil', 'gsutil')
79 if os.path.isfile(gsutil_bin) and check_gsutil(gsutil_bin): 90 if not clean and os.path.isfile(gsutil_bin) and check_gsutil(gsutil_bin):
80 # Everything is awesome! we're all done here. 91 # Everything is awesome! we're all done here.
81 return gsutil_bin 92 return gsutil_bin
82 93
83 if os.path.isdir(bin_dir): 94 if not os.path.exists(target):
95 os.makedirs(target)
96 with temporary_directory(target) as instance_dir:
84 # Clean up if we're redownloading a corrupted gsutil. 97 # Clean up if we're redownloading a corrupted gsutil.
85 shutil.rmtree(bin_dir) 98 cleanup_path = os.path.join(instance_dir, 'clean')
86 cache_dir = os.path.join(target, '.cache_dir') 99 try:
87 if not os.path.isdir(cache_dir): 100 os.rename(bin_dir, cleanup_path)
88 os.makedirs(cache_dir) 101 except (OSError, IOError):
89 target_zip_filename = download_gsutil(version, cache_dir) 102 cleanup_path = None
90 with zipfile.ZipFile(target_zip_filename, 'r') as target_zip: 103 if cleanup_path:
91 target_zip.extractall(bin_dir) 104 shutil.rmtree(cleanup_path)
105
106 download_dir = os.path.join(instance_dir, 'download')
107 target_zip_filename = download_gsutil(version, instance_dir)
108 with zipfile.ZipFile(target_zip_filename, 'r') as target_zip:
109 target_zip.extractall(download_dir)
110
111 try:
112 os.rename(download_dir, bin_dir)
113 except (OSError, IOError):
114 # Something else did this in parallel.
115 pass
92 116
93 # Final check that the gsutil bin is okay. This should never fail. 117 # Final check that the gsutil bin is okay. This should never fail.
94 if not check_gsutil(gsutil_bin): 118 if not check_gsutil(gsutil_bin):
95 raise InvalidGsutilError() 119 raise InvalidGsutilError()
96
97 return gsutil_bin 120 return gsutil_bin
98 121
99 122
100 def run_gsutil(force_version, fallback, target, args): 123 def run_gsutil(force_version, fallback, target, args, clean=False):
101 if force_version: 124 if force_version:
102 gsutil_bin = ensure_gsutil(force_version, target) 125 gsutil_bin = ensure_gsutil(force_version, target, clean)
103 else: 126 else:
104 gsutil_bin = fallback 127 gsutil_bin = fallback
105 cmd = [sys.executable, gsutil_bin] + args 128 cmd = [sys.executable, gsutil_bin] + args
106 return subprocess.call(cmd) 129 return subprocess.call(cmd)
107 130
108 131
109 def parse_args(): 132 def parse_args():
133 bin_dir = os.environ.get('DEPOT_TOOLS_GSUTIL_BIN_DIR', DEFAULT_BIN_DIR)
134
110 parser = argparse.ArgumentParser() 135 parser = argparse.ArgumentParser()
111 parser.add_argument('--force-version', default='4.13') 136 parser.add_argument('--force-version', default='4.13')
137 parser.add_argument('--clean', action='store_true',
138 help='Clear any existing gsutil package, forcing a new download.')
112 parser.add_argument('--fallback', default=DEFAULT_FALLBACK_GSUTIL) 139 parser.add_argument('--fallback', default=DEFAULT_FALLBACK_GSUTIL)
113 parser.add_argument('--target', default=DEFAULT_BIN_DIR) 140 parser.add_argument('--target', default=bin_dir,
141 help='The target directory to download/store a gsutil version in. '
142 '(default is %(default)s).')
114 parser.add_argument('args', nargs=argparse.REMAINDER) 143 parser.add_argument('args', nargs=argparse.REMAINDER)
115 144
116 args, extras = parser.parse_known_args() 145 args, extras = parser.parse_known_args()
117 if args.args and args.args[0] == '--': 146 if args.args and args.args[0] == '--':
118 args.args.pop(0) 147 args.args.pop(0)
119 if extras: 148 if extras:
120 args.args = extras + args.args 149 args.args = extras + args.args
121 return args.force_version, args.fallback, args.target, args.args 150 return args
122 151
123 152
124 def main(): 153 def main():
125 force_version, fallback, target, args = parse_args() 154 args = parse_args()
126 return run_gsutil(force_version, fallback, target, args) 155 return run_gsutil(args.force_version, args.fallback, args.target, args.args,
156 clean=args.clean)
127 157
128 if __name__ == '__main__': 158 if __name__ == '__main__':
129 sys.exit(main()) 159 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tests/gsutil_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698