Chromium Code Reviews

Side by Side Diff: build/upload_to_google_storage.py

Issue 11664024: Scripts to download files from google storage based on sha1 sums (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 """Script to download files from Google Storage."""
6 import hashlib
7 import os
8 import Queue
9 import re
10 import subprocess
11 import sys
12 import tempfile
13 import threading
14 import time
15 import zipfile
16
17 from optparse import OptionParser
18
19 GSUTIL_DEFAULT_PATH = os.path.join(os.path.dirname(os.path.normpath(__file__)),
20 '..', '..', 'third_party', 'gsutil', 'gsutil')
21
22
23 class Gsutil():
24 def __init__(self, path):
25 if os.path.exists(path):
26 self.path = path
27 else:
28 raise IOError('GSUtil not found in %s' % path)
29
30 def call(self, *args):
31 p = subprocess.Popen((sys.executable, self.path) + args)
32 return p.wait()
33
34 def check_call(self, *args):
35 p = subprocess.Popen((sys.executable, self.path) + args,
36 stdout=subprocess.PIPE,
37 stderr=subprocess.PIPE)
38 code = p.wait()
39 out, err = p.communicate()
40 self.stdout = out
41 self.stderr = err
42
43 if code == 0:
44 return 0
45
46 status_code_match = re.search('status=([0-9]+)', err)
47 if status_code_match:
48 return int(status_code_match.groups(1))
49 elif ('You are attempting to access protected data with '
50 'no configured credentials.' in err):
51 return 403
52 elif 'No such object' in err:
53 return 404
54 else:
55 return code
56
57
58 class GsutilFactory():
59 def __init__(self, path, boto=None):
60 self.path = path
61
62
63 def CheckSHA1(sha1_sum, filename):
64 sha1 = hashlib.sha1()
65 sha1.update(open(filename).read())
66 return sha1_sum == sha1.hexdigest()
67
68 def _upload_worker(thread_num, q, base_url, gsutil, options, md5_lock):
69 while True:
70 try:
71 filename, sha1_sum = q.get_nowait()
72 file_url = '%s/%s' % (base_url, sha1_sum)
73 if gsutil.call('ls', file_url) == 0 and not options.force:
74 # File exists, check MD5 hash.
75 gsutil.call('ls', '-L', file_url)
76 etag_match = re.search('ETag:\s+([a-z0-9]{32})', gsutil.stdout)
77 if etag_match:
78 remote_md5 = etag_match.groups()[0]
79 md5_calculator = hashlib.md5()
80 with md5_lock:
81 md5_calculator.update(open(filename).read())
82 local_md5 = md5_calculator.hexdigest()
83 if local_md5 == remote_md5:
84 print ('File already exists at %s and MD5 matches, exiting' %
85 file_url)
86 continue
87 print 'Uploading %s to %s' % (filename, file_url)
88 code = gsutil.call_interactive('cp', '-q', filename, file_url)
89 if code != 0:
90 print >>sys.stderr, gsutil.stderr
91 continue
92 except Queue.Empty:
93 return
94
95 def main(args):
96 parser = OptionParser()
97 parser.add_option('-d', '--delete', action='store_true', default=False,
98 help='Deletes the target file after upload.')
99 parser.add_option('-b', '--bucket', default='chrome-artifacts',
100 help='Google Storage bucket to fetch from.')
101 parser.add_option('-f', '--force', action='store_true', default=False,
102 help='Force upload even if remote file exists.')
103 parser.add_option('-g', '--gsutil_path', default=GSUTIL_DEFAULT_PATH,
104 help='Path to the gsutil script.')
105 parser.add_option('-t', '--num_threads', default=1, type='int',
106 help='Number of uploader threads to run.')
107 parser.add_option('-s', '--skip_hashing', action='store_true', default=False,
108 help='Skip hashing if .sha1 file exists.')
109 (options, args) = parser.parse_args()
110
111 if len(args) < 1:
112 print >>sys.stderr, 'Missing target.'
113 return 1
114 else:
115 input_filename = args[0]
116 base_url = 'gs://%s' % options.bucket
117 if os.path.exists(options.gsutil_path):
118 gsutil = Gsutil(options.gsutil_path)
119 else:
120 for path in os.environ["PATH"].split(os.pathsep):
121 if os.path.exists(path) and 'gsutil' in os.listdir(path):
122 gsutil = Gsutil(os.path.join(path, 'gsutil'))
123
124 # Check if we have permissions.
125 code = gsutil.call('ls', base_url)
126 if code == 403:
127 code = gsutil.call_interactive('config')
128 if code != 0:
129 print >>sys.stderr, 'Error while authenticating to %s, exiting' % base_url
130 return 403
131 elif code == 404:
132 print >>sys.stderr, '%s not found.' % base_url
133 return 404
134 elif code != 0:
135 print >>sys.stderr, gsutil.stderr
136 return code
137
138 # Enumerate the list of file(s) we want to transfer over.
139 hash_queue = []
140 if input_filename == '-':
141 # Take stdin as a newline-seperated list of files.
142 for line in sys.stdin.readlines():
143 hash_queue.append(line.strip())
144 else:
145 hash_queue.append(input_filename)
146
147 # We want to hash everything in a single thread since its faster.
148 # The bottleneck is in disk IO, not CPU.
149 upload_queue = Queue.Queue()
150 hash_timer = time.time()
151 for filename in hash_queue:
152 if os.path.exists('%s.sha1' % filename) and options.skip_hashing:
153 print 'Found hash for %s, skipping.' % filename
154 upload_queue.put((filename, open('%s.sha1' % filename).read()))
155 continue
156 print 'Calculating hash for %s...' % filename,
157 sha1_calculator = hashlib.sha1()
158 sha1_calculator.update(open(filename).read())
159 sha1_sum = sha1_calculator.hexdigest()
160 with open(filename + '.sha1', 'w') as f:
161 f.write(sha1_sum)
162 print 'done'
163 upload_queue.put((filename, sha1_sum))
164 hash_time = time.time() - hash_timer
165
166 # Start up all the worker threads.
167 all_threads = []
168
169 # We only want one MD5 calculation happening at a time.
170 md5_lock = threading.Lock()
171 upload_timer = time.time()
172
173 for thread_num in range(options.num_threads):
174 t = threading.Thread(target=_upload_worker, args=[thread_num,
175 upload_queue, base_url, gsutil, options, md5_lock])
176 t.daemon = True
177 t.start()
178 all_threads.append(t)
179
180 def _wait_thread(threads, done):
181 for t in threads:
182 t.join()
183 print 'Now we\'re done'
184 done.set()
185
186 # Have a thread set a flag when all the tasks are done.
187 done = threading.Event()
188 done_thread = threading.Thread(target=_wait_thread, args=[all_threads, done])
189 done_thread.daemon = True
190 done_thread.start()
191
192 while not done.is_set():
193 time.sleep(1) # Do a sleep loop so we can ctrl + c out of this anytime.
194 print 'Hashing %s took %1f seconds' % (len(hash_queue), hash_time)
195 print 'Uploading took %1f seconds' % (time.time() - upload_timer)
196
197 if __name__ == '__main__':
198 sys.exit(main(sys.argv))
OLDNEW
« build/download_from_google_storage.py ('K') | « build/download_from_google_storage.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine