Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/services/client_config_svc.py b/services/client_config_svc.py
index d5d6a25..d1eb123 100644
--- a/services/client_config_svc.py
+++ b/services/client_config_svc.py
@@ -1,19 +1,17 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 
 from __future__ import print_function
 from __future__ import division
 from __future__ import absolute_import
 
 import base64
+import binascii
 import json
 import logging
 import os
 import time
-from six.moves import urllib
-import webapp2
 import flask
 
 from google.appengine.api import app_identity
@@ -25,15 +23,14 @@
 
 import settings
 from framework import framework_constants
-from proto import api_clients_config_pb2
+from mrproto 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')
 LUCI_CONFIG_URL = (
-    'https://luci-config.appspot.com/_ah/api/config/v1/config_sets'
-    '/services/monorail-prod/config/api_clients.cfg')
+    'https://config.luci.app/prpc/config.service.v2.Configs/GetConfig')
 
 
 client_config_svc = None
@@ -51,26 +48,34 @@
     [ts_mon.BooleanField('success'),
      ts_mon.StringField('type')])
 
-
 def _process_response(response):
   try:
-    content = json.loads(response.content)
+    utf8_decoded_content = response.content.decode('utf-8')
+  except AttributeError:
+    logging.error('Response content was not binary: %r', response.content)
+    _CONFIG_LOADS.increment({'success': False, 'type': 'json-load-error'})
+    raise
+
+  try:
+    # Strip the XSSI prefix.
+    stripped_content = utf8_decoded_content[len(")]}'"):].strip()
+    json_config = json.loads(stripped_content)
   except ValueError:
     logging.error('Response was not JSON: %r', response.content)
     _CONFIG_LOADS.increment({'success': False, 'type': 'json-load-error'})
     raise
 
   try:
-    config_content = content['content']
+    config_raw_content = json_config['rawContent']
   except KeyError:
-    logging.error('JSON contained no content: %r', content)
+    logging.error('JSON missing rawContent: %r', json_config)
     _CONFIG_LOADS.increment({'success': False, 'type': 'json-key-error'})
     raise
 
   try:
-    content_text = base64.b64decode(config_content)
-  except TypeError:
-    logging.error('Content was not b64: %r', config_content)
+    content_text = base64.b64decode(config_raw_content)
+  except binascii.Error:
+    logging.error('Content was not b64: %r', config_raw_content)
     _CONFIG_LOADS.increment({'success': False, 'type': 'b64-decode-error'})
     raise
 
@@ -85,32 +90,44 @@
   return content_text
 
 
-def GetLoadApiClientConfigs():
-  global service_account_map
-  global qpm_dict
+def _CallLuciConfig() -> urlfetch._URLFetchResult:
   authorization_token, _ = app_identity.get_access_token(
       framework_constants.OAUTH_SCOPE)
   response = urlfetch.fetch(
       LUCI_CONFIG_URL,
-      method=urlfetch.GET,
+      method=urlfetch.POST,
       follow_redirects=False,
       headers={
-          'Content-Type': 'application/json; charset=UTF-8',
-          'Authorization': 'Bearer ' + authorization_token
-      })
-
+          'Content-Type': 'application/json; charset=utf-8',
+          'Authorization': 'Bearer ' + authorization_token,
+          'Accept': 'application/json'
+      },
+      payload=json.dumps(
+          {
+              'configSet': 'services/monorail-prod',
+              'path': 'api_clients.cfg'
+          }),
+  )
   if response.status_code != 200:
     logging.error('Invalid response from luci-config: %r', response)
     _CONFIG_LOADS.increment({'success': False, 'type': 'luci-cfg-error'})
     flask.abort(500, 'Invalid response from luci-config')
+  return response
+
+
+def GetLoadApiClientConfigs():
+  global service_account_map
+  global qpm_dict
+  response = _CallLuciConfig()
 
   try:
-    content_text = _process_response(response)
+    config_content_text = _process_response(response)
   except Exception as e:
     flask.abort(500, str(e))
 
-  logging.info('luci-config content decoded: %r.', content_text)
-  configs = ClientConfig(configs=content_text, key_name='api_client_configs')
+  logging.info('luci-config content decoded: %r.', config_content_text)
+  configs = ClientConfig(
+      configs=config_content_text, key_name='api_client_configs')
   configs.put()
   service_account_map = None
   qpm_dict = None