Take latest Rally K8S patch into account
[functest-kubernetes.git] / functest_kubernetes / k8stest.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2018 All rights reserved
4 # This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10
11 """
12 Define the parent for Kubernetes testing.
13 """
14
15 from __future__ import division
16
17 import logging
18 import os
19 from pathlib import Path
20 import re
21 import subprocess
22 import time
23 import yaml
24
25 from xtesting.core import testcase
26
27
28 class E2ETesting(testcase.TestCase):
29     """Kubernetes test runner"""
30     # pylint: disable=too-many-instance-attributes
31
32     __logger = logging.getLogger(__name__)
33
34     config = f'{Path.home()}/.kube/config'
35     gcr_repo = os.getenv("MIRROR_REPO", "gcr.io")
36     k8s_gcr_repo = os.getenv("MIRROR_REPO", "registry.k8s.io")
37
38     def __init__(self, **kwargs):
39         super().__init__(**kwargs)
40         self.cmd = []
41         self.dir_results = "/home/opnfv/functest/results"
42         self.res_dir = os.path.join(self.dir_results, self.case_name)
43         self.result = 0
44         self.start_time = 0
45         self.stop_time = 0
46         self.output_log_name = 'functest-kubernetes.log'
47         self.output_debug_log_name = 'functest-kubernetes.debug.log'
48
49     @staticmethod
50     def convert_ini_to_dict(value):
51         "Convert oslo.conf input to dict"
52         assert isinstance(value, str)
53         try:
54             return dict((x.rsplit(':', 1) for x in value.split(',')))
55         except ValueError:
56             return {}
57
58     def run_kubetest(self, **kwargs):  # pylint: disable=too-many-branches
59         """Run the test suites"""
60         cmd_line = [
61             'ginkgo', f'--nodes={kwargs.get("nodes", 1)}',
62             '--no-color', '/usr/local/bin/e2e.test', '--',
63             '-kubeconfig', self.config,
64             '-provider', kwargs.get('provider', 'local'),
65             '-report-dir', self.res_dir]
66         for arg in kwargs.get("ginkgo", {}):
67             cmd_line.extend([f'-ginkgo.{arg}', kwargs["ginkgo"][arg]])
68         for key, value in self.convert_ini_to_dict(
69                 os.environ.get("E2E_TEST_OPTS", "")).items():
70             cmd_line.extend([f'-{key}', value])
71         if "NON_BLOCKING_TAINTS" in os.environ:
72             cmd_line.extend(
73                 ['-non-blocking-taints', os.environ["NON_BLOCKING_TAINTS"]])
74         cmd_line.extend(['-disable-log-dump'])
75         self._generate_repo_list_file()
76         self.__logger.info("Starting k8s test: '%s'.", cmd_line)
77         env = os.environ.copy()
78         env["KUBE_TEST_REPO_LIST"] = f"{self.res_dir}/repositories.yml"
79         with subprocess.Popen(
80                 cmd_line, stdout=subprocess.PIPE,
81                 stderr=subprocess.STDOUT, env=env) as process:
82             boutput = process.stdout.read()
83         with open(os.path.join(
84                 self.res_dir, 'e2e.log'), 'w', encoding='utf-8') as foutput:
85             foutput.write(boutput.decode("utf-8"))
86         grp = re.search(
87             r'^(FAIL|SUCCESS)!.* ([0-9]+) Passed \| ([0-9]+) Failed \|'
88             r' ([0-9]+) Pending \| ([0-9]+) Skipped',
89             boutput.decode("utf-8", errors="ignore"),
90             re.MULTILINE | re.DOTALL)
91         try:
92             assert grp
93         except AssertionError:
94             self.__logger.exception(
95                 "Can not find the overall result in \n%s",
96                 boutput.decode("utf-8", errors="ignore"))
97             return False
98         self.details['passed'] = int(grp.group(2))
99         self.details['failed'] = int(grp.group(3))
100         self.details['pending'] = int(grp.group(4))
101         self.details['skipped'] = int(grp.group(5))
102         self.__logger.debug("details: %s", self.details)
103         self.result = self.details['passed'] * 100 / (
104             self.details['passed'] + self.details['failed'] +
105             self.details['pending'])
106         self.__logger.debug("result: %s", self.result)
107         if grp.group(1) == 'FAIL':
108             grp2 = re.search(
109                 r'^(Summarizing [0-9]+ Failure.*)Ran',
110                 boutput.decode("utf-8", errors="ignore"),
111                 re.MULTILINE | re.DOTALL)
112             if grp2:
113                 self.__logger.error(grp2.group(1))
114         return True
115
116     def run(self, **kwargs):
117         res = self.EX_RUN_ERROR
118         if not os.path.exists(self.res_dir):
119             os.makedirs(self.res_dir)
120         if not os.path.isfile(self.config):
121             self.__logger.error(
122                 "Cannot run k8s testcases. Config file not found")
123             return self.EX_RUN_ERROR
124         self.start_time = time.time()
125         try:
126             if self.run_kubetest(**kwargs):
127                 res = self.EX_OK
128         except Exception:  # pylint: disable=broad-except
129             self.__logger.exception("Error with running kubetest:")
130         self.stop_time = time.time()
131         return res
132
133     def _generate_repo_list_file(self):
134         """Generate the repositories list for the test."""
135         # The list is taken from
136         # https://github.com/kubernetes/kubernetes/blob/master/test/utils/image/manifest.go
137         # It may needs update regularly
138         gcr_repo = os.getenv("GCR_REPO", self.gcr_repo)
139         k8s_gcr_repo = os.getenv("K8S_GCR_REPO", self.k8s_gcr_repo)
140         repo_list = {
141             "gcAuthenticatedRegistry":
142                 f"{gcr_repo}/authenticated-image-pulling",
143             "e2eRegistry": f"{gcr_repo}/kubernetes-e2e-test-images",
144             "promoterE2eRegistry": f"{k8s_gcr_repo}/e2e-test-images",
145             "buildImageRegistry": f"{k8s_gcr_repo}/build-image",
146             "invalidRegistry": "invalid.com/invalid",
147             "gcEtcdRegistry": k8s_gcr_repo,
148             "gcRegistry": k8s_gcr_repo,
149             "sigStorageRegistry": f"{k8s_gcr_repo}/sig-storage",
150             "privateRegistry": f"{gcr_repo}/k8s-authenticated-test",
151             "sampleRegistry": f"{gcr_repo}/google-samples",
152             "gcrReleaseRegistry": f"{gcr_repo}/gke-release",
153             "microsoftRegistry": "mcr.microsoft.com"
154         }
155         with open(
156                 f"{self.res_dir}/repositories.yml", 'w',
157                 encoding='utf-8') as file:
158             yaml.dump(repo_list, file)