import logging
import os
+from pathlib import Path
+import re
import subprocess
import time
+import yaml
-from functest.core import testcase
+from xtesting.core import testcase
-LOGGER = logging.getLogger(__name__)
+class E2ETesting(testcase.TestCase):
+ """Kubernetes test runner"""
+ # pylint: disable=too-many-instance-attributes
+ __logger = logging.getLogger(__name__)
-class K8sTesting(testcase.TestCase):
- """Kubernetes test runner"""
+ config = f'{Path.home()}/.kube/config'
+ gcr_repo = os.getenv("MIRROR_REPO", "gcr.io")
+ k8s_gcr_repo = os.getenv("MIRROR_REPO", "registry.k8s.io")
def __init__(self, **kwargs):
- super(K8sTesting, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.cmd = []
+ self.dir_results = "/home/opnfv/functest/results"
+ self.res_dir = os.path.join(self.dir_results, self.case_name)
self.result = 0
self.start_time = 0
self.stop_time = 0
+ self.output_log_name = 'functest-kubernetes.log'
+ self.output_debug_log_name = 'functest-kubernetes.debug.log'
- def run_kubetest(self):
- """Run the test suites"""
- cmd_line = self.cmd
- LOGGER.info("Starting k8s test: '%s'.", cmd_line)
-
- process = subprocess.Popen(cmd_line, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- remark = []
- lines = process.stdout.readlines()
- for i in range(len(lines) - 1, -1, -1):
- new_line = str(lines[i])
-
- if 'SUCCESS!' in new_line or 'FAIL!' in new_line:
- remark = new_line.replace('--', '|').split('|')
- break
-
- if remark and 'SUCCESS!' in remark[0]:
- self.result = 100
-
- def run(self):
+ @staticmethod
+ def convert_ini_to_dict(value):
+ "Convert oslo.conf input to dict"
+ assert isinstance(value, str)
+ try:
+ return dict((x.rsplit(':', 1) for x in value.split(',')))
+ except ValueError:
+ return {}
- if not os.path.isfile(os.getenv('KUBECONFIG')):
- LOGGER.error("Cannot run k8s testcases. Config file not found ")
+ def run_kubetest(self, **kwargs): # pylint: disable=too-many-branches
+ """Run the test suites"""
+ cmd_line = [
+ 'ginkgo', f'--nodes={kwargs.get("nodes", 1)}',
+ '--no-color', '/usr/local/bin/e2e.test', '--',
+ '-kubeconfig', self.config,
+ '-provider', kwargs.get('provider', 'local'),
+ '-report-dir', self.res_dir]
+ for arg in kwargs.get("ginkgo", {}):
+ cmd_line.extend([f'-ginkgo.{arg}', kwargs["ginkgo"][arg]])
+ for key, value in self.convert_ini_to_dict(
+ os.environ.get("E2E_TEST_OPTS", "")).items():
+ cmd_line.extend([f'-{key}', value])
+ if "NON_BLOCKING_TAINTS" in os.environ:
+ cmd_line.extend(
+ ['-non-blocking-taints', os.environ["NON_BLOCKING_TAINTS"]])
+ cmd_line.extend(['-disable-log-dump'])
+ self._generate_repo_list_file()
+ self.__logger.info("Starting k8s test: '%s'.", cmd_line)
+ env = os.environ.copy()
+ env["KUBE_TEST_REPO_LIST"] = f"{self.res_dir}/repositories.yml"
+ with subprocess.Popen(
+ cmd_line, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, env=env) as process:
+ boutput = process.stdout.read()
+ with open(os.path.join(
+ self.res_dir, 'e2e.log'), 'w', encoding='utf-8') as foutput:
+ foutput.write(boutput.decode("utf-8"))
+ grp = re.search(
+ r'^(FAIL|SUCCESS)!.* ([0-9]+) Passed \| ([0-9]+) Failed \|'
+ r' ([0-9]+) Pending \| ([0-9]+) Skipped',
+ boutput.decode("utf-8", errors="ignore"),
+ re.MULTILINE | re.DOTALL)
+ try:
+ assert grp
+ except AssertionError:
+ self.__logger.exception(
+ "Can not find the overall result in \n%s",
+ boutput.decode("utf-8", errors="ignore"))
+ return False
+ self.details['passed'] = int(grp.group(2))
+ self.details['failed'] = int(grp.group(3))
+ self.details['pending'] = int(grp.group(4))
+ self.details['skipped'] = int(grp.group(5))
+ self.__logger.debug("details: %s", self.details)
+ self.result = self.details['passed'] * 100 / (
+ self.details['passed'] + self.details['failed'] +
+ self.details['pending'])
+ self.__logger.debug("result: %s", self.result)
+ if grp.group(1) == 'FAIL':
+ grp2 = re.search(
+ r'^(Summarizing [0-9]+ Failure.*)Ran',
+ boutput.decode("utf-8", errors="ignore"),
+ re.MULTILINE | re.DOTALL)
+ if grp2:
+ self.__logger.error(grp2.group(1))
+ return True
+
+ def run(self, **kwargs):
+ res = self.EX_RUN_ERROR
+ if not os.path.exists(self.res_dir):
+ os.makedirs(self.res_dir)
+ if not os.path.isfile(self.config):
+ self.__logger.error(
+ "Cannot run k8s testcases. Config file not found")
return self.EX_RUN_ERROR
-
self.start_time = time.time()
try:
- self.run_kubetest()
- res = self.EX_OK
- except Exception as ex: # pylint: disable=broad-except
- LOGGER.error("Error with running %s", str(ex))
- res = self.EX_RUN_ERROR
-
+ if self.run_kubetest(**kwargs):
+ res = self.EX_OK
+ except Exception: # pylint: disable=broad-except
+ self.__logger.exception("Error with running kubetest:")
self.stop_time = time.time()
return res
- def check_envs(self): # pylint: disable=no-self-use
- """Check if required environment variables are set"""
- try:
- assert 'DEPLOY_SCENARIO' in os.environ
- assert 'KUBECONFIG' in os.environ
- assert 'KUBE_MASTER_IP' in os.environ
- assert 'KUBERNETES_PROVIDER' in os.environ
- assert 'KUBE_MASTER_URL' in os.environ
- except Exception as ex:
- raise Exception("Cannot run k8s testcases. "
- "Please check env var: %s" % str(ex))
-
-
-class K8sSmokeTest(K8sTesting):
- """Kubernetes smoke test suite"""
- def __init__(self, **kwargs):
- if "case_name" not in kwargs:
- kwargs.get("case_name", 'k8s_smoke')
- super(K8sSmokeTest, self).__init__(**kwargs)
- self.check_envs()
- self.cmd = ['/src/k8s.io/kubernetes/cluster/test-smoke.sh', '--host',
- os.getenv('KUBE_MASTER_URL')]
+ def _generate_repo_list_file(self):
+ """Generate the repositories list for the test."""
+ # The list is taken from
+ # https://github.com/kubernetes/kubernetes/blob/master/test/utils/image/manifest.go
+ # It may needs update regularly
+ gcr_repo = os.getenv("GCR_REPO", self.gcr_repo)
+ k8s_gcr_repo = os.getenv("K8S_GCR_REPO", self.k8s_gcr_repo)
+ repo_list = {
+ "gcAuthenticatedRegistry":
+ f"{gcr_repo}/authenticated-image-pulling",
+ "e2eRegistry": f"{gcr_repo}/kubernetes-e2e-test-images",
+ "promoterE2eRegistry": f"{k8s_gcr_repo}/e2e-test-images",
+ "buildImageRegistry": f"{k8s_gcr_repo}/build-image",
+ "invalidRegistry": "invalid.com/invalid",
+ "gcEtcdRegistry": k8s_gcr_repo,
+ "gcRegistry": k8s_gcr_repo,
+ "sigStorageRegistry": f"{k8s_gcr_repo}/sig-storage",
+ "privateRegistry": f"{gcr_repo}/k8s-authenticated-test",
+ "sampleRegistry": f"{gcr_repo}/google-samples",
+ "gcrReleaseRegistry": f"{gcr_repo}/gke-release",
+ "microsoftRegistry": "mcr.microsoft.com"
+ }
+ with open(
+ f"{self.res_dir}/repositories.yml", 'w',
+ encoding='utf-8') as file:
+ yaml.dump(repo_list, file)