A user running Zuul in the Google cloud may want to use service accounts to authenticate to Gerrit. Notably, the Gerrit project itself operates in this manner. Therefore, add support for using the default service account to access Gerrit. It is also very likely that a user may not want to expose a service account with Gerrit credentials to the executors. Therefore, we can not assume that they will have access to the same service account. Therefore if the gloud_service auth type is specified for a Gerrit connection, we will only use anonymous HTTP access for the git repositories. This restriction is not ideal, and may be able to be removed later if we find an out-of-band method of communicating a service account token to the executor in a manner that would not make it available to jobs running on it. Change-Id: I02bc3219018278d517bc3ae6f1ac0be22ef7c0ed
67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
# Copyright 2012 Google Inc.
|
|
# Copyright 2019 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import logging
|
|
import requests
|
|
import threading
|
|
import time
|
|
|
|
TOKEN_URL = ('http://metadata.google.internal/computeMetadata/'
|
|
'v1/instance/service-accounts/default/token')
|
|
REFRESH = 25
|
|
RETRY_INTERVAL = 5
|
|
|
|
|
|
class GCloudAuth(requests.auth.AuthBase):
|
|
log = logging.getLogger('zuul.GerritConnection')
|
|
|
|
def __init__(self, user, password):
|
|
self.token = None
|
|
self.expires = 0
|
|
try:
|
|
self.getToken()
|
|
except Exception:
|
|
self.log.exception("Error updating token:")
|
|
self.update_thread = threading.Thread(target=self.update)
|
|
self.update_thread.daemon = True
|
|
self.update_thread.start()
|
|
|
|
def update(self):
|
|
while True:
|
|
try:
|
|
self._update()
|
|
except Exception:
|
|
self.log.exception("Error updating token:")
|
|
time.sleep(5)
|
|
|
|
def _update(self):
|
|
now = time.time()
|
|
expires = self.expires - REFRESH
|
|
expires = max(expires, now + RETRY_INTERVAL)
|
|
while now < expires:
|
|
time.sleep(expires - now)
|
|
now = time.time()
|
|
self.getToken()
|
|
|
|
def getToken(self):
|
|
r = requests.get(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})
|
|
data = r.json()
|
|
self.token = data['access_token']
|
|
self.expires = time.time() + data['expires_in']
|
|
|
|
def __call__(self, request):
|
|
request.prepare_cookies({'o': self.token})
|
|
return request
|