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

Side by Side Diff: Tools/AutoSheriff/buildbot.py

Issue 398823008: WIP: Add auto-sheriff.appspot.com code to Blink Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 5 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
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import collections
6 import json
7 import logging
8 import operator
9 import os
10 import requests
11 import urlparse
12 import string_helpers
13
14
15 # Python logging is stupidly verbose to configure.
ojan 2014/07/22 02:01:24 Not really a useful comment.
16 def setup_logging():
17 logger = logging.getLogger(__name__)
18 logger.setLevel(logging.DEBUG)
19 handler = logging.StreamHandler()
20 handler.setLevel(logging.DEBUG)
21 formatter = logging.Formatter('%(levelname)s: %(message)s')
22 handler.setFormatter(formatter)
23 logger.addHandler(handler)
24 return logger, handler
25
26
27 log, logging_handler = setup_logging()
28
29
30 CBE_BASE = 'https://chrome-build-extract.appspot.com'
31
32 # Unclear if this should be specific to builds.
33 class BuildCache(object):
34 def __init__(self, root_path):
35 self.root_path = root_path
36
37 # Could be in operator.
38 def has(self, key):
39 path = os.path.join(self.root_path, key)
40 return os.path.exists(path)
41
42 # Could be attr getter.
43 def get(self, key):
44 path = os.path.join(self.root_path, key)
45 if not self.has(path):
46 return None
47 with open(path) as cached:
48 return json.load(cached)
49
50 # Could be attr setter.
51 def set(self, key, json_object):
52 path = os.path.join(self.root_path, key)
53 cache_dir = os.path.dirname(path)
54 if not os.path.exists(cache_dir):
55 os.makedirs(cache_dir)
56 with open(path, 'w') as cached:
57 cached.write(json.dumps(json_object))
58
59
60 def master_name_from_url(master_url):
61 return urlparse.urlparse(master_url).path.split('/')[-1]
62
63
64 def cache_key_for_build(master_url, builder_name, build_number):
65 master_name = master_name_from_url(master_url)
66 return os.path.join(master_name, builder_name, "%s.json" % build_number)
67
68
69 def fetch_master_json(master_url):
70 master_name = master_name_from_url(master_url)
71 url = '%s/get_master/%s' % (CBE_BASE, master_name)
72 return requests.get(url).json()
73
74
75 def prefill_builds_cache(cache, master_url, builder_name):
76 master_name = master_name_from_url(master_url)
77 builds_url = '%s/get_builds' % CBE_BASE
78 params = { 'master': master_name, 'builder': builder_name }
79 response = requests.get(builds_url, params=params)
80 builds = response.json()['builds']
81 for build in builds:
82 if not build.get('number'):
83 index = builds.index(build)
84 log.error('build at index %s in %s missing number?' % (index, respon se.url))
85 continue
86 build_number = build['number']
87 key = cache_key_for_build(master_url, builder_name, build_number)
88 cache.set(key, build)
89 build_numbers = map(operator.itemgetter('number'), builds)
90 log.debug('Prefilled (%.1fs) %s for %s %s' %
91 (response.elapsed.total_seconds(),
92 string_helpers.re_range(build_numbers),
93 master_name, builder_name))
94 return build_numbers
95
96
97 def fetch_and_cache_build(cache, url, cache_key):
98 log.debug('Fetching %s.' % url)
99 try:
100 build = requests.get(url).json()
101 # Don't cache builds which are just errors?
102 if build.get('number'):
103 if build.get('eta') is None:
104 cache.set(cache_key, build)
105 else:
106 log.debug('Not caching in-progress build from %s.')
107 return build
108 except ValueError, e:
109 log.error('Not caching invalid json: %s: %s' % (url, e))
110
111
112 def fetch_build_json(cache, master_url, builder_name, build_number):
113 cache_key = cache_key_for_build(master_url, builder_name, build_number)
114 build = cache.get(cache_key)
115 # I accidentally stored some error builds and incomplete builds before.
116 if build and (not build.get('number') or build.get('eta')):
117 log.warn('Refetching %s %s %s' % (master_url, builder_name, build_number))
118 build = None
119
120 master_name = master_name_from_url(master_url)
121
122 cbe_url = "https://chrome-build-extract.appspot.com/p/%s/builders/%s/builds/%s ?json=1" % (
123 master_name, builder_name, build_number)
124 if not build:
125 build = fetch_and_cache_build(cache, cbe_url, cache_key)
126
127 if not build:
128 log.warn("CBE failed, failover to buildbot %s" % cbe_url)
129 buildbot_url = "https://build.chromium.org/p/%s/json/builders/%s/builds/%s" % (
130 master_name, builder_name, build_number)
131 build = fetch_and_cache_build(cache, buildbot_url, cache_key)
132
133 return build
134
135
136 # This effectively extracts the 'configuration' of the build
137 # we could extend this beyond repo versions in the future.
138 def revisions_from_build(build_json):
139 def _property_value(build_json, property_name):
140 for prop_tuple in build_json['properties']:
141 if prop_tuple[0] == property_name:
142 return prop_tuple[1]
143
144 REVISION_VARIABLES = [
145 ('chromium', 'got_revision'),
146 ('blink', 'got_webkit_revision'),
147 ('v8', 'got_v8_revision'),
148 ('nacl', 'got_nacl_revision'),
149 # Skia, for whatever reason, isn't exposed in the buildbot properties so
150 # don't bother to include it here.
151 ]
152
153 revisions = {}
154 for repo_name, buildbot_property in REVISION_VARIABLES:
155 # This is epicly stupid: 'tester' builders have the wrong
156 # revision for 'got_foo_revision' and we have to use
157 # parent_got_foo_revision instead, but non-tester builders
158 # don't have the parent_ versions, so we have to fall back
159 # to got_foo_revision in those cases!
160 # Don't even think about using 'revision' that's wrong too.
ojan 2014/07/22 02:01:24 Lol! This should perhaps be a FIXME? Theoreticall
161 revision = _property_value(build_json, 'parent_' + buildbot_property)
162 if not revision:
163 revision = _property_value(build_json, buildbot_property)
164 revisions[repo_name] = revision
165 return revisions
166
167
168 def latest_revisions_for_master(cache, master_url, master_json):
169 latest_revisions = collections.defaultdict(dict)
170 master_name = master_name_from_url(master_url)
171 for builder_name, builder_json in master_json['builders'].items():
172 # recent_builds can include current builds
173 recent_builds = set(builder_json['cachedBuilds'])
174 active_builds = set(builder_json['currentBuilds'])
175 last_finished_id = sorted(recent_builds - active_builds, reverse=True)[0]
176 last_build = fetch_build_json(cache, master_url, builder_name, last_finished _id)
177 latest_revisions[master_name][builder_name] = revisions_from_build(last_buil d)
178 return latest_revisions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698