Configure gerrit-init.py script with a config file

The gerrit-init.py script was configured with parameters. That was hard
to track and sharing configuration between scripts was difficult.

This change adds a configmap to the helm charts that contains the
configuration for the gerrit-init script.

Change-Id: Id42b64fce7bdea209067442383b2f06f02d95572
diff --git a/container-images/gerrit-init/Dockerfile b/container-images/gerrit-init/Dockerfile
index a1a6dd1..9049214 100644
--- a/container-images/gerrit-init/Dockerfile
+++ b/container-images/gerrit-init/Dockerfile
@@ -2,13 +2,23 @@
 
 USER root
 
-COPY tools/* /var/tools/
+COPY dependencies/* /var/tools/
 WORKDIR /var/tools
 
 RUN apk update && \
     apk add --no-cache \
-      python3
+      python3 && \
+    python3 -m ensurepip && \
+    rm -r /usr/lib/python*/ensurepip && \
+    pip3 install --no-cache --upgrade pip setuptools wheel pipenv && \
+    pipenv install --python 3.7.3 --system
+
+COPY tools/* /var/tools/
+COPY config/* /var/config/
+
+COPY tools/* /var/tools/
 
 USER gerrit
 
 ENTRYPOINT ["/var/tools/gerrit_init.py", "-s", "/var/gerrit"]
+CMD ["-c", "/var/config/default.config.yaml"]
diff --git a/container-images/gerrit-init/config/default.config.yaml b/container-images/gerrit-init/config/default.config.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/container-images/gerrit-init/config/default.config.yaml
diff --git a/container-images/gerrit-init/dependencies/Pipfile b/container-images/gerrit-init/dependencies/Pipfile
new file mode 100644
index 0000000..32dcbb1
--- /dev/null
+++ b/container-images/gerrit-init/dependencies/Pipfile
@@ -0,0 +1,12 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+
+[packages]
+pyyaml = "~=5.1.1"
+
+[requires]
+python_version = "3.7"
diff --git a/container-images/gerrit-init/dependencies/Pipfile.lock b/container-images/gerrit-init/dependencies/Pipfile.lock
new file mode 100644
index 0000000..4196274
--- /dev/null
+++ b/container-images/gerrit-init/dependencies/Pipfile.lock
@@ -0,0 +1,40 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "4c5889e94a1b967f4cf238d7c5265e62e03611deb65b66c4a77f1673505436d7"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.7"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "pyyaml": {
+            "hashes": [
+                "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
+                "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
+                "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
+                "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
+                "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
+                "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
+                "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
+                "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
+                "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
+                "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
+                "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
+                "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
+                "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
+            ],
+            "index": "pypi",
+            "version": "==5.1.2"
+        }
+    },
+    "develop": {}
+}
diff --git a/container-images/gerrit-init/tools/gerrit_init.py b/container-images/gerrit-init/tools/gerrit_init.py
index 85f4a5d..f4be415 100755
--- a/container-images/gerrit-init/tools/gerrit_init.py
+++ b/container-images/gerrit-init/tools/gerrit_init.py
@@ -20,15 +20,16 @@
 import sys
 
 from git_config_parser import GitConfigParser
+from init_config import InitConfig
 from log import get_logger
 
 LOG = get_logger("init")
 
 
 class GerritInit:
-    def __init__(self, site, wanted_plugins):
+    def __init__(self, site, config):
         self.site = site
-        self.wanted_plugins = set(wanted_plugins)
+        self.config = config
 
         self.gerrit_config = self._parse_gerrit_config()
         self.is_slave = self._is_slave()
@@ -65,7 +66,7 @@
         return installed_plugins
 
     def _remove_unwanted_plugins(self):
-        for plugin in self.installed_plugins.difference(self.wanted_plugins):
+        for plugin in self.installed_plugins.difference(self.config.packaged_plugins):
             LOG.info("Removing plugin %s", plugin)
             os.remove(os.path.join(self.site, "plugins", "%s.jar" % plugin))
 
@@ -90,7 +91,7 @@
             LOG.info("Reinitializing site to perform update.")
             return True
 
-        if self.wanted_plugins.difference(self.installed_plugins):
+        if self.config.packaged_plugins.difference(self.installed_plugins):
             LOG.info("Reininitializing site to install additional plugins.")
             return True
 
@@ -108,9 +109,13 @@
         else:
             LOG.info("No gerrit.config found. Initializing default site.")
 
-        if self.wanted_plugins:
+        if self.config.packaged_plugins:
             plugin_options = " ".join(
-                ["--install-plugin %s" % plugin for plugin in self.wanted_plugins]
+                [
+                    "--install-plugin %s" % plugin
+                    for plugin in self.config.packaged_plugins
+                    if plugin
+                ]
             )
         else:
             plugin_options = ""
@@ -149,14 +154,16 @@
         required=True,
     )
     parser.add_argument(
-        "-p",
-        "--plugin",
-        help="Gerrit plugin to be installed. Can be used multiple times.",
-        dest="wanted_plugins",
-        action="append",
-        default=list(),
+        "-c",
+        "--config",
+        help="Path to configuration file for init process.",
+        dest="config",
+        action="store",
+        required=True,
     )
     args = parser.parse_args()
 
-    init = GerritInit(args.site, args.wanted_plugins)
+    config = InitConfig().parse(args.config)
+
+    init = GerritInit(args.site, config)
     init.execute()
diff --git a/container-images/gerrit-init/tools/init_config.py b/container-images/gerrit-init/tools/init_config.py
new file mode 100644
index 0000000..31d9394
--- /dev/null
+++ b/container-images/gerrit-init/tools/init_config.py
@@ -0,0 +1,37 @@
+# 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 os.path
+
+import yaml
+
+
+class InitConfig:
+    def __init__(self):
+        self.packaged_plugins = set()
+
+    def parse(self, config_file):
+        if not os.path.exists(config_file):
+            raise FileNotFoundError("Could not find config file: %s" % config_file)
+
+        with open(config_file, "r") as f:
+            config = yaml.load(f, Loader=yaml.SafeLoader)
+
+        if config is None:
+            raise ValueError("Invalid config-file: %s" % config_file)
+
+        if "packagedPlugins" in config:
+            self.packaged_plugins = set(config["packagedPlugins"])
+
+        return self
diff --git a/helm-charts/gerrit-master/templates/gerrit-master.configmap.yaml b/helm-charts/gerrit-master/templates/gerrit-master.configmap.yaml
index df7422d..30cddc6 100644
--- a/helm-charts/gerrit-master/templates/gerrit-master.configmap.yaml
+++ b/helm-charts/gerrit-master/templates/gerrit-master.configmap.yaml
@@ -11,4 +11,20 @@
   gerrit.config: |-
 {{ .Values.gerritMaster.config.gerrit | indent 4 }}
   replication.config: |-
-{{ .Values.gerritMaster.config.replication | indent 4 }}
\ No newline at end of file
+{{ .Values.gerritMaster.config.replication | indent 4 }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Release.Name }}-gerrit-init-configmap
+  labels:
+    app: gerrit-master
+    chart: {{ template "gerrit-master.chart" . }}
+    heritage: {{ .Release.Service }}
+    release: {{ .Release.Name }}
+data:
+  gerrit-init.yaml: |-
+    {{- if .Values.gerritMaster.plugins.packaged }}
+    packagedPlugins:
+{{ toYaml .Values.gerritMaster.plugins.packaged | indent 6}}
+    {{- end }}
diff --git a/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml b/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml
index 2c05a00..bfae1f6 100644
--- a/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml
+++ b/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml
@@ -73,11 +73,7 @@
           fi
 
           /var/tools/gerrit_init.py \
-            {{- range .Values.gerritMaster.plugins.packaged }}
-            {{- if . }}
-            -p {{ . }} \
-            {{- end }}
-            {{- end }}
+            -c /var/config/gerrit-init.yaml \
             -s /var/gerrit
 
           symlink_config_to_site
@@ -86,6 +82,9 @@
           mountPath: "/var/gerrit"
         - name: git-filesystem
           mountPath: "/var/mnt/git"
+        - name: gerrit-init-config
+          mountPath: "/var/config/gerrit-init.yaml"
+          subPath: gerrit-init.yaml
         - name: gerrit-config
           mountPath: "/var/config/gerrit.config"
           subPath: gerrit.config
@@ -147,6 +146,9 @@
           {{- else }}
           claimName: {{ .Release.Name }}-git-filesystem-pvc
           {{- end }}
+      - name: gerrit-init-config
+        configMap:
+          name: {{ .Release.Name }}-gerrit-init-configmap
       - name: gerrit-config
         configMap:
           name: {{ .Release.Name }}-gerrit-master-configmap
diff --git a/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml b/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
index e4f12c8..9586b6f 100644
--- a/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
+++ b/helm-charts/gerrit-slave/templates/gerrit-slave.configmap.yaml
@@ -9,4 +9,18 @@
     release: {{ .Release.Name }}
 data:
   gerrit.config: |-
-{{ .Values.gerritSlave.config.gerrit | indent 4 }}
\ No newline at end of file
+{{ .Values.gerritSlave.config.gerrit | indent 4 }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Release.Name }}-gerrit-init-configmap
+  labels:
+    app: gerrit-slave
+    chart: {{ template "gerrit-slave.chart" . }}
+    heritage: {{ .Release.Service }}
+    release: {{ .Release.Name }}
+data:
+  gerrit-init.yaml: |-
+    packagedPlugins:
+      - singleusergroup
diff --git a/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml b/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
index 73fd8e2..3fa2357 100644
--- a/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
+++ b/helm-charts/gerrit-slave/templates/gerrit-slave.deployment.yaml
@@ -58,14 +58,17 @@
         args:
         - |
           /var/tools/gerrit_init.py \
-            -s /var/gerrit \
-            -p singleusergroup
+            -c /var/config/gerrit-init.yaml \
+            -s /var/gerrit
 
           # The git repositories will be mounted from a volume
           [ -L /var/gerrit/git ] || rm -rf /var/gerrit/git
         volumeMounts:
         - name: gerrit-site
           mountPath: "/var/gerrit"
+        - name: gerrit-init-config
+          mountPath: "/var/config/gerrit-init.yaml"
+          subPath: gerrit-init.yaml
       {{- end }}
       # If configured, run initialization taking the given Gerrit configuration
       # and persisted volumes into account.
@@ -94,8 +97,8 @@
 
           {{ if .Values.gerritSlave.initializeTestSite.enabled -}}
           /var/tools/gerrit_init.py \
-            -s /var/gerrit \
-            -p singleusergroup
+            -c /var/config/gerrit-init.yaml \
+            -s /var/gerrit
 
           symlink_config_to_site
           {{- end }}
@@ -106,6 +109,9 @@
           mountPath: "/var/gerrit"
         - name: git-filesystem
           mountPath: "/var/mnt/git"
+        - name: gerrit-init-config
+          mountPath: "/var/config/gerrit-init.yaml"
+          subPath: gerrit-init.yaml
         - name: gerrit-config
           mountPath: "/var/config/gerrit.config"
           subPath: gerrit.config
@@ -152,6 +158,9 @@
       - name: git-filesystem
         persistentVolumeClaim:
           claimName: {{ .Release.Name }}-git-filesystem-pvc
+      - name: gerrit-init-config
+        configMap:
+          name: {{ .Release.Name }}-gerrit-init-configmap
       - name: gerrit-config
         configMap:
           name: {{ .Release.Name }}-gerrit-slave-configmap
diff --git a/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py b/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
index 057f5ba..082ffa7 100644
--- a/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
+++ b/tests/container-images/gerrit-init/test_container_integration_gerrit_init.py
@@ -14,11 +14,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os.path
 import re
 
 from docker.errors import NotFound
 
 import pytest
+import yaml
 
 
 @pytest.fixture(scope="class")
@@ -44,14 +46,28 @@
 
 
 @pytest.fixture(scope="class")
-def container_run_endless(docker_client, gerrit_init_image, tmp_path_factory):
-    tmp_site_dir = tmp_path_factory.mktemp("gerrit_site")
+def init_config_dir(tmp_path_factory):
+    return tmp_path_factory.mktemp("init_config")
+
+
+@pytest.fixture(scope="class")
+def tmp_site_dir(tmp_path_factory):
+    return tmp_path_factory.mktemp("gerrit_site")
+
+
+@pytest.fixture(scope="class")
+def container_run_endless(
+    docker_client, gerrit_init_image, init_config_dir, tmp_site_dir
+):
     container_run = docker_client.containers.run(
         image=gerrit_init_image.id,
         entrypoint="/bin/ash",
         command=["-c", "tail -f /dev/null"],
         user="gerrit",
-        volumes={tmp_site_dir: {"bind": "/var/gerrit", "mode": "rw"}},
+        volumes={
+            tmp_site_dir: {"bind": "/var/gerrit", "mode": "rw"},
+            init_config_dir: {"bind": "/var/config", "mode": "rw"},
+        },
         detach=True,
         auto_remove=True,
     )
@@ -88,52 +104,44 @@
         assert container_run_default.attrs["State"]["ExitCode"] == 0
 
 
+@pytest.fixture(
+    scope="function",
+    params=[
+        ["replication", "reviewnotes"],
+        ["replication", "reviewnotes", "hooks"],
+        ["download-commands"],
+        [],
+    ],
+)
+def plugins_to_install(request):
+    return request.param
+
+
 @pytest.mark.docker
 @pytest.mark.incremental
 @pytest.mark.integration
 class TestGerritInitPluginInstallation:
-    def test_gerrit_init_plugins_are_installed(self, container_run_endless):
-        exit_code, _ = container_run_endless.exec_run(
-            "/var/tools/gerrit_init.py -s /var/gerrit -p replication -p reviewnotes"
-        )
-        assert exit_code == 0
-        cmd = (
-            "/bin/ash -c '"
-            + "test -f /var/gerrit/plugins/replication.jar && "
-            + "test -f /var/gerrit/plugins/reviewnotes.jar'"
-        )
-        exit_code, _ = container_run_endless.exec_run(cmd)
-        assert exit_code == 0
+    def _configure_packaged_plugins(self, file_path, plugins):
+        with open(file_path, "w") as f:
+            yaml.dump({"packagedPlugins": plugins}, f, default_flow_style=False)
 
-    def test_gerrit_init_plugins_are_added_in_existing_site(
-        self, container_run_endless
+    def test_gerrit_init_plugins_are_installed(
+        self, container_run_endless, init_config_dir, plugins_to_install, tmp_site_dir
     ):
+        self._configure_packaged_plugins(
+            os.path.join(init_config_dir, "init.yaml"), plugins_to_install
+        )
+
         exit_code, _ = container_run_endless.exec_run(
-            "/var/tools/gerrit_init.py -s /var/gerrit -p replication -p reviewnotes -p hooks"
+            "/var/tools/gerrit_init.py -s /var/gerrit -c /var/config/init.yaml"
         )
         assert exit_code == 0
 
-        cmd = (
-            "/bin/ash -c '"
-            + "test -f /var/gerrit/plugins/replication.jar && "
-            + "test -f /var/gerrit/plugins/reviewnotes.jar && "
-            + "test -f /var/gerrit/plugins/hooks.jar'"
-        )
-        exit_code, _ = container_run_endless.exec_run(cmd)
-        assert exit_code == 0
+        plugins_path = os.path.join(tmp_site_dir, "plugins")
 
-    def test_gerrit_init_plugins_are_installed_in_existing_site(
-        self, container_run_endless
-    ):
-        exit_code, _ = container_run_endless.exec_run(
-            "/var/tools/gerrit_init.py -s /var/gerrit -p download-commands"
-        )
-        assert exit_code == 0
+        for plugin in plugins_to_install:
+            assert os.path.exists(os.path.join(plugins_path, "%s.jar" % plugin))
 
-        cmd = "/bin/ash -c '" + "test -f /var/gerrit/plugins/download-commands.jar'"
-        exit_code, _ = container_run_endless.exec_run(cmd)
-        assert exit_code == 0
-
-        cmd = "/bin/ash -c '" + "test -f /var/gerrit/plugins/reviewnotes.jar'"
-        exit_code, _ = container_run_endless.exec_run(cmd)
-        assert exit_code == 1
+        installed_plugins = os.listdir(plugins_path)
+        for plugin in installed_plugins:
+            assert os.path.splitext(plugin)[0] in plugins_to_install
diff --git a/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py b/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
index 21249bc..38d37ec 100755
--- a/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
+++ b/tests/container-images/gerrit-init/test_container_structure_gerrit_init.py
@@ -26,7 +26,11 @@
 
 @pytest.fixture(
     scope="function",
-    params=["/var/tools/gerrit_init.py", "/var/tools/git_config_parser.py"],
+    params=[
+        "/var/tools/gerrit_init.py",
+        "/var/tools/git_config_parser.py",
+        "/var/tools/init_config.py",
+    ],
 )
 def expected_script(request):
     return request.param
@@ -37,6 +41,11 @@
     return request.param
 
 
+@pytest.fixture(scope="function", params=["pyyaml"])
+def expected_pip_package(request):
+    return request.param
+
+
 # pylint: disable=E1101
 @pytest.mark.structure
 def test_gerrit_init_inherits_from_gerrit_base(gerrit_init_image):
diff --git a/tests/helm-charts/gerrit-master/test_chart_gerrit_master_plugins.py b/tests/helm-charts/gerrit-master/test_chart_gerrit_master_plugins.py
index 63edd10..d4470ec 100644
--- a/tests/helm-charts/gerrit-master/test_chart_gerrit_master_plugins.py
+++ b/tests/helm-charts/gerrit-master/test_chart_gerrit_master_plugins.py
@@ -121,6 +121,7 @@
         chart = gerrit_master_deployment_with_packaged_plugins
         chart["removed_plugin"] = chart["installed_plugins"].pop()
         plugins_opts_string = "{%s}" % (",".join(chart["installed_plugins"]))
+        plugins_opts_string = plugins_opts_string if plugins_opts_string else "false"
         update_chart(
             test_cluster.helm,
             chart,