| OLD | NEW |
| (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 | |
| 6 """ Upload actual GM results to the cloud to allow for rebaselining.""" | |
| 7 | |
| 8 # TODO(mtklein): Delete most of this and s/GM/DM/g the rest when GM's gone. | |
| 9 | |
| 10 from build_step import BuildStep | |
| 11 from utils import gs_utils | |
| 12 from utils import old_gs_utils | |
| 13 import datetime | |
| 14 import os | |
| 15 import posixpath | |
| 16 import re | |
| 17 import shutil | |
| 18 import skia_vars | |
| 19 import sys | |
| 20 import tempfile | |
| 21 | |
| 22 | |
| 23 GS_SUMMARIES_BUCKET = 'gs://chromium-skia-gm-summaries' | |
| 24 | |
| 25 | |
| 26 class UploadGMResults(BuildStep): | |
| 27 def __init__(self, timeout=4800, **kwargs): | |
| 28 super(UploadGMResults, self).__init__(timeout=timeout, **kwargs) | |
| 29 | |
| 30 def _GSUploadAllImages(self, src_dir): | |
| 31 """Upload all image files from src_dir to Google Storage. | |
| 32 We know that GM wrote out these image files with a filename pattern we | |
| 33 can use to generate the checksum-based Google Storage paths.""" | |
| 34 all_files = sorted(os.listdir(src_dir)) | |
| 35 def filematcher(filename): | |
| 36 return filename.endswith('.png') | |
| 37 files_to_upload = filter(filematcher, all_files) | |
| 38 print 'Uploading %d GM-actual files to Google Storage...' % ( | |
| 39 len(files_to_upload)) | |
| 40 if not files_to_upload: | |
| 41 return | |
| 42 filename_pattern = re.compile('^([^_]+)_(.+)_([^_]+)\.png$') | |
| 43 | |
| 44 gm_actuals_subdir = 'gm' | |
| 45 temp_root = tempfile.mkdtemp() | |
| 46 try: | |
| 47 # Copy all of the desired files to a staging dir, with new filenames. | |
| 48 for filename in files_to_upload: | |
| 49 match = filename_pattern.match(filename) | |
| 50 if not match: | |
| 51 print 'Warning: found no images matching pattern "%s"' % filename | |
| 52 continue | |
| 53 (hashtype, test, hashvalue) = match.groups() | |
| 54 src_filepath = os.path.join(src_dir, filename) | |
| 55 temp_dir = os.path.join(temp_root, gm_actuals_subdir, hashtype, test) | |
| 56 if not os.path.isdir(temp_dir): | |
| 57 os.makedirs(temp_dir) | |
| 58 shutil.copy(src_filepath, os.path.join(temp_dir, hashvalue + '.png')) | |
| 59 | |
| 60 # Upload the entire staging dir to Google Storage. | |
| 61 # At present, this will merge the entire contents of [temp_root]/gm | |
| 62 # into the existing contents of gs://chromium-skia-gm/gm . | |
| 63 # | |
| 64 # TODO(epoger): Add a "noclobber" mode to gs_utils.upload_dir_contents() | |
| 65 # and use it here so we don't re-upload image files we already have | |
| 66 # in Google Storage. | |
| 67 bucket_url = gs_utils.GSUtils.with_gs_prefix( | |
| 68 skia_vars.GetGlobalVariable('googlestorage_bucket')) | |
| 69 old_gs_utils.upload_dir_contents( | |
| 70 local_src_dir=os.path.abspath( | |
| 71 os.path.join(temp_root, gm_actuals_subdir)), | |
| 72 remote_dest_dir=posixpath.join(bucket_url, gm_actuals_subdir), | |
| 73 gs_acl='public-read', | |
| 74 http_header_lines=['Cache-Control:public,max-age=3600']) | |
| 75 finally: | |
| 76 shutil.rmtree(temp_root) | |
| 77 | |
| 78 def _GSUploadJsonFiles(self, src_dir, step_name=None): | |
| 79 """Upload just the JSON files within src_dir to GS_SUMMARIES_BUCKET. | |
| 80 | |
| 81 Args: | |
| 82 src_dir: (string) directory to upload contents of | |
| 83 step_name: (string) name of the step that is performing this action; | |
| 84 defaults to self.__class__.__name__ | |
| 85 """ | |
| 86 if not step_name: | |
| 87 step_name = self.__class__.__name__ | |
| 88 all_files = sorted(os.listdir(src_dir)) | |
| 89 def filematcher(filename): | |
| 90 return filename.endswith('.json') | |
| 91 files_to_upload = filter(filematcher, all_files) | |
| 92 print 'Uploading %d JSON files to Google Storage: %s...' % ( | |
| 93 len(files_to_upload), files_to_upload) | |
| 94 gs_dest_dir = posixpath.join( | |
| 95 GS_SUMMARIES_BUCKET, self._args['builder_name']) | |
| 96 for filename in files_to_upload: | |
| 97 src_path = os.path.join(src_dir, filename) | |
| 98 gs_dest_path = posixpath.join(gs_dest_dir, filename) | |
| 99 old_gs_utils.upload_file( | |
| 100 local_src_path=src_path, remote_dest_path=gs_dest_path, | |
| 101 gs_acl='public-read', only_if_modified=True, | |
| 102 http_header_lines=['Cache-Control:public,max-age=3600']) | |
| 103 | |
| 104 def _UploadDMResults(self): | |
| 105 # self._dm_dir holds a bunch of <hash>.png files and one dm.json. | |
| 106 | |
| 107 # Upload the dm.json summary file. | |
| 108 summary_dir = tempfile.mkdtemp() | |
| 109 shutil.move(os.path.join(self._dm_dir, 'dm.json'), | |
| 110 os.path.join(summary_dir, 'dm.json')) | |
| 111 | |
| 112 # /dm-json-v1/year/month/day/hour/git-hash/builder/build-number | |
| 113 now = datetime.datetime.utcnow() | |
| 114 summary_dest_dir = '/'.join(['dm-json-v1', | |
| 115 str(now.year).zfill(4), | |
| 116 str(now.month).zfill(2), | |
| 117 str(now.day).zfill(2), | |
| 118 str(now.hour).zfill(2), | |
| 119 self._got_revision, | |
| 120 self._builder_name, | |
| 121 self._build_number]) | |
| 122 # Trybot results are further siloed by CL. | |
| 123 if self._is_try: | |
| 124 summary_dest_dir = '/'.join(['trybot', | |
| 125 summary_dest_dir, | |
| 126 self._args['issue_number']]) | |
| 127 | |
| 128 gs = gs_utils.GSUtils() | |
| 129 acl = gs.PLAYBACK_CANNED_ACL # Private, | |
| 130 fine_acls = gs.PLAYBACK_FINEGRAINED_ACL_LIST # but Google-visible. | |
| 131 | |
| 132 gs.upload_dir_contents( | |
| 133 source_dir=summary_dir, | |
| 134 dest_bucket=skia_vars.GetGlobalVariable('dm_summaries_bucket'), | |
| 135 dest_dir=summary_dest_dir, | |
| 136 upload_if=gs.UploadIf.ALWAYS, | |
| 137 predefined_acl=acl, | |
| 138 fine_grained_acl_list=fine_acls) | |
| 139 | |
| 140 # Now we can upload everything that's left, all the .pngs. | |
| 141 gs.upload_dir_contents( | |
| 142 source_dir=self._dm_dir, | |
| 143 dest_bucket=skia_vars.GetGlobalVariable('dm_images_bucket'), | |
| 144 dest_dir='dm-images-v1', | |
| 145 upload_if=gs.UploadIf.IF_NEW, | |
| 146 predefined_acl=acl, | |
| 147 fine_grained_acl_list=fine_acls) | |
| 148 | |
| 149 # Just for hygiene, put dm.json back. | |
| 150 shutil.move(os.path.join(summary_dir, 'dm.json'), | |
| 151 os.path.join(self._dm_dir, 'dm.json')) | |
| 152 os.rmdir(summary_dir) | |
| 153 | |
| 154 def _Run(self): | |
| 155 # TODO(epoger): Can't we get gm_output_dir from BuildStep._gm_actual_dir ? | |
| 156 gm_output_dir = os.path.join(os.pardir, os.pardir, 'gm', 'actual', | |
| 157 self._args['builder_name']) | |
| 158 self._GSUploadAllImages(src_dir=gm_output_dir) | |
| 159 self._GSUploadJsonFiles(src_dir=gm_output_dir) | |
| 160 self._UploadDMResults() | |
| 161 | |
| 162 | |
| 163 if '__main__' == __name__: | |
| 164 sys.exit(BuildStep.RunBuildStep(UploadGMResults)) | |
| OLD | NEW |