Add test for gerrit-master chart creating and cloning a repo
This change adds integration tests for the gerrit-master chart, which
test whether creation of a project via the Rest API and the subsequent
cloning of this project works. These tests should validate that basic
Gerrit functionality is working using the helm chart to set up a Gerrit
deployment.
Change-Id: I288578ed77f823043703fe66c610a98bb60a20dc
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 2f6d344..fdca7ed 100644
--- a/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml
+++ b/helm-charts/gerrit-master/templates/gerrit-master.stateful-set.yaml
@@ -21,6 +21,9 @@
metadata:
labels:
app: gerrit-master
+ chart: {{ template "gerrit-master.chart" . }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
spec:
securityContext:
fsGroup: 100
diff --git a/tests/helm-charts/conftest.py b/tests/helm-charts/conftest.py
index 02c8f05..26f4390 100644
--- a/tests/helm-charts/conftest.py
+++ b/tests/helm-charts/conftest.py
@@ -104,6 +104,7 @@
self.current_context = None
self.helm = None
+ self.namespaces = list()
def _load_kube_config(self):
config.load_kube_config(config_file=self.kube_config)
@@ -159,7 +160,7 @@
else:
raise exc
- def _create_image_pull_secret(self):
+ def create_image_pull_secret(self, namespace="default"):
secret_metadata = client.V1ObjectMeta(name="image-pull-secret")
auth_string = str.encode(
"%s:%s" % (self.registry["user"], self.registry["pwd"])
@@ -181,7 +182,7 @@
)
core_v1 = client.CoreV1Api()
try:
- core_v1.create_namespaced_secret("default", secret_body)
+ core_v1.create_namespaced_secret(namespace, secret_body)
except client.rest.ApiException as exc:
if exc.status == 409 and exc.reason == "Conflict":
warnings.warn(
@@ -190,6 +191,21 @@
else:
raise exc
+ def create_namespace(self, name):
+ namespace_metadata = client.V1ObjectMeta(name=name)
+ namespace_body = client.V1Namespace(
+ kind="Namespace", api_version="v1", metadata=namespace_metadata
+ )
+ core_v1 = client.CoreV1Api()
+ core_v1.create_namespace(body=namespace_body)
+ self.namespaces.append(name)
+ self.create_image_pull_secret(name)
+
+ def delete_namespace(self, name):
+ core_v1 = client.CoreV1Api()
+ core_v1.delete_namespace(name, body=client.V1DeleteOptions())
+ self.namespaces.remove(name)
+
def init_helm(self):
self._create_and_deploy_helm_crb()
self._create_and_deploy_helm_service_account()
@@ -225,7 +241,7 @@
def setup(self):
self._load_kube_config()
- self._create_image_pull_secret()
+ self.create_image_pull_secret()
self.init_helm()
self.install_storage_provisioner()
@@ -237,6 +253,8 @@
core_v1.delete_namespaced_secret(
"image-pull-secret", "default", body=client.V1DeleteOptions()
)
+ while self.namespaces:
+ self.delete_namespace(self.namespaces[0])
@pytest.fixture(scope="session")
diff --git a/tests/helm-charts/gerrit-master/conftest.py b/tests/helm-charts/gerrit-master/conftest.py
index 19b5abc..510a4b2 100644
--- a/tests/helm-charts/gerrit-master/conftest.py
+++ b/tests/helm-charts/gerrit-master/conftest.py
@@ -15,9 +15,16 @@
# limitations under the License.
import os.path
+import time
import pytest
+from kubernetes import client
+
+import utils
+
+GERRIT_MASTER_STARTUP_TIMEOUT = 240
+
@pytest.fixture(scope="module")
def gerrit_master_deployment(
@@ -30,17 +37,55 @@
gerrit_init_image,
):
chart_path = os.path.join(repository_root, "helm-charts", "gerrit-master")
- chart_name = "gerrit-master"
+ chart_name = "gerrit-master-" + utils.create_random_string()
chart_opts = {
"images.registry.name": request.config.getoption("--registry"),
"images.version": docker_tag,
"gerritMaster.ingress.host": "master.%s"
% request.config.getoption("--ingress-url"),
}
+ namespace_name = utils.create_random_string()
+ test_cluster.create_namespace(namespace_name)
test_cluster.helm.install(
- chart_path, chart_name, set_values=chart_opts, fail_on_err=True
+ chart_path,
+ chart_name,
+ set_values=chart_opts,
+ fail_on_err=True,
+ namespace=namespace_name,
)
- yield
+ yield {"name": chart_name, "namespace": namespace_name}
test_cluster.helm.delete(chart_name)
+ test_cluster.delete_namespace(namespace_name)
+
+
+@pytest.fixture(scope="module")
+def gerrit_master_ready_deployment(gerrit_master_deployment):
+ def wait_for_readiness():
+ pod_labels = "app=gerrit-master,release=%s" % gerrit_master_deployment["name"]
+ core_v1 = client.CoreV1Api()
+ pod_list = core_v1.list_pod_for_all_namespaces(
+ watch=False, label_selector=pod_labels
+ )
+ for condition in pod_list.items[0].status.conditions:
+ if condition.type == "Ready" and condition.status == "True":
+ return True
+ return False
+
+ timeout = time.time() + GERRIT_MASTER_STARTUP_TIMEOUT
+
+ finished_in_time = False
+
+ while time.time() <= timeout:
+ finished_in_time = wait_for_readiness()
+ if finished_in_time:
+ break
+
+ if not finished_in_time:
+ raise utils.TimeOutException(
+ "Gerrit master pod was not ready in time (%d s)."
+ % GERRIT_MASTER_STARTUP_TIMEOUT
+ )
+
+ yield finished_in_time
diff --git a/tests/helm-charts/gerrit-master/test_chart_gerrit_master_usage.py b/tests/helm-charts/gerrit-master/test_chart_gerrit_master_usage.py
new file mode 100644
index 0000000..e7e376e
--- /dev/null
+++ b/tests/helm-charts/gerrit-master/test_chart_gerrit_master_usage.py
@@ -0,0 +1,54 @@
+# pylint: disable=W0613
+
+# 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 git
+import pytest
+import requests
+
+
+@pytest.mark.slow
+@pytest.mark.incremental
+@pytest.mark.integration
+@pytest.mark.kubernetes
+class TestGerritMasterChartSetup:
+ @pytest.mark.timeout(240)
+ def test_create_project_rest(
+ self, request, test_cluster, gerrit_master_ready_deployment
+ ):
+ create_project_url = "http://master.%s/a/projects/test" % (
+ request.config.getoption("--ingress-url")
+ )
+ response = None
+
+ while not response:
+ try:
+ response = requests.put(create_project_url, auth=("admin", "secret"))
+ except requests.exceptions.ConnectionError:
+ continue
+
+ assert response.status_code == 201
+
+ def test_cloning_project(
+ self, request, tmp_path_factory, test_cluster, gerrit_master_ready_deployment
+ ):
+ clone_dest = tmp_path_factory.mktemp("gerrit_master_chart_clone_test")
+ repo_url = "http://master.%s/test.git" % (
+ request.config.getoption("--ingress-url")
+ )
+ repo = git.Repo.clone_from(repo_url, clone_dest)
+ assert repo.git_dir == os.path.join(clone_dest, ".git")
diff --git a/tests/helpers/helm.py b/tests/helpers/helm.py
index 2c7aadc..28ed2f3 100644
--- a/tests/helpers/helm.py
+++ b/tests/helpers/helm.py
@@ -20,11 +20,11 @@
def __init__(self, kubeconfig, kubecontext):
"""Wrapper for Helm CLI.
- Arguments:
- kubeconfig {str} -- Path to kubeconfig-file describing the cluster to
- connect to.
- kubecontext {str} -- Name of the context to use.
- """
+ Arguments:
+ kubeconfig {str} -- Path to kubeconfig-file describing the cluster to
+ connect to.
+ kubecontext {str} -- Name of the context to use.
+ """
self.kubeconfig = kubeconfig
self.kubecontext = kubecontext
@@ -61,7 +61,15 @@
helm_cmd = ["init", "--wait", "--service-account", serviceaccount]
return self._exec_command(helm_cmd)
- def install(self, chart, name, values_file=None, set_values=None, fail_on_err=True):
+ def install(
+ self,
+ chart,
+ name,
+ values_file=None,
+ set_values=None,
+ namespace=None,
+ fail_on_err=True,
+ ):
"""Installs a chart on the cluster
Arguments:
@@ -73,6 +81,7 @@
set_values {dict} -- Dictionary containing key-value-pairs that are used
to overwrite values in the values.yaml-file.
(default: {None})
+ namespace {str} -- Namespace to install the release into (default: {default})
fail_on_err {bool} -- Whether to fail with an exception if the installation
fails (default: {True})
@@ -88,6 +97,8 @@
if set_values:
opt_list = ["%s=%s" % (k, v) for k, v in set_values.items()]
helm_cmd.extend(("--set", ",".join(opt_list)))
+ if namespace:
+ helm_cmd.extend(("--namespace", namespace))
return self._exec_command(helm_cmd, fail_on_err)
def list(self):
diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py
index 8a74927..7b9bf72 100644
--- a/tests/helpers/utils.py
+++ b/tests/helpers/utils.py
@@ -12,11 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import random
+import string
+
+
+class TimeOutException(Exception):
+ """ Exception to be raised, if some action does not finish in time. """
+
def check_if_ancestor_image_is_inherited(image, ancestor):
"""Helper function that looks for a given ancestor image in the layers of a
- provided image. It can be used to check, whether an image uses the expected
- FROM-statement
+ provided image. It can be used to check, whether an image uses the expected
+ FROM-statement
Arguments:
image {docker.images.Image} -- Docker image object to be checked
@@ -32,3 +39,7 @@
if contains_tag:
break
return contains_tag
+
+
+def create_random_string(length=8):
+ return "".join([random.choice(string.ascii_letters) for n in range(length)]).lower()