blob: 4eff0c873214d5d4d4463f4927407e7f00916891 [file] [log] [blame] [edit]
#!/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 enum
import os.path
import shutil
import subprocess
import sys
import requests
from ..constants import MNT_PATH, SITE_PATH
from ..helpers import git, log
LOG = log.get_logger("reindex")
INDEX_CONFIG_PATH = f"{SITE_PATH}/index/gerrit_index.config"
INDEXES_PRIMARY = set(["accounts", "changes", "groups", "projects"])
INDEXES_REPLICA = set(["groups"])
class IndexType(enum.Enum):
LUCENE = enum.auto()
ELASTICSEARCH = enum.auto()
class GerritAbstractReindexer(abc.ABC):
def __init__(self, gerrit_config, initializer_config):
self.init_config = initializer_config
self.gerrit_config = gerrit_config
self.is_replica = self.gerrit_config.get_boolean("container.replica", False)
self.configured_indices = self._parse_gerrit_index_config()
@abc.abstractmethod
def _get_indices(self):
pass
@abc.abstractmethod
def _prepare(self):
pass
def _parse_gerrit_index_config(self):
indices = {}
if os.path.exists(INDEX_CONFIG_PATH):
config = git.GitConfigParser(INDEX_CONFIG_PATH)
options = config.list()
for opt in options:
name, version = opt["subsection"].rsplit("_", 1)
ready = opt["value"].lower() == "true"
if name in indices:
indices[name] = {
"read": version if ready else indices[name]["read"],
"latest_write": max(version, indices[name]["latest_write"]),
}
else:
indices[name] = {
"read": version if ready else None,
"latest_write": version,
}
return indices
def _get_not_ready_indices(self):
not_ready_indices = []
for index, index_attrs in self.configured_indices.items():
if not index_attrs["read"]:
LOG.info("Index %s not ready.", index)
not_ready_indices.append(index)
index_set = INDEXES_REPLICA if self.is_replica else INDEXES_PRIMARY
not_ready_indices.extend(index_set.difference(self.configured_indices.keys()))
return not_ready_indices
def _get_missing_indices(self):
indices = self._get_indices()
missing_indices = []
index_set = INDEXES_REPLICA if self.is_replica else INDEXES_PRIMARY
if not indices:
return index_set
for index in index_set:
if index not in indices:
missing_indices.append(index)
return missing_indices
def reindex(self, indices=None):
LOG.info("Starting to reindex.")
command = f"java -jar /var/war/gerrit.war reindex -d {SITE_PATH}"
if indices:
command += " ".join([f" --index {i}" for i in indices])
reindex_process = subprocess.run(
command.split(), stdout=subprocess.PIPE, check=True
)
if reindex_process.returncode > 0:
LOG.error(
"An error occurred, when reindexing Gerrit indices. Exit code: %d",
reindex_process.returncode,
)
sys.exit(1)
LOG.info("Finished reindexing.")
def start(self, is_forced):
self._prepare()
self.configured_indices = self._parse_gerrit_index_config()
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
missing_indices = self._get_missing_indices()
if missing_indices:
LOG.info("Missing at least one index. Reindexing all.")
self.reindex(missing_indices)
return
not_ready_indices = self._get_not_ready_indices()
if not_ready_indices:
self.reindex(not_ready_indices)
return
LOG.info("Skipping reindexing.")
class GerritLuceneReindexer(GerritAbstractReindexer):
def _prepare(self):
pass
def _get_indices(self):
file_list = os.listdir(os.path.join(SITE_PATH, "index"))
file_list.remove("gerrit_index.config")
lucene_indices = {}
for index in file_list:
try:
(name, version) = index.split("_")
if name in lucene_indices:
lucene_indices[name] = max(version, lucene_indices[name])
else:
lucene_indices[name] = 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 = {}
gerrit_config = git.GitConfigParser(
os.path.join(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 = f"{es_config['server']}/{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 = {}
for index, _ in response.json().items():
try:
index = index.replace(es_config["prefix"], "", 1)
(name, version) = index.split("_")
es_indices[name] = version
except ValueError:
LOG.debug("Found unknown index: %s", index)
return es_indices
def _prepare(self):
index_mnt_path = f"{MNT_PATH}/index"
index_site_path = f"{SITE_PATH}/index"
if not os.path.exists(index_mnt_path):
LOG.warning("No mounted index directory found in %s", index_mnt_path)
return
if os.path.exists(index_site_path):
if os.path.islink(index_site_path):
os.unlink(index_site_path)
elif os.path.isdir(index_site_path):
shutil.rmtree(index_site_path)
LOG.info(os.path.exists(index_site_path))
os.symlink(index_mnt_path, index_site_path, target_is_directory=True)
def get_reindexer(gerrit_config, initializer_config):
if get_index_type(gerrit_config) == IndexType.ELASTICSEARCH:
return GerritElasticSearchReindexer(gerrit_config, initializer_config)
return GerritLuceneReindexer(gerrit_config, initializer_config)
def get_index_type(gerrit_config):
indexModule = gerrit_config.get("gerrit.installIndexModule")
if indexModule and indexModule.startswith("com.google.gerrit.elasticsearch"):
return IndexType.ELASTICSEARCH
return IndexType.LUCENE