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

Side by Side Diff: tools/isolate/run_test_from_archive.py

Issue 10880009: Enforces strict manifest content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Implemented lower-overhead straight forward technique Created 8 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 | tools/isolate/run_test_from_archive_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 (c) 2012 The Chromium Authors. All rights reserved. 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 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 """Reads a manifest, creates a tree of hardlinks and runs the test. 6 """Reads a manifest, creates a tree of hardlinks and runs the test.
7 7
8 Keeps a local cache. 8 Keeps a local cache.
9 """ 9 """
10 10
11 import ctypes 11 import ctypes
12 import json 12 import json
13 import logging 13 import logging
14 import optparse 14 import optparse
15 import os 15 import os
16 import re 16 import re
17 import shutil 17 import shutil
18 import stat 18 import stat
19 import subprocess 19 import subprocess
20 import sys 20 import sys
21 import tempfile 21 import tempfile
22 import time 22 import time
23 import urllib 23 import urllib
24 24
25 25
26 # Types of action accepted by recreate_tree(). 26 # Types of action accepted by recreate_tree().
27 HARDLINK, SYMLINK, COPY = range(1, 4) 27 HARDLINK, SYMLINK, COPY = range(1, 4)
28 28
29 RE_IS_SHA1 = re.compile(r'^[a-fA-F0-9]{40}$')
30
31
32 class ConfigError(ValueError):
33 """Generic failure to load a manifest."""
34 pass
35
29 36
30 class MappingError(OSError): 37 class MappingError(OSError):
31 """Failed to recreate the tree.""" 38 """Failed to recreate the tree."""
32 pass 39 pass
33 40
34 41
35 def os_link(source, link_name): 42 def os_link(source, link_name):
36 """Add support for os.link() on Windows.""" 43 """Add support for os.link() on Windows."""
37 if sys.platform == 'win32': 44 if sys.platform == 'win32':
38 if not ctypes.windll.kernel32.CreateHardLinkW( 45 if not ctypes.windll.kernel32.CreateHardLinkW(
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 172
166 173
167 def make_temp_dir(prefix, root_dir): 174 def make_temp_dir(prefix, root_dir):
168 """Returns a temporary directory on the same file system as root_dir.""" 175 """Returns a temporary directory on the same file system as root_dir."""
169 base_temp_dir = None 176 base_temp_dir = None
170 if not is_same_filesystem(root_dir, tempfile.gettempdir()): 177 if not is_same_filesystem(root_dir, tempfile.gettempdir()):
171 base_temp_dir = os.path.dirname(root_dir) 178 base_temp_dir = os.path.dirname(root_dir)
172 return tempfile.mkdtemp(prefix=prefix, dir=base_temp_dir) 179 return tempfile.mkdtemp(prefix=prefix, dir=base_temp_dir)
173 180
174 181
182 def load_manifest(content):
183 """Verifies the manifest is valid and loads this object with the json data.
184 """
185 data = json.loads(content)
186 if not isinstance(data, dict):
187 raise ConfigError('Expected dict, got %r' % data)
188
189 for key, value in data.iteritems():
190 if key == 'command':
191 if not isinstance(value, list):
192 raise ConfigError('Expected list, got %r' % value)
193 for subvalue in value:
194 if not isinstance(subvalue, basestring):
195 raise ConfigError('Expected string, got %r' % subvalue)
196
197 elif key == 'files':
198 if not isinstance(value, dict):
199 raise ConfigError('Expected dict, got %r' % value)
200 for subkey, subvalue in value.iteritems():
201 if not isinstance(subkey, basestring):
202 raise ConfigError('Expected string, got %r' % subkey)
203 if not isinstance(subvalue, dict):
204 raise ConfigError('Expected dict, got %r' % subvalue)
205 for subsubkey, subsubvalue in subvalue.iteritems():
M-A Ruel 2012/08/28 20:51:02 The main issue is that it's much easier to make a
206 if subsubkey == 'link':
207 if not isinstance(subsubvalue, basestring):
208 raise ConfigError('Expected string, got %r' % subsubvalue)
209 elif subsubkey == 'mode':
210 if not isinstance(subsubvalue, int):
211 raise ConfigError('Expected int, got %r' % subsubvalue)
212 elif subsubkey == 'sha-1':
213 if not RE_IS_SHA1.match(subsubvalue):
214 raise ConfigError('Expected sha-1, got %r' % subsubvalue)
215 elif subsubkey == 'timestamp':
216 if not isinstance(subsubvalue, int):
217 raise ConfigError('Expected int, got %r' % subsubvalue)
218 else:
219 raise ConfigError('Unknown key %s' % subsubkey)
220 if bool('sha-1' in subvalue) and bool('link' in subvalue):
221 raise ConfigError(
222 'Did not expect both \'sha-1\' and \'link\', got: %r' % subvalue)
223
224 elif key == 'read_only':
225 if not isinstance(value, bool):
226 raise ConfigError('Expected bool, got %r' % value)
227
228 elif key == 'relative_cwd':
229 if not isinstance(value, basestring):
230 raise ConfigError('Expected string, got %r' % value)
231
232 else:
233 raise ConfigError('Unknown key %s' % subkey)
234
235 return data
236
237
175 def fix_python_path(cmd): 238 def fix_python_path(cmd):
176 """Returns the fixed command line to call the right python executable.""" 239 """Returns the fixed command line to call the right python executable."""
177 out = cmd[:] 240 out = cmd[:]
178 if out[0] == 'python': 241 if out[0] == 'python':
179 out[0] = sys.executable 242 out[0] = sys.executable
180 elif out[0].endswith('.py'): 243 elif out[0].endswith('.py'):
181 out.insert(0, sys.executable) 244 out.insert(0, sys.executable)
182 return out 245 return out
183 246
184 247
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 os.makedirs(outfiledir) 450 os.makedirs(outfiledir)
388 if 'sha-1' in properties: 451 if 'sha-1' in properties:
389 # A normal file. 452 # A normal file.
390 infile = properties['sha-1'] 453 infile = properties['sha-1']
391 cache.retrieve(infile) 454 cache.retrieve(infile)
392 link_file(outfile, cache.path(infile), HARDLINK) 455 link_file(outfile, cache.path(infile), HARDLINK)
393 elif 'link' in properties: 456 elif 'link' in properties:
394 # A symlink. 457 # A symlink.
395 os.symlink(properties['link'], outfile) 458 os.symlink(properties['link'], outfile)
396 else: 459 else:
397 raise ValueError('Unexpected entry: %s' % properties) 460 raise ConfigError('Unexpected entry: %s' % properties)
398 if 'mode' in properties: 461 if 'mode' in properties:
399 # It's not set on Windows. 462 # It's not set on Windows.
400 os.chmod(outfile, properties['mode']) 463 os.chmod(outfile, properties['mode'])
401 464
402 cwd = os.path.join(outdir, manifest.get('relative_cwd', '')) 465 cwd = os.path.join(outdir, manifest.get('relative_cwd', ''))
403 if not os.path.isdir(cwd): 466 if not os.path.isdir(cwd):
404 os.makedirs(cwd) 467 os.makedirs(cwd)
405 if manifest.get('read_only'): 468 if manifest.get('read_only'):
406 make_writable(outdir, True) 469 make_writable(outdir, True)
407 cmd = manifest['command'] 470 cmd = manifest['command']
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 parser.error('One and only one of --manifest or --hash is required.') 539 parser.error('One and only one of --manifest or --hash is required.')
477 if not options.remote: 540 if not options.remote:
478 parser.error('--remote is required.') 541 parser.error('--remote is required.')
479 if args: 542 if args:
480 parser.error('Unsupported args %s' % ' '.join(args)) 543 parser.error('Unsupported args %s' % ' '.join(args))
481 544
482 if options.hash: 545 if options.hash:
483 # First calculate the reference to it. 546 # First calculate the reference to it.
484 options.manifest = '%s/%s' % (options.remote.rstrip('/'), options.hash) 547 options.manifest = '%s/%s' % (options.remote.rstrip('/'), options.hash)
485 try: 548 try:
486 manifest = json.load(open_remote(options.manifest)) 549 manifest = load_manifest(open_remote(options.manifest).read())
487 except IOError as e: 550 except IOError as e:
488 parser.error( 551 parser.error(
489 'Failed to read manifest %s; remote:%s; hash:%s; %s' % 552 'Failed to read manifest %s; remote:%s; hash:%s; %s' %
490 (options.manifest, options.remote, options.hash, str(e))) 553 (options.manifest, options.remote, options.hash, str(e)))
491 554
492 policies = CachePolicies( 555 policies = CachePolicies(
493 options.max_cache_size, options.min_free_space, options.max_items) 556 options.max_cache_size, options.min_free_space, options.max_items)
494 try: 557 try:
495 return run_tha_test( 558 return run_tha_test(
496 manifest, 559 manifest,
497 os.path.abspath(options.cache), 560 os.path.abspath(options.cache),
498 options.remote, 561 options.remote,
499 policies) 562 policies)
500 except MappingError, e: 563 except (ConfigError, MappingError), e:
501 print >> sys.stderr, str(e) 564 print >> sys.stderr, str(e)
502 return 1 565 return 1
503 566
504 567
505 if __name__ == '__main__': 568 if __name__ == '__main__':
506 sys.exit(main()) 569 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/isolate/run_test_from_archive_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698