Convert update_blueprint to use the Gerrit REST API

This calls the Gerrit REST API to retrieve subject and topic for a
change instead of querying the Gerrit database. The ReviewDb was
removed in Gerrit 3.0 [1], so we need to use the REST API instead.

This also uses the Gerrit API to get the change commit message instead
of running git commands directly in the git directory.

[1] https://www.gerritcodereview.com/releases-readme.html#30-eol

Change-Id: I25b67745d3943786767d6c8960ff19cdc51b5769
diff --git a/jeepyb/cmd/update_blueprint.py b/jeepyb/cmd/update_blueprint.py
index 07bcce9..2583ee8 100644
--- a/jeepyb/cmd/update_blueprint.py
+++ b/jeepyb/cmd/update_blueprint.py
@@ -18,55 +18,28 @@
 # corresponding Launchpad blueprints with links back to the change.
 
 import argparse
-import six
-from six.moves import configparser
+import json
+import logging
 import os
 import re
-import subprocess
 
 from launchpadlib import launchpad
 from launchpadlib import uris
-import pymysql
+import requests
 
 from jeepyb import projects as p
 
 
-GERRIT_GIT_DIR = os.environ.get(
-    'GERRIT_GIT_DIR', '/home/gerrit2/review_site/git')
 GERRIT_CACHE_DIR = os.path.expanduser(
     os.environ.get('GERRIT_CACHE_DIR',
                    '~/.launchpadlib/cache'))
 GERRIT_CREDENTIALS = os.path.expanduser(
     os.environ.get('GERRIT_CREDENTIALS',
                    '~/.launchpadlib/creds'))
-GERRIT_CONFIG = os.environ.get('GERRIT_CONFIG',
-                               '/home/gerrit2/review_site/etc/gerrit.config')
-GERRIT_SECURE_CONFIG_DEFAULT = '/home/gerrit2/review_site/etc/secure.config'
-GERRIT_SECURE_CONFIG = os.environ.get('GERRIT_SECURE_CONFIG',
-                                      GERRIT_SECURE_CONFIG_DEFAULT)
+GERRIT_API_URL = 'https://review.opendev.org'
+GERRIT_API_CHANGES_URL = GERRIT_API_URL + '/changes/'
 SPEC_RE = re.compile(r'\b(blueprint|bp)\b[ \t]*[#:]?[ \t]*(\S+)', re.I)
-BODY_RE = re.compile(r'^\s+.*$')
-
-
-def get_broken_config(filename):
-    """gerrit config ini files are broken and have leading tabs."""
-    text = ""
-    with open(filename, "r") as conf:
-        for line in conf.readlines():
-            text = "%s%s" % (text, line.lstrip())
-
-    fp = six.StringIO(text)
-    c = configparser.ConfigParser(strict=False)
-    c.readfp(fp)
-    return c
-
-
-GERRIT_CONFIG = get_broken_config(GERRIT_CONFIG)
-SECURE_CONFIG = get_broken_config(GERRIT_SECURE_CONFIG)
-DB_HOST = GERRIT_CONFIG.get("database", "hostname")
-DB_USER = GERRIT_CONFIG.get("database", "username")
-DB_PASS = SECURE_CONFIG.get("database", "password")
-DB_DB = GERRIT_CONFIG.get("database", "database")
+LOG = logging.getLogger('update_blueprint')
 
 
 def update_spec(launchpad, project, name, subject, link, topic=None):
@@ -108,28 +81,55 @@
         spec.lp_save()
 
 
-def find_specs(launchpad, dbconn, args):
-    git_dir_arg = '--git-dir={base_dir}/{project}.git'.format(
-        base_dir=GERRIT_GIT_DIR,
-        project=args.project)
-    git_log = subprocess.Popen(
-        [
-            'git', git_dir_arg, 'log', '--no-merges',
-            args.commit + '^1..' + args.commit
-        ],
-        stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
-
+def find_specs(launchpad, args):
+    # Newer gerrit provides the change argument in this format:
+    # gtest-org%2Fgtest~master~I117f34aaa4253e0b82b98de9077f7188d55c3f33
+    # So this should be unique to a branch and return a single change.
     change = args.change
-    if '~' in change:
-        # Newer gerrit provides the change argument in this format:
-        # gtest-org%2Fgtest~master~I117f34aaa4253e0b82b98de9077f7188d55c3f33
-        # So we need to split off the changeid if there is other data in there.
-        change = change.rsplit('~', 1)[1]
-    cur = dbconn.cursor()
-    cur.execute("select subject, topic from changes where change_key=%s",
-                change)
-    subject, topic = cur.fetchone()
-    specs = set([m.group(2) for m in SPEC_RE.finditer(git_log)])
+    commit = args.commit
+
+    # Get the change to get the subject and topic.
+    # Get Change: 'GET /changes/{change-id}'
+    url = GERRIT_API_CHANGES_URL + change
+    try:
+        resp = requests.get(url, headers={'Accept': 'application/json'})
+    except Exception:
+        LOG.exception('Error calling: %s', url)
+        return
+
+    if resp.status_code == 200:
+        ret = json.loads(resp.text[4:])
+        if not ret:
+            return
+    else:
+        LOG.error('Request to %s returned: [%d] %s',
+                  resp.url, resp.status_code, resp.reason)
+        return
+
+    subject = ret['subject']
+    # topic is optional and update_spec() handles topic=None
+    topic = ret.get('topic')
+
+    # Get the commit to get the commit message.
+    # Get Commit: 'GET /changes/{change-id}/revisions/{revision-id}/commit'
+    url = GERRIT_API_CHANGES_URL + change + '/revisions/' + commit + '/commit'
+    try:
+        resp = requests.get(url, headers={'Accept': 'application/json'})
+    except Exception:
+        LOG.exception('Error calling: %s', url)
+        return
+
+    if resp.status_code == 200:
+        ret = json.loads(resp.text[4:])
+        if not ret:
+            return
+    else:
+        LOG.error('Request to %s returned: [%d] %s',
+                  resp.url, resp.status_code, resp.reason)
+        return
+
+    commit_message = ret['message']
+    specs = set([m.group(2) for m in SPEC_RE.finditer(commit_message)])
 
     if topic:
         topicspec = topic.split('/')[-1]
@@ -169,10 +169,7 @@
         'Gerrit User Sync', uris.LPNET_SERVICE_ROOT, GERRIT_CACHE_DIR,
         credentials_file=GERRIT_CREDENTIALS, version='devel')
 
-    conn = pymysql.connect(
-        host=DB_HOST, user=DB_USER, password=DB_PASS, db=DB_DB)
-
-    find_specs(lpconn, conn, args)
+    find_specs(lpconn, args)
 
 
 if __name__ == "__main__":