blob: 184a4f617cf9df8ef55658fda0e18918379e71ec [file] [log] [blame]
#!/usr/bin/python3
# Copyright (C) 2018 The Android Open Source Project
#
# 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 argparse
import os.path
import sys
import time
from abc import ABC, abstractmethod
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
from git_config_parser import GitConfigParser
class AbstractGerritDB(ABC):
def __init__(self, config, secure_config):
self._read_config(config, secure_config)
@abstractmethod
def _read_config(self, config, secure_config):
"""
Read all required configuration values.
"""
@abstractmethod
def _create_db_url(self):
"""
Create a URL with which the database can be reached.
"""
@abstractmethod
def wait_for_db_server(self):
"""
Wait until a connection with the database server is achieved.
"""
@abstractmethod
def wait_for_db(self):
"""
Check whether a database with the name configured for the ReviewDB
exists on the database server and wait for its creation.
"""
@abstractmethod
def wait_for_schema(self):
"""
Check whether the schema of the ReviewDBwas created and wait for its
creation.
"""
class H2GerritDB(AbstractGerritDB):
def __init__(self, config, secure_config, site):
super().__init__(config, secure_config)
self.site = site
self.url = self._create_db_url()
def _read_config(self, config, secure_config):
self.name = config.get("database.database", default="ReviewDB")
def _create_db_url(self):
suffix = '.h2.db'
if os.path.isabs(self.name):
if self.name.endswith(suffix):
return self.name
else:
return self.name + suffix
else:
return os.path.join(self.site, "db", self.name) + suffix
def wait_for_db_server(self):
# Not required. H2 is an embedded database.
pass
def wait_for_db(self):
print("%s: Waiting for database to be available..." % time.ctime())
while not os.path.exists(self.url):
time.sleep(3)
print("%s: Still waiting..." % time.ctime(), flush=True)
print("%s: Found it!" % time.ctime())
def wait_for_schema(self):
# Since no replication of a H2 databas is implemented yet, this test is not
# needed, because the schema is created by Gerrit.
pass
class MysqlGerritDB(AbstractGerritDB):
def __init__(self, config, secure_config):
super().__init__(config, secure_config)
# tables expected in Gerrit 2.12 - 2.16
self.tables = ['changes', 'patch_sets']
self.server_url, self.reviewdb_url = self._create_db_url()
self.engine = None
self.connection = None
def _read_config(self, config, secure_config):
self.host = config.get("database.hostname", default="localhost")
self.port = config.get("database.port", default="3306")
self.name = config.get("database.database", default="reviewdb")
self.user = secure_config.get("database.username", default="")
self.pwd = secure_config.get("database.password", default="")
def _create_db_url(self):
server_url = "mysql+pymysql://%s:%s@%s:%s" % (
self.user,
self.pwd,
self.host,
self.port)
reviewdb_url = "%s/%s" % (server_url, self.name)
return (server_url, reviewdb_url)
def _connect_to_db(self, url):
self.engine = create_engine(url)
self.connection = self.engine.connect()
def wait_for_db_server(self):
print("%s: Waiting for database server connection..." % time.ctime())
while not self.connection or self.connection.closed:
try:
self._connect_to_db(self.server_url)
continue
except SQLAlchemyError:
print("%s: Still waiting..." % time.ctime(), flush=True)
time.sleep(3)
self.connection.close()
print("%s: Connection successful!" % time.ctime())
def wait_for_db(self):
print("%s: Waiting for database to be available..." % time.ctime())
self.connection.close()
while not self.connection or self.connection.closed:
try:
self._connect_to_db(self.reviewdb_url)
continue
except SQLAlchemyError:
print("%s: Still waiting..." % time.ctime(), flush=True)
time.sleep(3)
self.connection.close()
print("%s: Found it!" % time.ctime())
def wait_for_schema(self):
print("%s: Waiting for database schema to be created..." % time.ctime())
for table in self.tables:
while not self.engine.dialect.has_table(self.engine, table):
print("%s: Still waiting for table %s..." % (time.ctime(), table),
flush=True)
time.sleep(3)
print("%s: Schema appears to have been created!" % time.ctime())
def select_db(gerrit_site_path):
gerrit_config_path = os.path.join(gerrit_site_path, "etc/gerrit.config")
config = GitConfigParser(gerrit_config_path)
gerrit_secure_config_path = os.path.join(gerrit_site_path, "etc/secure.config")
secure_config = GitConfigParser(gerrit_secure_config_path)
db_type = config.get("database.type")
if db_type.upper() == "H2":
gerrit_db = H2GerritDB(config, secure_config, gerrit_site_path)
elif db_type.upper() == "MYSQL":
gerrit_db = MysqlGerritDB(config, secure_config)
else:
print("Unknown database type.")
sys.exit(1)
return gerrit_db
def validate_db(gerrit_site_path):
gerrit_db = select_db(gerrit_site_path)
gerrit_db.wait_for_db_server()
gerrit_db.wait_for_db()
gerrit_db.wait_for_schema()
# pylint: disable=C0103
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-s",
"--site",
help="Path to Gerrit site",
dest="site",
action="store",
default="/var/gerrit",
required=True)
args = parser.parse_args()
validate_db(args.site)