Rewrite gerrit-init image to allow to handle other databases

The gerrit-init container was only able to initialize a MySQL
database. The provided bash scripts weren't easy to extend to support
more databases. With Gerrit 2.16 a H2-database is usually enough, since
the content does not change during runtime. Thus, at least this database
should be supported.

This change adapts the gerrit-init container to be able to support
H2-databases. To do this, python scripts were used instead of bash,
since it makes it easier to provide extensible code and since python
possesses libraries to communicate with several databases, making it
easier and more efficient to work with the databases.

This change makes the Gerrit initialization in the helm charts more
flexible. Also, using python and sqlalchemy to communicate with the
database is a lot faster than using bash and the mysql client, greatly
fasten up pod startup.

Change-Id: Ic06de5ba6686b4c47365cbadb919c9a6ebfbd6b5
diff --git a/.gitignore b/.gitignore
index b61b82b..386ecda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
 *.pem
 *.crt
 /helm-charts/*/charts
-/helm-charts/*/requirements.lock
\ No newline at end of file
+/helm-charts/*/requirements.lock
+
+__pycache__
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index fabb67f..4137660 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
 
+```
                                  Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
@@ -187,7 +188,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] The Android Open Source Project
+   Copyright 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.
@@ -200,3 +201,46 @@
    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.
+```
+
+## Subcomponents
+
+This project includes the following subcomponents that are subject to separate
+license terms. Your use of these subcomponents is subject to the separate
+license terms applicable to each subcomponent.
+
+PyMySQL \
+https://github.com/PyMySQL/PyMySQL \
+Copyright (c) 2010, 2013 PyMySQL contributors \
+MIT License (https://github.com/PyMySQL/PyMySQL/blob/master/LICENSE)
+
+SQLAlchemy \
+https://github.com/sqlalchemy/sqlalchemy/ \
+Copyright (c) 2005-2019 Michael Bayer and contributors.
+SQLAlchemy is a trademark of Michael Bayer. \
+MIT License (https://docs.sqlalchemy.org/en/latest/copyright.html)
+
+---
+## The MIT License (MIT)
+
+```
+Copyright <YEAR> <COPYRIGHT HOLDER>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
diff --git a/container-images/gerrit-init/.dockerignore b/container-images/gerrit-init/.dockerignore
new file mode 100644
index 0000000..7c68535
--- /dev/null
+++ b/container-images/gerrit-init/.dockerignore
@@ -0,0 +1 @@
+tools/__pycache__
diff --git a/container-images/gerrit-init/Dockerfile b/container-images/gerrit-init/Dockerfile
index fd34dc8..0975f68 100644
--- a/container-images/gerrit-init/Dockerfile
+++ b/container-images/gerrit-init/Dockerfile
@@ -3,12 +3,18 @@
 USER root
 
 RUN apt-get update && \
-    apt-get install -y mysql-client
+    apt-get install -y \
+      mysql-client \
+      python3 \
+      python3-pip && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/* && \
+    pip3 install \
+      PyMySQL \
+      sqlalchemy
 
 COPY tools/* /var/tools/
 
-ENV TEST_MODE=
-
 USER gerrit
 
-ENTRYPOINT ["/bin/bash", "-c", "/var/tools/verify_fs_permissions && /var/tools/start"]
+ENTRYPOINT ["/var/tools/gerrit_init.py", "-s", "/var/gerrit"]
diff --git a/container-images/gerrit-init/README.md b/container-images/gerrit-init/README.md
index 053c748..9357e2d 100644
--- a/container-images/gerrit-init/README.md
+++ b/container-images/gerrit-init/README.md
@@ -1,29 +1,33 @@
 # Gerrit slave init container image
 
-Kubernetes init container for initializing a gerrit slave. Currently also
-used to initialize gerrit master using a different Entrypoint, will be cleaned
-up in a future change.
+Kubernetes init container for initializing gerrit. The python script running in
+the container makes sure, that the database is initialized (currently supported:
+H2 and MySQL) and initializes Gerrit including the installation of configured
+core plugins.
 
 ## Content
 
-* gerrit-slave image
+* gerrit-base image
 
 ## Setup and configuration
 
-* install mysql-client
+* install mysql-client, python 3 and pip
+* install sqlalchemy and mysql driver for python
 * copy tool scripts
 
 ## Start
 
-* verify filesystem permissions
-* start the container via start script `/var/tools/start` (definition of
- Entrypoint is inherited from gerrit-base image)
+* start the container via start script `/var/tools/gerrit_init.py`
 
-The start script
+The `gerrit_init.py`-script
 
-* reads database configuration from gerrit.config
+* reads configuration from gerrit.config (via `gerrit_config_parser.py`)
+* waits for the database to start (via `validate_db.py`)
+* initializes Gerrit
+
+The `validate_db.py`-script
+
+* reads database configuration from gerrit.config (via `gerrit_config_parser.py`)
 * waits for the database to start
-* waits for All-Projects.git and All-Users.git to arrive via replication via
- apache-git-http-backend from Gerrit master
-* waits for MySQL slave database schema to arrive via database replication from
- Gerrit slave
\ No newline at end of file
+* waits for the reviewdb database
++ waits for some selected tables to ensure that the schema is initialized
diff --git a/container-images/gerrit-init/tools/gerrit_init.py b/container-images/gerrit-init/tools/gerrit_init.py
new file mode 100755
index 0000000..4b4b1c5
--- /dev/null
+++ b/container-images/gerrit-init/tools/gerrit_init.py
@@ -0,0 +1,82 @@
+#!/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 subprocess
+import sys
+import time
+
+from git_config_parser import GitConfigParser
+from validate_db import select_db
+
+def ensure_database_connection(gerrit_site_path):
+  db = select_db(gerrit_site_path)
+  db.wait_for_db_server()
+
+def determine_is_slave(gerrit_site_path):
+  gerrit_config_path = os.path.join(gerrit_site_path, "etc/gerrit.config")
+  config = GitConfigParser(gerrit_config_path)
+  return config.get_boolean("container.slave", False)
+
+def initialize_gerrit(gerrit_site_path, plugins):
+  if os.path.exists(os.path.join(gerrit_site_path, "etc/gerrit.config")):
+    print("%s: Existing gerrit.config found." % time.ctime())
+    ensure_database_connection(gerrit_site_path)
+  else:
+    print("%s: No gerrit.config found. Initializing default site." % time.ctime())
+
+  if plugins:
+    plugin_options = ' '.join(['--install-plugin %s' % plugin for plugin in plugins])
+  else:
+    plugin_options = ''
+
+  flags = "--no-auto-start --batch"
+
+  if determine_is_slave(gerrit_site_path):
+    flags += " --no-reindex"
+
+  command = "java -jar /var/war/gerrit.war init %s %s -d %s" % (
+    flags,
+    plugin_options,
+    gerrit_site_path)
+
+  init_process = subprocess.run(command.split(), stdout=subprocess.PIPE)
+
+  if init_process.returncode > 0:
+    print("An error occured, when initializing Gerrit. Exit code: ", exit_code)
+    sys.exit(1)
+
+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(
+    "-p",
+    "--plugin",
+    help="Gerrit plugin to be installed. Can be used multiple times.",
+    dest="plugins",
+    action="append",
+    default=None)
+  args = parser.parse_args()
+
+  initialize_gerrit(args.site, args.plugins)
diff --git a/container-images/gerrit-init/tools/git_config_parser.py b/container-images/gerrit-init/tools/git_config_parser.py
new file mode 100644
index 0000000..dc35619
--- /dev/null
+++ b/container-images/gerrit-init/tools/git_config_parser.py
@@ -0,0 +1,61 @@
+# 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 subprocess
+
+class GitConfigParser:
+
+  def __init__(self, config_path):
+    self.path = config_path
+
+  def _execute_shell_command_and_get_output_lines(self, command):
+    sub_process_run = subprocess.run(
+      command.split(),
+      stdout=subprocess.PIPE,
+      check=True,
+      universal_newlines=True)
+    return [line.strip() for line in sub_process_run.stdout.splitlines()]
+
+  def _get_value(self, key):
+    command = "git config -f %s --get %s" % (self.path, key)
+    return self._execute_shell_command_and_get_output_lines(command)
+
+  def get(self, key, default=None):
+    """
+    Returns value of given key in the configuration file. If the key appears
+    multiple times, the last value is returned.
+    """
+    try:
+      return self._get_value(key)[-1]
+    except subprocess.CalledProcessError:
+      return default
+
+  def get_boolean(self, key, default=False):
+    """
+    Returns boolean value of given key in the configuration file. If the key
+    appears multiple times, the last value is returned.
+    """
+    if not type(default) == bool:
+      raise TypeError("Default has to be a boolean.")
+
+    try:
+      value = self._get_value(key)[-1].lower()
+      if value == "true":
+        return True
+      elif value == "false":
+        return False
+      else:
+        raise TypeError("Value is not a boolean.")
+    except subprocess.CalledProcessError:
+      return default
diff --git a/container-images/gerrit-init/tools/start b/container-images/gerrit-init/tools/start
deleted file mode 100755
index b1e4e32..0000000
--- a/container-images/gerrit-init/tools/start
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/bin/bash
-
-get_db_config(){
-  if test -f "/var/gerrit/etc/secure.config"; then
-    export DB_USER=$(git config --file /var/gerrit/etc/secure.config --get database.username)
-    export DB_PASSWORD=$(git config --file /var/gerrit/etc/secure.config --get database.password)
-  fi
-
-  if test -f "/var/gerrit/etc/gerrit.config"; then
-    export DB_TYPE=$(git config --file /var/gerrit/etc/gerrit.config --get database.type)
-    export DB_NAME=$(git config --file /var/gerrit/etc/gerrit.config --get database.database | cut -d'?' -f1)
-    export DB_HOST=$(git config --file /var/gerrit/etc/gerrit.config --get database.hostname)
-    export DB_PORT=$(git config --file /var/gerrit/etc/gerrit.config --get database.port)
-  fi
-
-  if test -z "${DB_USER}"; then
-    echo "Missing database username in Gerrit config."
-    exit 1
-  fi
-
-  if test -z "${DB_PASSWORD}"; then
-    echo "Missing database password in Gerrit config."
-    exit 1
-  fi
-
-  if test -z "${DB_NAME}"; then
-    export DB_NAME="reviewdb"
-  fi
-
-  if test -z "${DB_HOST}"; then
-    echo "Missing database host in Gerrit config."
-    exit 1
-  fi
-
-  if test -z "${DB_PORT}"; then
-    echo "Missing database port in Gerrit config."
-    exit 1
-  fi
-
-  return 0
-}
-
-test_mysql_db(){
-  local EXIT_CODE=0
-  mysql -h ${DB_HOST} -P${DB_PORT} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} >/dev/null 2>&1 </dev/null || EXIT_CODE=1
-  # Check existence of some tables
-  for table in changes patch_sets; do    # tables expected in Gerrit 2.12 - 2.16
-    SQL_EXISTS=$(printf 'SHOW TABLES LIKE "%s"' "${table}")
-    if [[ $(mysql -h ${DB_HOST} -P${DB_PORT} -u ${DB_USER} -p${DB_PASSWORD} -e "${SQL_EXISTS}" ${DB_NAME}) ]]; then
-      echo "Table ${table} was found."
-    else
-      echo "Table ${table} was NOT found. Continuing to wait..."
-      local EXIT_CODE=1
-      break
-    fi
-  done
-  return ${EXIT_CODE}
-}
-
-wait_for_db_schema(){
-  echo "Waiting for database to be ready."
-
-  case "${DB_TYPE^^}" in
-    MYSQL)
-      while [ true ]; do
-        test_mysql_db && break
-        sleep 10
-      done
-      ;;
-
-    *)
-      echo "Database type ${DB_TYPE} not supported."
-      exit 1
-      ;;
-  esac
-  echo "Database was found."
-}
-
-wait_for_db_connection(){
-  echo "Waiting for database connection..."
-
-  case "${DB_TYPE^^}" in
-    MYSQL)
-      while ! mysqladmin ping -h "${DB_HOST}" -P"${DB_PORT}" --silent; do
-          sleep 1
-      done
-      ;;
-
-    *)
-      echo "Database type ${DB_TYPE} not supported."
-      exit 1
-      ;;
-  esac
-}
-
-# cleanup from last start
-rm -f /var/gerrit/logs/gerrit.pid
-
-# read db configuration from gerrit.config
-get_db_config || exit 1
-
-# wait for db to start
-wait_for_db_connection
-
-if [[ "$TEST_MODE" == "true" ]]; then
-  java -jar /var/war/gerrit.war init \
-    --batch \
-    --no-auto-start \
-    --install-plugin singleusergroup \
-    -d /var/gerrit
-fi
-
-# wait for db schema to arrive via replication from master
-wait_for_db_schema
-
-echo "Gerrit site appears to be initialized. Gerrit slave can be startet."
diff --git a/container-images/gerrit-init/tools/validate_db.py b/container-images/gerrit-init/tools/validate_db.py
new file mode 100755
index 0000000..99077f9
--- /dev/null
+++ b/container-images/gerrit-init/tools/validate_db.py
@@ -0,0 +1,209 @@
+#!/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.
+
+from abc import ABC, abstractmethod
+from sqlalchemy import create_engine
+
+import argparse
+import os.path
+import sys
+import time
+
+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.
+    """
+    pass
+
+  @abstractmethod
+  def _create_db_url(self):
+    """
+    Create a URL with which the database can be reached.
+    """
+    pass
+
+  @abstractmethod
+  def wait_for_db_server(self):
+    """
+    Wait until a connection with the database server is achieved.
+    """
+    pass
+
+  @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.
+    """
+    pass
+
+  @abstractmethod
+  def wait_for_schema(self):
+    """
+    Check whether the schema of the ReviewDBwas created and wait for its
+    creation.
+    """
+    pass
+
+
+class H2GerritDB(AbstractGerritDB):
+
+  def __init__(self, config, secure_config, site):
+    super().__init__(config, secure_config)
+    self.url = self._create_db_url(site)
+
+  def _read_config(self, config, secure_config):
+    self.name = config.get("database.database", default="ReviewDB")
+
+  def _create_db_url(self, site):
+    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(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()
+
+  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 hasattr(self, 'connection') or self.connection.closed:
+      try:
+        self._connect_to_db(self.server_url)
+        continue
+      except:
+        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 hasattr(self, 'connection') or self.connection.closed:
+      try:
+        self._connect_to_db(self.reviewdb_url)
+        continue
+      except:
+        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()
+
+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)
diff --git a/container-images/gerrit-init/tools/verify_fs_permissions b/container-images/gerrit-init/tools/verify_fs_permissions
deleted file mode 100755
index ee1baf6..0000000
--- a/container-images/gerrit-init/tools/verify_fs_permissions
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-gerrit_uid=$(id -u)
-gerrit_gid=$(cut -d: -f3 < <(getent group users))
-
-for dir in /var/gerrit/*; do
-  /var/tools/validate_site.sh $dir $gerrit_uid $gerrit_gid || exit 1
-done
\ No newline at end of file
diff --git a/helm-charts/gerrit-master/README.md b/helm-charts/gerrit-master/README.md
index 109e0f0..79b84e1 100644
--- a/helm-charts/gerrit-master/README.md
+++ b/helm-charts/gerrit-master/README.md
@@ -121,11 +121,21 @@
 
 ### Database
 
-Gerrit requires a database containing the user data. Currently this chart provides
-the possibility to install a MySQL database for this purpose. Other databases may
-be installed manually, if wanted.
+Gerrit requires a database containing the user data. Currently, this chart provides
+the possibility to install a MySQL database for this purpose or to use a H2 database.
+Other databases may be installed manually, if wanted.
 
-Since the configuration of the database is different depending on the database
+H2 does not require special configuration. It just has to be configured
+in the `gerrit.config`. Also persistence for the database-directory should be
+enabled.
+
+***note
+In general, H2-databases should not be used in production with Gerrit versions <=
+2.15. With Gerrit 2.16 the data stored in the database does not change at runtime,
+thus a H2 database can be safely used.
+***
+
+Since the configuration of the other databases is different depending on the database
 provider used, the configuration is described in separate documents (other
 databases may be added in future):
 
diff --git a/helm-charts/gerrit-master/templates/gerrit-master.deployment.yaml b/helm-charts/gerrit-master/templates/gerrit-master.deployment.yaml
index 82c78a4..ef15963 100644
--- a/helm-charts/gerrit-master/templates/gerrit-master.deployment.yaml
+++ b/helm-charts/gerrit-master/templates/gerrit-master.deployment.yaml
@@ -43,14 +43,50 @@
         imagePullPolicy: {{ .Values.images.imagePullPolicy }}
         command:
         - /bin/bash
-        - -c
+        - -ce
         args:
         - |
-          echo "Waiting for database..."
-          while ! mysqladmin ping -h "gerrit-master-mysql" -P"3306" --silent; do
-            sleep 10
-          done
-          echo "Database connection successful!"
+          symlink_config_to_site(){
+            {{ if .Values.gerritMaster.keystore -}}
+            ln -s /var/keystore /var/gerrit/etc/keystore
+            {{- end }}
+            ln -sf /var/config/gerrit.config /var/gerrit/etc/gerrit.config
+            ln -sf /var/config/secure.config /var/gerrit/etc/secure.config
+          }
+
+          mkdir -p /var/gerrit/etc
+          symlink_config_to_site
+
+          if [ ! -f /var/gerrit/bin/gerrit.sh ]; then
+            /var/tools/gerrit_init.py \
+              -s /var/gerrit \
+              -p replication \
+              -p commit-message-length-validator \
+              -p download-commands \
+              -p reviewnotes
+
+            symlink_config_to_site
+          fi
+
+          /var/tools/validate_db.py -s /var/gerrit
+        volumeMounts:
+        - name: gerrit-site
+          mountPath: "/var/gerrit"
+        - name: gerrit-db
+          mountPath: "/var/gerrit/db"
+        - name: git-filesystem
+          mountPath: "/var/gerrit/git"
+        - name: gerrit-config
+          mountPath: "/var/config/gerrit.config"
+          subPath: gerrit.config
+        - name: gerrit-master-secure-config
+          mountPath: "/var/config/secure.config"
+          subPath: secure.config
+        {{ if .Values.gerritMaster.keystore -}}
+        - name: gerrit-master-secure-config
+          mountPath: "/var/keystore"
+          subPath: keystore
+        {{- end }}
       containers:
       - name: gerrit-master
         image: {{ template "registry" . }}{{ .Values.gerritMaster.images.gerritMaster }}:{{ .Values.images.version }}
@@ -72,16 +108,6 @@
           mkdir -p /var/gerrit/etc
           symlink_config_to_site
 
-          java -jar /var/war/gerrit.war init \
-              --batch \
-              --install-plugin replication \
-              --install-plugin commit-message-length-validator \
-              --install-plugin download-commands \
-              --install-plugin reviewnotes \
-              -d /var/gerrit
-
-          symlink_config_to_site
-
           java -jar /var/gerrit/bin/gerrit.war reindex \
               -d /var/gerrit
 
diff --git a/helm-charts/gerrit-master/values.yaml b/helm-charts/gerrit-master/values.yaml
index 1b5630f..1940dd0 100644
--- a/helm-charts/gerrit-master/values.yaml
+++ b/helm-charts/gerrit-master/values.yaml
@@ -117,7 +117,8 @@
 
   config:
     # Some values are expected to have a specific value for the deployment installed
-    # by this chart to work. These are marked with `# FIXED`. Do not change them!
+    # by this chart to work. These are marked with `# FIXED`.
+    # Do not change them!
     gerrit: |-
       [gerrit]
         basePath = git # FIXED
diff --git a/helm-charts/gerrit-slave/README.md b/helm-charts/gerrit-slave/README.md
index 8bd2d5e..5c70746 100644
--- a/helm-charts/gerrit-slave/README.md
+++ b/helm-charts/gerrit-slave/README.md
@@ -165,18 +165,31 @@
 
 ***note
 Future implementations will provide the possibility to bring custom databases
-from different providers, but so far the setup expects to setup its own MySQL
-database.
+from different providers, but so far the setup expects to setup its own MySQL or
+H2 database.
 ***
 
-| Parameter                      | Description                                            | Default |
-|--------------------------------|--------------------------------------------------------|---------|
-| `database.provider`            | Database type/provider to be used (Available: mysql)   | `mysql` |
-| `database.replication.enabled` | Whether to initialize replication from master database | `true`  |
+| Parameter                      | Description                                                                   | Default |
+|--------------------------------|-------------------------------------------------------------------------------|---------|
+| `database.provider`            | Database type/provider to be used (Available: mysql, h2)                      | `mysql` |
+| `database.replication.enabled` | Whether to initialize replication from master database (not available for h2) | `true`  |
 
-The usual way to provide a database is meant to deploy it as a dependency of
-this chart. Since the configuration of the database is different depending on
-the database provider used, the configuration is described in separate documents:
+H2 databases do not require special configuration. It just has to be configured
+in the `gerrit.config`. Also persistence for the database-directory should be
+enabled.
+
+***note
+In general, H2-databases should not be used in production with Gerrit versions <=
+2.15. This chart does not provide replication for H2 databases, thus changes
+to the database at runtime will not be replicated. With Gerrit 2.16 the data
+stored in the database does not change at runtime, thus a H2 database can be
+safely used.
+***
+
+The usual way to provide a database other than H2 is meant to deploy it as a
+dependency of this chart. Since the configuration of the database is different
+depending on the database provider used, the configuration is described in
+separate documents:
 
 - [MySQL](/helm-charts/gerrit-slave/docs/mysql.md)
 
diff --git a/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml b/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
index f41c678..e4f12c8 100644
--- a/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
+++ b/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
@@ -8,6 +8,5 @@
     heritage: {{ .Release.Service }}
     release: {{ .Release.Name }}
 data:
-  test-mode: {{ if .Values.gerritSlave.initializeTestSite.enabled }} "true" {{ else }} "false" {{ end }}
   gerrit.config: |-
 {{ .Values.gerritSlave.config.gerrit | indent 4 }}
\ No newline at end of file
diff --git a/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml b/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
index 65dd6f6..9c0baa3 100644
--- a/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
+++ b/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
@@ -51,11 +51,9 @@
         args:
         - |
           if [ ! -f /var/gerrit/bin/gerrit.sh ]; then
-            java -jar /var/war/gerrit.war init \
-              --batch \
-              --no-auto-start \
-              --install-plugin singleusergroup \
-              -d /var/gerrit
+            /var/tools/gerrit_init.py \
+              -s /var/gerrit \
+              -p singleusergroup
           fi
         volumeMounts:
         - name: gerrit-site
@@ -63,12 +61,14 @@
         - name: gerrit-db
           mountPath: "/var/gerrit/db"
       {{- end }}
+      # Wait for database to be ready and, if configured, run initialization
+      # taking the given Gerrit configuration and persisted volumes into account.
       - name: gerrit-init
         image: {{ template "registry" . }}{{ .Values.gerritSlave.images.gerritInit }}:{{ .Values.images.version }}
         imagePullPolicy: {{ .Values.images.imagePullPolicy }}
         command:
         - /bin/bash
-        - -c
+        - -ce
         args:
         - |
           symlink_config_to_site(){
@@ -82,20 +82,22 @@
           mkdir -p /var/gerrit/etc
           symlink_config_to_site
 
-          /var/tools/start
-        env:
-        - name: TEST_MODE
-          valueFrom:
-            configMapKeyRef:
-              name: {{ .Release.Name }}-gerrit-slave-configmap
-              key: test-mode
+          {{ if .Values.gerritSlave.initializeTestSite.enabled -}}
+          if [ ! -f /var/gerrit/bin/gerrit.sh ]; then
+            /var/tools/gerrit_init.py \
+              -s /var/gerrit \
+              -p singleusergroup
+
+            symlink_config_to_site
+          fi
+          {{- end }}
+
+          /var/tools/validate_db.py -s /var/gerrit
         volumeMounts:
         - name: gerrit-site
           mountPath: "/var/gerrit"
         - name: gerrit-db
           mountPath: "/var/gerrit/db"
-        - name: gerrit-logs
-          mountPath: "/var/gerrit/logs"
         - name: gerrit-config
           mountPath: "/var/config/gerrit.config"
           subPath: gerrit.config
@@ -158,12 +160,6 @@
               --slave &
 
           tail -F -n +1 /var/gerrit/logs/{error,httpd,sshd}_log
-        env:
-        - name: TEST_MODE
-          valueFrom:
-            configMapKeyRef:
-              name: {{ .Release.Name }}-gerrit-slave-configmap
-              key: test-mode
         ports:
         - containerPort: 8080
         volumeMounts:
diff --git a/helm-charts/gerrit-slave/values.yaml b/helm-charts/gerrit-slave/values.yaml
index 941e291..54f13c0 100644
--- a/helm-charts/gerrit-slave/values.yaml
+++ b/helm-charts/gerrit-slave/values.yaml
@@ -207,7 +207,8 @@
 
   config:
     # Some values are expected to have a specific value for the deployment installed
-    # by this chart to work. These are marked with `# FIXED`. Do not change them!
+    # by this chart to work. These are marked with `# FIXED`.
+    # Do not change them!
     gerrit: |-
       [gerrit]
         basePath = git # FIXED