Index: appengine/monorail/services/client_config_svc.py |
diff --git a/appengine/monorail/services/client_config_svc.py b/appengine/monorail/services/client_config_svc.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4a92cdcd0d398335274e29b873cfc6e063c4fbbe |
--- /dev/null |
+++ b/appengine/monorail/services/client_config_svc.py |
@@ -0,0 +1,134 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is govered by a BSD-style |
+# license that can be found in the LICENSE file or at |
+# https://developers.google.com/open-source/licenses/bsd |
+ |
+import base64 |
+import json |
+import logging |
+import os |
+import time |
+import urllib |
+ |
+from google import protobuf |
+from google.appengine.api import app_identity |
+from google.appengine.api import urlfetch |
+ |
+import settings |
+from framework import framework_constants |
+from proto import api_clients_config_pb2 |
+ |
+ |
+CONFIG_FILE_PATH = os.path.join( |
+ os.path.dirname(os.path.dirname(os.path.realpath(__file__))), |
+ 'testing', 'api_clients.cfg') |
+MONORAIL_CONFIG_SET = urllib.quote( |
+ 'services/%s' % app_identity.get_application_id(), safe='') |
+LUCI_CONFIG_URL = ( |
+ 'https://luci-config.appspot.com/_ah/api/config/v1/config_sets' |
+ '/%s/config/api_clients.cfg') % MONORAIL_CONFIG_SET |
+ |
+ |
+client_config_svc = None |
+service_account_map = None |
+ |
+ |
+class ClientConfigService(object): |
+ """The persistence layer for client config data.""" |
+ |
+ # One hour |
+ EXPIRES_IN = 3600 |
+ |
+ def __init__(self): |
+ self.client_configs = None |
+ self.load_time = 0 |
+ |
+ def GetConfigs(self, use_cache=True, cur_time=None): |
+ """Read client configs.""" |
+ |
+ cur_time = cur_time or int(time.time()) |
+ force_load = False |
+ if not self.client_configs: |
+ force_load = True |
+ elif not use_cache: |
+ force_load = True |
+ elif cur_time - self.load_time > self.EXPIRES_IN: |
+ force_load = True |
+ |
+ if force_load: |
+ if settings.dev_mode or settings.unit_test_mode: |
+ self._ReadFromLocal() |
+ else: |
+ self._ReadFromLuciConfig() |
+ |
+ return self.client_configs |
+ |
+ def _ReadFromLocal(self): |
+ try: |
+ with open(CONFIG_FILE_PATH, 'r') as f: |
+ content_text = f.read() |
+ logging.info('Read client configs from local file.') |
+ cfg = api_clients_config_pb2.ClientCfg() |
+ protobuf.text_format.Merge(content_text, cfg) |
+ self.client_configs = cfg |
+ self.load_time = int(time.time()) |
+ except Exception as ex: |
+ logging.exception( |
+ 'Failed to read client configs: %s', |
+ str(ex)) |
+ |
+ def _ReadFromLuciConfig(self): |
+ try: |
+ authorization_token, _ = app_identity.get_access_token( |
+ framework_constants.OAUTH_SCOPE) |
+ response = urlfetch.fetch( |
+ LUCI_CONFIG_URL, |
+ method=urlfetch.GET, |
+ follow_redirects=False, |
+ headers={'Content-Type': 'application/json; charset=UTF-8', |
+ 'Authorization': 'Bearer ' + authorization_token}) |
+ if response.status_code == 200: |
+ content = json.loads(response.content) |
+ config_content = content['content'] |
+ content_text = base64.b64decode(config_content) |
+ logging.info('luci-config content decoded: %r.', content_text) |
+ cfg = api_clients_config_pb2.ClientCfg() |
+ protobuf.text_format.Merge(content_text, cfg) |
+ self.client_configs = cfg |
+ self.load_time = int(time.time()) |
+ else: |
+ logging.error('Invalid response from luci-config: %r', response) |
+ except Exception as ex: |
+ logging.exception( |
+ 'Failed to retrieve client configs from luci-config: %s', |
+ str(ex)) |
+ |
+ def GetClientIDEmails(self): |
+ """Get client IDs and Emails.""" |
+ self.GetConfigs(use_cache=True) |
+ client_ids = [c.client_id for c in self.client_configs.clients] |
+ client_emails = [c.client_email for c in self.client_configs.clients] |
+ return client_ids, client_emails |
+ |
+ def GetDisplayNames(self): |
+ """Get client display names.""" |
+ self.GetConfigs(use_cache=True) |
+ names_dict = {} |
+ for client in self.client_configs.clients: |
+ if client.display_name: |
+ names_dict[client.client_email] = client.display_name |
+ return names_dict |
+ |
+ |
+def GetClientConfigSvc(): |
+ global client_config_svc |
+ if client_config_svc is None: |
+ client_config_svc = ClientConfigService() |
+ return client_config_svc |
+ |
+def GetServiceAccountMap(): |
+ global service_account_map |
+ if service_account_map is None: |
+ service_account_map = GetClientConfigSvc().GetDisplayNames() |
+ return service_account_map |
+ |