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

Side by Side Diff: client/third_party/infra_libs/httplib2_utils.py

Issue 2705273003: Roll infra_libs and gae_ts_mon in luci-py, and add field_specs to all metrics (Closed)
Patch Set: Rebase Created 3 years, 9 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
1 # Copyright 2015 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import base64
5 import collections 6 import collections
6 import copy 7 import copy
7 import json 8 import json
8 import logging 9 import logging
9 import os 10 import os
10 import re 11 import re
11 import socket 12 import socket
12 import sys 13 import sys
13 import time 14 import time
14 15
15 import httplib2 16 import httplib2
16 import oauth2client.client 17 import oauth2client.client
17 18
18 from googleapiclient import errors 19 from googleapiclient import errors
19 from infra_libs.ts_mon.common import http_metrics 20 from infra_libs.ts_mon.common import http_metrics
21 from oauth2client import util
20 22
21 DEFAULT_SCOPES = ['email'] 23 DEFAULT_SCOPES = ['email']
22 24
23 # default timeout for http requests, in seconds 25 # default timeout for http requests, in seconds
24 DEFAULT_TIMEOUT = 30 26 DEFAULT_TIMEOUT = 30
25 27
26 # This is part of the API. 28 # This is part of the API.
27 if sys.platform.startswith('win'): # pragma: no cover 29 if sys.platform.startswith('win'): # pragma: no cover
28 SERVICE_ACCOUNTS_CREDS_ROOT = 'C:\\creds\\service_accounts' 30 SERVICE_ACCOUNTS_CREDS_ROOT = 'C:\\creds\\service_accounts'
29 else: 31 else:
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 credentials_filename, 157 credentials_filename,
156 scope=scope, 158 scope=scope,
157 service_accounts_creds_root=service_accounts_creds_root) 159 service_accounts_creds_root=service_accounts_creds_root)
158 160
159 if http_identifier: 161 if http_identifier:
160 http = InstrumentedHttp(http_identifier, timeout=timeout) 162 http = InstrumentedHttp(http_identifier, timeout=timeout)
161 else: 163 else:
162 http = httplib2.Http(timeout=timeout) 164 http = httplib2.Http(timeout=timeout)
163 return creds.authorize(http) 165 return creds.authorize(http)
164 166
167
168 class DelegateServiceAccountCredentials(
169 oauth2client.client.AssertionCredentials):
170 """Authorizes an HTTP client with a service account for which we are an actor.
171
172 This class uses the IAM API to sign a JWT with the private key of another
173 service account for which we have the "Service Account Actor" role.
174 """
175
176 MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
177 _SIGN_BLOB_URL = 'https://iam.googleapis.com/v1/%s:signBlob'
178
179 def __init__(self, http, service_account_email, scopes, project='-'):
180 """
181 Args:
182 http: An httplib2.Http object that is authorized by another
183 oauth2client.client.OAuth2Credentials with credentials that have the
184 service account actor role on the service_account_email.
185 service_account_email: The email address of the service account for which
186 to obtain an access token.
187 scopes: The desired scopes for the token.
188 project: The cloud project to which service_account_email belongs. The
189 default of '-' makes the IAM API figure it out for us.
190 """
191
192 super(DelegateServiceAccountCredentials, self).__init__(None)
193 self._service_account_email = service_account_email
194 self._scopes = util.scopes_to_string(scopes)
195 self._http = http
196 self._name = 'projects/%s/serviceAccounts/%s' % (
197 project, service_account_email)
198
199 def sign_blob(self, blob):
200 response, content = self._http.request(
201 self._SIGN_BLOB_URL % self._name,
202 method='POST',
203 body=json.dumps({'bytesToSign': base64.b64encode(blob)}),
204 headers={'Content-Type': 'application/json'})
205 if response.status != 200:
206 raise AuthError('Failed to sign blob as %s: %d %s' % (
207 self._service_account_email, response.status, response.reason))
208
209 data = json.loads(content)
210 return data['keyId'], data['signature']
211
212 def _generate_assertion(self):
213 # This is copied with small modifications from
214 # oauth2client.service_account._ServiceAccountCredentials.
215
216 header = {
217 'alg': 'RS256',
218 'typ': 'JWT',
219 }
220
221 now = int(time.time())
222 payload = {
223 'aud': self.token_uri,
224 'scope': self._scopes,
225 'iat': now,
226 'exp': now + self.MAX_TOKEN_LIFETIME_SECS,
227 'iss': self._service_account_email,
228 }
229
230 assertion_input = (
231 self._urlsafe_b64encode(header) + b'.' +
232 self._urlsafe_b64encode(payload))
233
234 # Sign the assertion.
235 _, rsa_bytes = self.sign_blob(assertion_input)
236 signature = rsa_bytes.rstrip(b'=')
237
238 return assertion_input + b'.' + signature
239
240 def _urlsafe_b64encode(self, data):
241 # Copied verbatim from oauth2client.service_account.
242 return base64.urlsafe_b64encode(
243 json.dumps(data, separators=(',', ':')).encode('UTF-8')).rstrip(b'=')
244
245
165 class RetriableHttp(object): 246 class RetriableHttp(object):
166 """A httplib2.Http object that retries on failure.""" 247 """A httplib2.Http object that retries on failure."""
167 248
168 def __init__(self, http, max_tries=5, backoff_time=1, 249 def __init__(self, http, max_tries=5, backoff_time=1,
169 retrying_statuses_fn=None): 250 retrying_statuses_fn=None):
170 """ 251 """
171 Args: 252 Args:
172 http: an httplib2.Http instance 253 http: an httplib2.Http instance
173 max_tries: a number of maximum tries 254 max_tries: a number of maximum tries
174 backoff_time: a number of seconds to sleep between retries 255 backoff_time: a number of seconds to sleep between retries
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
209 def __getattr__(self, name): 290 def __getattr__(self, name):
210 return getattr(self._http, name) 291 return getattr(self._http, name)
211 292
212 def __setattr__(self, name, value): 293 def __setattr__(self, name, value):
213 if name in ('request', '_http', '_max_tries', '_backoff_time', 294 if name in ('request', '_http', '_max_tries', '_backoff_time',
214 '_retrying_statuses_fn'): 295 '_retrying_statuses_fn'):
215 self.__dict__[name] = value 296 self.__dict__[name] = value
216 else: 297 else:
217 setattr(self._http, name, value) 298 setattr(self._http, name, value)
218 299
300
219 class InstrumentedHttp(httplib2.Http): 301 class InstrumentedHttp(httplib2.Http):
220 """A httplib2.Http object that reports ts_mon metrics about its requests.""" 302 """A httplib2.Http object that reports ts_mon metrics about its requests."""
221 303
222 def __init__(self, name, time_fn=time.time, timeout=DEFAULT_TIMEOUT, 304 def __init__(self, name, time_fn=time.time, timeout=DEFAULT_TIMEOUT,
223 **kwargs): 305 **kwargs):
224 """ 306 """
225 Args: 307 Args:
226 name: An identifier for the HTTP requests made by this object. 308 name: An identifier for the HTTP requests made by this object.
227 time_fn: Function returning the current time in seconds. Use for testing 309 time_fn: Function returning the current time in seconds. Use for testing
228 purposes only. 310 purposes only.
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 self.requests_made.append(self.HttpCall(uri, method, body, headers)) 393 self.requests_made.append(self.HttpCall(uri, method, body, headers))
312 headers = None 394 headers = None
313 body = None 395 body = None
314 for candidate in self._uris: 396 for candidate in self._uris:
315 if candidate[0].match(uri): 397 if candidate[0].match(uri):
316 _, headers, body = candidate 398 _, headers, body = candidate
317 break 399 break
318 if not headers: 400 if not headers:
319 raise AssertionError("Unexpected request to %s" % uri) 401 raise AssertionError("Unexpected request to %s" % uri)
320 return httplib2.Response(headers), body 402 return httplib2.Response(headers), body
OLDNEW
« no previous file with comments | « client/third_party/infra_libs/event_mon/protos/goma_stats_pb2.py ('k') | client/third_party/infra_libs/logs/logs.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698