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

Side by Side Diff: scripts/slave/recipe_modules/cipd/resources/bootstrap.py

Issue 1193813004: cipd recipe_module (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: remove unused imports, pass bin_path in as a data param Created 5 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
OLDNEW
(Empty)
1 # Copyright 2015 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 errno
6 import hashlib
7 import httplib
8 import json
9 import os
10 import sys
11 import tempfile
12 import time
13 import traceback
14 import urllib
15 import urllib2
16
17
18 # Default package repository URL.
19 CIPD_BACKEND_URL = 'https://chrome-infra-packages.appspot.com'
20
21
22 class CipdBootstrapError(Exception):
23 """Raised by install_cipd_client on fatal error."""
24
25 def install_cipd_client(path, package, version):
26 """Installs CIPD client to <path>/cipd.
27
28 Args:
29 path: root directory to install CIPD client into.
30 package: cipd client package name, e.g. infra/tools/cipd/linux-amd64.
31 version: version of the package to install.
32
33 Returns:
34 Absolute path to CIPD executable.
35 """
36 print('Ensuring CIPD client is up-to-date')
37 version_file = os.path.join(path, 'VERSION')
38 bin_file = os.path.join(path, 'cipd')
39
40 # Resolve version to concrete instance ID, e.g "live" -> "abcdef0123....".
41 instance_id = call_cipd_api(
42 'repo/v1/instance/resolve',
43 {'package_name': package, 'version': version})['instance_id']
44 print('CIPD client %s => %s', version, instance_id)
45
46 # Already installed?
47 installed_instance_id = (read_file(version_file) or '').strip()
48 if installed_instance_id == instance_id and os.path.exists(bin_file):
49 return bin_file
50
51 # Resolve instance ID to an URL to fetch client binary from.
52 client_info = call_cipd_api(
53 'repo/v1/client',
54 {'package_name': package, 'instance_id': instance_id})
55 print('CIPD client binary info:\n%s', dump_json(client_info))
56
57 # Fetch the client. It is ~10 MB, so don't bother and fetch it into memory.
58 status, raw_client_bin = fetch_url(client_info['client_binary']['fetch_url'])
59 if status != 200:
60 print('Failed to fetch client binary, HTTP %d' % status)
61 raise CipdBootstrapError('Failed to fetch client binary, HTTP %d' % status)
62 digest = hashlib.sha1(raw_client_bin).hexdigest()
63 if digest != client_info['client_binary']['sha1']:
64 raise CipdBootstrapError('Client SHA1 mismatch')
65
66 # Success.
67 print('Fetched CIPD client %s:%s at %s', package, instance_id, bin_file)
68 write_file(bin_file, raw_client_bin)
69 os.chmod(bin_file, 0755)
70 write_file(version_file, instance_id + '\n')
71 return bin_file
72
73
74 def call_cipd_api(endpoint, query):
75 """Sends GET request to CIPD backend, parses JSON response."""
76 url = '%s/_ah/api/%s' % (CIPD_BACKEND_URL, endpoint)
77 if query:
78 url += '?' + urllib.urlencode(query)
79 status, body = fetch_url(url)
80 if status != 200:
81 raise CipdBootstrapError('Server replied with HTTP %d' % status)
82 try:
83 body = json.loads(body)
84 except ValueError:
85 raise CipdBootstrapError('Server returned invalid JSON')
86 status = body.get('status')
87 if status != 'SUCCESS':
88 m = body.get('error_message') or '<no error message>'
89 raise CipdBootstrapError('Server replied with error %s: %s' % (status, m))
90 return body
91
92
93 def fetch_url(url, headers=None):
94 """Sends GET request (with retries).
95
96 Args:
97 url: URL to fetch.
98 headers: dict with request headers.
99
100 Returns:
101 (200, reply body) on success.
102 (HTTP code, None) on HTTP 401, 403, or 404 reply.
103
104 Raises:
105 Whatever urllib2 raises.
106 """
107 req = urllib2.Request(url)
108 req.add_header('User-Agent', 'cipd recipe bootstrap.py')
109 for k, v in (headers or {}).iteritems():
110 req.add_header(str(k), str(v))
111 i = 0
112 while True:
113 i += 1
114 try:
115 print('GET %s', url)
116 return 200, urllib2.urlopen(req, timeout=60).read()
117 except Exception as e:
118 if isinstance(e, urllib2.HTTPError):
119 print('Failed to fetch %s, server returned HTTP %d', url, e.code)
120 if e.code in (401, 403, 404):
121 return e.code, None
122 else:
123 print('Failed to fetch %s', url)
124 if i == 20:
125 raise
126 print('Retrying in %d sec.', i)
127 time.sleep(i)
128
129
130 def ensure_directory(path):
131 """Creates a directory."""
132 # Handle a case where a file is being converted into a directory.
133 chunks = path.split(os.sep)
134 for i in xrange(len(chunks)):
135 p = os.sep.join(chunks[:i+1])
136 if os.path.exists(p) and not os.path.isdir(p):
137 os.remove(p)
138 break
139 try:
140 os.makedirs(path)
141 except OSError as e:
142 if e.errno != errno.EEXIST:
143 raise
144
145
146 def read_file(path):
147 """Returns contents of a file or None if missing."""
148 try:
149 with open(path, 'r') as f:
150 return f.read()
151 except IOError as e:
152 if e.errno == errno.ENOENT:
153 return None
154 raise
155
156
157 def write_file(path, data):
158 """Puts a file on disk, atomically."""
159 assert sys.platform in ('linux2', 'darwin')
160 ensure_directory(os.path.dirname(path))
161 fd, temp_file = tempfile.mkstemp(dir=os.path.dirname(path))
162 with os.fdopen(fd, 'w') as f:
163 f.write(data)
164 os.rename(temp_file, path)
165
166
167 def dump_json(obj):
168 """Pretty-formats object to JSON."""
169 return json.dumps(obj, indent=2, sort_keys=True, separators=(',',':'))
170
171
172 def main():
173 data = json.load(sys.stdin)
174 package = data['package']
175 version = data['version']
176
177 bin_path = data['bin_path']
178 print ("should install cipd at %s" % bin_path)
179
180 # return if this client version is already installed.
181 try:
182 if not os.path.isdir(bin_path):
Vadim Sh. 2015/07/01 00:10:57 exe_path = os.path.join(bin_path, 'cipd') try: i
seanmccullough 2015/07/01 00:50:19 Done.
183 # To get a current version ID:
184 # Look for "cipd - upload packages" step in a recent build, e.g.
185 # http://build.chromium.org/p/chromium.infra/builders/infra-continuous-tru sty-64/
186 # and the last step contains the package name and hash you see here:
187 install_cipd_client(bin_path, package, version)
188
189 # move the binary into the current directory.
190 os.rename('%s/cipd' % bin_path, "cipd")
191 except Exception as e:
192 print ("Exception installing cipd: %s" % e)
193 traceback.print_ts(sys.last_traceback)
194 return 1
195
196 return 0
197
198 if __name__ == '__main__':
199 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698