blob: 99ac70d0cdeddd9c7706aa60a3e0e906ba96f067 [file] [log] [blame]
#!/usr/bin/python3
# Copyright (C) 2019 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 abc
import argparse
import enum
import os.path
import subprocess
import sys
import requests
from git_config_parser import GitConfigParser
from init_config import InitConfig
from log import get_logger
LOG = get_logger("reindex")
class IndexType(enum.Enum):
LUCENE = enum.auto()
ELASTICSEARCH = enum.auto()
class GerritAbstractReindexer(abc.ABC):
def __init__(self, gerrit_site_path, config):
self.gerrit_site_path = gerrit_site_path
self.index_config_path = "%s/index/gerrit_index.config" % self.gerrit_site_path
self.init_config = config
self.configured_indices = self._parse_gerrit_index_config()
@abc.abstractmethod
def _get_indices(self):
pass
def _parse_gerrit_index_config(self):
indices = dict()
if os.path.exists(self.index_config_path):
config = GitConfigParser(self.index_config_path)
options = config.list()
for opt in options:
name, version = opt["subsection"].rsplit("_", 1)
# TODO (Thomas): Properly handle multiple versions of the same index,
# which may be present due to online-reindexing in progress.
indices[name] = {
"version": int(version),
"ready": opt["value"].lower() == "true",
}
return indices
def _get_unready_indices(self):
unready_indices = []
for index, index_attrs in self.configured_indices.items():
if not index_attrs["ready"]:
LOG.info("Index %s not ready.", index)
unready_indices.append(index)
return unready_indices
def _check_index_versions(self):
indices = self._get_indices()
if not indices:
return False
for index, index_attrs in self.configured_indices.items():
if index_attrs["version"] is not indices[index]:
return False
return True
def reindex(self, indices=None):
LOG.info("Starting to reindex.")
command = "java -jar /var/war/gerrit.war reindex -d %s" % self.gerrit_site_path
if indices:
command += " ".join([" --index %s" % i for i in indices])
reindex_process = subprocess.run(command.split(), stdout=subprocess.PIPE)
if reindex_process.returncode > 0:
LOG.error(
"An error occured, when reindexing Gerrit indices. Exit code: %d",
reindex_process.returncode,
)
sys.exit(1)
LOG.info("Finished reindexing.")
def start(self, is_forced):
if is_forced:
self.reindex()
return
if not self.configured_indices:
LOG.info("gerrit_index.config does not exist. Creating all indices.")
self.reindex()
return
unready_indices = self._get_unready_indices()
if unready_indices:
self.reindex(unready_indices)
if not self._check_index_versions():
LOG.info("Not all indices are up-to-date.")
self.reindex()
return
LOG.info("Skipping reindexing.")
class GerritLuceneReindexer(GerritAbstractReindexer):
def _get_indices(self):
file_list = os.listdir(os.path.join(self.gerrit_site_path, "index"))
file_list.remove("gerrit_index.config")
lucene_indices = dict()
for index in file_list:
try:
(name, version) = index.split("_")
lucene_indices[name] = int(version)
except ValueError:
LOG.debug("Ignoring invalid file in index-directory: %s", index)
return lucene_indices
class GerritElasticSearchReindexer(GerritAbstractReindexer):
def _get_elasticsearch_config(self):
es_config = dict()
gerrit_config = GitConfigParser(
os.path.join(self.gerrit_site_path, "etc", "gerrit.config")
)
es_config["prefix"] = gerrit_config.get(
"elasticsearch.prefix", default=""
).lower()
es_config["server"] = gerrit_config.get(
"elasticsearch.server", default=""
).lower()
return es_config
def _get_indices(self):
es_config = self._get_elasticsearch_config()
url = "{url}/{prefix}*".format(
url=es_config["server"], prefix=es_config["prefix"]
)
try:
response = requests.get(url)
except requests.exceptions.SSLError:
response = requests.get(url, verify=self.init_config.ca_cert_path)
es_indices = dict()
for index, _ in response.json().items():
try:
index = index.replace(es_config["prefix"], "", 1)
(name, version) = index.split("_")
es_indices[name] = int(version)
except ValueError:
LOG.debug("Found unknown index: %s", index)
return es_indices
def get_reindexer(gerrit_site_path, config):
gerrit_config = GitConfigParser(
os.path.join(gerrit_site_path, "etc", "gerrit.config")
)
index_type = gerrit_config.get("index.type", default=IndexType.LUCENE.name)
if IndexType[index_type.upper()] is IndexType.LUCENE:
return GerritLuceneReindexer(gerrit_site_path, config)
if IndexType[index_type.upper()] is IndexType.ELASTICSEARCH:
return GerritElasticSearchReindexer(gerrit_site_path, config)
raise RuntimeError("Unknown index type %s." % index_type)
# 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,
)
parser.add_argument(
"-f",
"--force",
help="Reindex even if indices are ready.",
dest="force",
action="store_true",
)
parser.add_argument(
"-c",
"--config",
help="Path to configuration file for init process.",
dest="config",
action="store",
required=True,
)
args = parser.parse_args()
config = InitConfig().parse(args.config)
reindexer = get_reindexer(args.site, config)
reindexer.start(args.force)