OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 """ | 3 """ |
4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
5 | 5 |
6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
7 found in the LICENSE file. | 7 found in the LICENSE file. |
8 | 8 |
9 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
10 """ | 10 """ |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 _SKP_PLATFORMS[0], _SKP_PLATFORMS[1])) | 253 _SKP_PLATFORMS[0], _SKP_PLATFORMS[1])) |
254 file_handle.write('</li>') | 254 file_handle.write('</li>') |
255 | 255 |
256 file_handle.write('\n</ul></body></html>') | 256 file_handle.write('\n</ul></body></html>') |
257 | 257 |
258 | 258 |
259 class Server(object): | 259 class Server(object): |
260 """ HTTP server for our HTML rebaseline viewer. """ | 260 """ HTTP server for our HTML rebaseline viewer. """ |
261 | 261 |
262 def __init__(self, | 262 def __init__(self, |
| 263 actuals_source, |
263 actuals_dir=DEFAULT_ACTUALS_DIR, | 264 actuals_dir=DEFAULT_ACTUALS_DIR, |
264 json_filename=DEFAULT_JSON_FILENAME, | 265 json_filename=DEFAULT_JSON_FILENAME, |
265 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, | |
266 port=DEFAULT_PORT, export=False, editable=True, | 266 port=DEFAULT_PORT, export=False, editable=True, |
267 reload_seconds=0, config_pairs=None, builder_regex_list=None, | 267 reload_seconds=0, config_pairs=None, builder_regex_list=None, |
268 boto_file_path=None, | 268 boto_file_path=None, |
269 imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): | 269 imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): |
270 """ | 270 """ |
271 Args: | 271 Args: |
| 272 actuals_source: actuals_source.get_builders() -> |
| 273 {builder:string -> [ bucket:string, path:string, generation:string ]} |
| 274 If None, don't fetch new actual-results files |
| 275 at all, just compare to whatever files are already in actuals_dir |
272 actuals_dir: directory under which we will check out the latest actual | 276 actuals_dir: directory under which we will check out the latest actual |
273 GM results | 277 GM results |
274 json_filename: basename of the JSON summary file to load for each builder | 278 json_filename: basename of the JSON summary file to load for each builder |
275 gm_summaries_bucket: Google Storage bucket to download json_filename | |
276 files from; if None or '', don't fetch new actual-results files | |
277 at all, just compare to whatever files are already in actuals_dir | |
278 port: which TCP port to listen on for HTTP requests | 279 port: which TCP port to listen on for HTTP requests |
279 export: whether to allow HTTP clients on other hosts to access this server | 280 export: whether to allow HTTP clients on other hosts to access this server |
280 editable: whether HTTP clients are allowed to submit new GM baselines | 281 editable: whether HTTP clients are allowed to submit new GM baselines |
281 (SKP baseline modifications are performed using an entirely different | 282 (SKP baseline modifications are performed using an entirely different |
282 mechanism, not affected by this parameter) | 283 mechanism, not affected by this parameter) |
283 reload_seconds: polling interval with which to check for new results; | 284 reload_seconds: polling interval with which to check for new results; |
284 if 0, don't check for new results at all | 285 if 0, don't check for new results at all |
285 config_pairs: List of (string, string) tuples; for each tuple, compare | 286 config_pairs: List of (string, string) tuples; for each tuple, compare |
286 actual results of these two configs. If None or empty, | 287 actual results of these two configs. If None or empty, |
287 don't compare configs at all. | 288 don't compare configs at all. |
288 builder_regex_list: List of regular expressions specifying which builders | 289 builder_regex_list: List of regular expressions specifying which builders |
289 we will process. If None, process all builders. | 290 we will process. If None, process all builders. |
290 boto_file_path: Path to .boto file giving us credentials to access | 291 boto_file_path: Path to .boto file giving us credentials to access |
291 Google Storage buckets; if None, we will only be able to access | 292 Google Storage buckets; if None, we will only be able to access |
292 public GS buckets. | 293 public GS buckets. |
293 imagediffdb_threads: How many threads to spin up within imagediffdb. | 294 imagediffdb_threads: How many threads to spin up within imagediffdb. |
294 """ | 295 """ |
| 296 self._actuals_source = actuals_source |
295 self._actuals_dir = actuals_dir | 297 self._actuals_dir = actuals_dir |
296 self._json_filename = json_filename | 298 self._json_filename = json_filename |
297 self._gm_summaries_bucket = gm_summaries_bucket | |
298 self._port = port | 299 self._port = port |
299 self._export = export | 300 self._export = export |
300 self._editable = editable | 301 self._editable = editable |
301 self._reload_seconds = reload_seconds | 302 self._reload_seconds = reload_seconds |
302 self._config_pairs = config_pairs or [] | 303 self._config_pairs = config_pairs or [] |
303 self._builder_regex_list = builder_regex_list | 304 self._builder_regex_list = builder_regex_list |
304 self.truncate_results = False | 305 self.truncate_results = False |
305 | 306 |
306 if boto_file_path: | 307 if boto_file_path: |
307 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) | 308 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 the same time. | 379 the same time. |
379 | 380 |
380 Args: | 381 Args: |
381 invalidate: if True, invalidate self._results immediately upon entry; | 382 invalidate: if True, invalidate self._results immediately upon entry; |
382 otherwise, we will let readers see those results until we | 383 otherwise, we will let readers see those results until we |
383 replace them | 384 replace them |
384 """ | 385 """ |
385 with self.results_rlock: | 386 with self.results_rlock: |
386 if invalidate: | 387 if invalidate: |
387 self._results = None | 388 self._results = None |
388 if self._gm_summaries_bucket: | 389 |
| 390 if self._actuals_source: |
389 logging.info( | 391 logging.info( |
390 'Updating GM result summaries in %s from gm_summaries_bucket %s ...' | 392 'Updating GM result summaries in %s from %s ...' |
391 % (self._actuals_dir, self._gm_summaries_bucket)) | 393 % (self._actuals_dir, self._actuals_source.description())) |
392 | 394 |
393 # Clean out actuals_dir first, in case some builders have gone away | 395 # Clean out actuals_dir first, in case some builders have gone away |
394 # since we last ran. | 396 # since we last ran. |
395 if os.path.isdir(self._actuals_dir): | 397 if os.path.isdir(self._actuals_dir): |
396 shutil.rmtree(self._actuals_dir) | 398 shutil.rmtree(self._actuals_dir) |
397 | 399 |
398 # Get the list of builders we care about. | 400 # Get the list of actuals we care about. |
399 all_builders = download_actuals.get_builders_list( | 401 all_actuals = self._actuals_source.get_builders() |
400 summaries_bucket=self._gm_summaries_bucket) | 402 |
401 if self._builder_regex_list: | 403 if self._builder_regex_list: |
402 matching_builders = [] | 404 matching_builders = [] |
403 for builder in all_builders: | 405 for builder in all_actuals: |
404 for regex in self._builder_regex_list: | 406 for regex in self._builder_regex_list: |
405 if re.match(regex, builder): | 407 if re.match(regex, builder): |
406 matching_builders.append(builder) | 408 matching_builders.append(builder) |
407 break # go on to the next builder, no need to try more regexes | 409 break # go on to the next builder, no need to try more regexes |
408 else: | 410 else: |
409 matching_builders = all_builders | 411 matching_builders = all_actuals.keys() |
410 | 412 |
411 # Download the JSON file for each builder we care about. | 413 # Download the JSON file for each builder we care about. |
412 # | 414 # |
413 # TODO(epoger): When this is a large number of builders, we would be | 415 # TODO(epoger): When this is a large number of builders, we would be |
414 # better off downloading them in parallel! | 416 # better off downloading them in parallel! |
415 for builder in matching_builders: | 417 for builder in matching_builders: |
416 self._gs.download_file( | 418 self._gs.download_file( |
417 source_bucket=self._gm_summaries_bucket, | 419 source_bucket=all_actuals[builder].bucket, |
418 source_path=posixpath.join(builder, self._json_filename), | 420 source_path=all_actuals[builder].path, |
| 421 source_generation=all_actuals[builder].generation, |
419 dest_path=os.path.join(self._actuals_dir, builder, | 422 dest_path=os.path.join(self._actuals_dir, builder, |
420 self._json_filename), | 423 self._json_filename), |
421 create_subdirs_if_needed=True) | 424 create_subdirs_if_needed=True) |
422 | 425 |
423 # We only update the expectations dir if the server was run with a | 426 # We only update the expectations dir if the server was run with a |
424 # nonzero --reload argument; otherwise, we expect the user to maintain | 427 # nonzero --reload argument; otherwise, we expect the user to maintain |
425 # her own expectations as she sees fit. | 428 # her own expectations as she sees fit. |
426 # | 429 # |
427 # Because the Skia repo is hosted using git, and git does not | 430 # Because the Skia repo is hosted using git, and git does not |
428 # support updating a single directory tree, we have to update the entire | 431 # support updating a single directory tree, we have to update the entire |
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
892 parser.add_argument('--editable', action='store_true', | 895 parser.add_argument('--editable', action='store_true', |
893 help=('Allow HTTP clients to submit new GM baselines; ' | 896 help=('Allow HTTP clients to submit new GM baselines; ' |
894 'SKP baselines can be edited regardless of this ' | 897 'SKP baselines can be edited regardless of this ' |
895 'setting.')) | 898 'setting.')) |
896 parser.add_argument('--export', action='store_true', | 899 parser.add_argument('--export', action='store_true', |
897 help=('Instead of only allowing access from HTTP clients ' | 900 help=('Instead of only allowing access from HTTP clients ' |
898 'on localhost, allow HTTP clients on other hosts ' | 901 'on localhost, allow HTTP clients on other hosts ' |
899 'to access this server. WARNING: doing so will ' | 902 'to access this server. WARNING: doing so will ' |
900 'allow users on other hosts to modify your ' | 903 'allow users on other hosts to modify your ' |
901 'GM expectations, if combined with --editable.')) | 904 'GM expectations, if combined with --editable.')) |
| 905 parser.add_argument('--rietveld-issue', |
| 906 help=('Download json_filename files from latest trybot' |
| 907 'runs on this codereview.chromium.org issue.' |
| 908 'Overrides --gm-summaries-bucket.')) |
902 parser.add_argument('--gm-summaries-bucket', | 909 parser.add_argument('--gm-summaries-bucket', |
903 help=('Google Cloud Storage bucket to download ' | 910 help=('Google Cloud Storage bucket to download ' |
904 'JSON_FILENAME files from. ' | 911 'JSON_FILENAME files from. ' |
905 'Defaults to %(default)s ; if set to ' | 912 'Defaults to %(default)s ; if set to ' |
906 'empty string, just compare to actual-results ' | 913 'empty string, just compare to actual-results ' |
907 'already found in ACTUALS_DIR.'), | 914 'already found in ACTUALS_DIR.'), |
908 default=DEFAULT_GM_SUMMARIES_BUCKET) | 915 default=DEFAULT_GM_SUMMARIES_BUCKET) |
909 parser.add_argument('--json-filename', | 916 parser.add_argument('--json-filename', |
910 help=('JSON summary filename to read for each builder; ' | 917 help=('JSON summary filename to read for each builder; ' |
911 'defaults to %(default)s.'), | 918 'defaults to %(default)s.'), |
(...skipping 17 matching lines...) Expand all Loading... |
929 default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) | 936 default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) |
930 parser.add_argument('--truncate', action='store_true', | 937 parser.add_argument('--truncate', action='store_true', |
931 help=('FOR TESTING ONLY: truncate the set of images we ' | 938 help=('FOR TESTING ONLY: truncate the set of images we ' |
932 'process, to speed up testing.')) | 939 'process, to speed up testing.')) |
933 args = parser.parse_args() | 940 args = parser.parse_args() |
934 if args.compare_configs: | 941 if args.compare_configs: |
935 config_pairs = CONFIG_PAIRS_TO_COMPARE | 942 config_pairs = CONFIG_PAIRS_TO_COMPARE |
936 else: | 943 else: |
937 config_pairs = None | 944 config_pairs = None |
938 | 945 |
| 946 if args.rietveld_issue: |
| 947 actuals_source = download_actuals.RietveldIssueActuals(args.rietveld_issue, |
| 948 args.json_filename) |
| 949 else: |
| 950 actuals_source = download_actuals.TipOfTreeActuals(args.gm_summaries_bucket, |
| 951 args.json_filename) |
| 952 |
939 global _SERVER | 953 global _SERVER |
940 _SERVER = Server(actuals_dir=args.actuals_dir, | 954 _SERVER = Server(actuals_source, |
| 955 actuals_dir=args.actuals_dir, |
941 json_filename=args.json_filename, | 956 json_filename=args.json_filename, |
942 gm_summaries_bucket=args.gm_summaries_bucket, | |
943 port=args.port, export=args.export, editable=args.editable, | 957 port=args.port, export=args.export, editable=args.editable, |
944 reload_seconds=args.reload, config_pairs=config_pairs, | 958 reload_seconds=args.reload, config_pairs=config_pairs, |
945 builder_regex_list=args.builders, boto_file_path=args.boto, | 959 builder_regex_list=args.builders, boto_file_path=args.boto, |
946 imagediffdb_threads=args.threads) | 960 imagediffdb_threads=args.threads) |
947 if args.truncate: | 961 if args.truncate: |
948 _SERVER.truncate_results = True | 962 _SERVER.truncate_results = True |
949 _SERVER.run() | 963 _SERVER.run() |
950 | 964 |
951 | 965 |
952 if __name__ == '__main__': | 966 if __name__ == '__main__': |
953 main() | 967 main() |
OLD | NEW |