| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 1 | #    Copyright 2017 Mirantis, Inc. | 
|  | 2 | # | 
|  | 3 | #    Licensed under the Apache License, Version 2.0 (the "License"); you may | 
|  | 4 | #    not use this file except in compliance with the License. You may obtain | 
|  | 5 | #    a copy of the License at | 
|  | 6 | # | 
|  | 7 | #         http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | # | 
|  | 9 | #    Unless required by applicable law or agreed to in writing, software | 
|  | 10 | #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | 11 | #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | 12 | #    License for the specific language governing permissions and limitations | 
|  | 13 | #    under the License. | 
|  | 14 |  | 
|  | 15 | import pytest | 
| Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 16 | import time | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 17 |  | 
|  | 18 | from tcp_tests.helpers import ext | 
| Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 19 | from tcp_tests.helpers import utils | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 20 | from tcp_tests import logger | 
|  | 21 | from tcp_tests.managers import k8smanager | 
|  | 22 |  | 
|  | 23 | LOG = logger.logger | 
|  | 24 |  | 
|  | 25 |  | 
|  | 26 | @pytest.fixture(scope='function') | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 27 | def k8s_actions(config, underlay_actions, salt_actions): | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 28 | """Fixture that provides various actions for K8S | 
|  | 29 |  | 
|  | 30 | :param config: fixture provides oslo.config | 
|  | 31 | :param underlay: fixture provides underlay manager | 
|  | 32 | :param salt_deployed: fixture provides salt manager | 
|  | 33 | :rtype: K8SManager | 
|  | 34 |  | 
|  | 35 | For use in tests or fixtures to deploy a custom K8S | 
|  | 36 | """ | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 37 | return k8smanager.K8SManager(config, underlay_actions, salt_actions) | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 38 |  | 
|  | 39 |  | 
|  | 40 | @pytest.mark.revert_snapshot(ext.SNAPSHOT.k8s_deployed) | 
|  | 41 | @pytest.fixture(scope='function') | 
|  | 42 | def k8s_deployed(revert_snapshot, request, config, hardware, underlay, | 
| Dennis Dmitriev | ea48cf5 | 2018-07-18 18:04:39 +0300 | [diff] [blame] | 43 | core_deployed, salt_deployed, k8s_actions): | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 44 | """Fixture to get or install k8s on environment | 
|  | 45 |  | 
|  | 46 | :param revert_snapshot: fixture that reverts snapshot that is specified | 
|  | 47 | in test with @pytest.mark.revert_snapshot(<name>) | 
|  | 48 | :param request: fixture provides pytest data | 
|  | 49 | :param config: fixture provides oslo.config | 
|  | 50 | :param hardware: fixture provides enviromnet manager | 
|  | 51 | :param underlay: fixture provides underlay manager | 
| Dennis Dmitriev | ea48cf5 | 2018-07-18 18:04:39 +0300 | [diff] [blame] | 52 | :param core_deployed: fixture provides CoreManager | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 53 | :param k8s_actions: fixture provides K8SManager instance | 
|  | 54 | :rtype: K8SManager | 
|  | 55 |  | 
|  | 56 | If config.k8s.k8s_installed is not set, this fixture assumes | 
|  | 57 | that the k8s services were not installed, and do the following: | 
|  | 58 | - install k8s services | 
|  | 59 | - make snapshot with name 'k8s_deployed' | 
|  | 60 | - return K8SManager instance | 
|  | 61 |  | 
|  | 62 | If config.k8s.k8s_installed was set, this fixture assumes that | 
|  | 63 | the k8s services were already installed, and do the following: | 
|  | 64 | - return K8SManager instance | 
|  | 65 |  | 
|  | 66 | If you want to revert 'k8s_deployed' snapshot, please use mark: | 
|  | 67 | @pytest.mark.revert_snapshot("k8s_deployed") | 
|  | 68 | """ | 
|  | 69 |  | 
|  | 70 | # Deploy Kubernetes cluster | 
|  | 71 | if not config.k8s.k8s_installed: | 
| Vladimir Jigulin | fbe2e5c | 2018-08-28 21:43:57 +0400 | [diff] [blame] | 72 | # Workaround for dhclient not killed on non-dhcp interfaces | 
|  | 73 | # see https://mirantis.jira.com/browse/PROD-22473 | 
|  | 74 | tgt = 'I@kubernetes:pool' | 
|  | 75 | LOG.warning('Killing dhclient on every non-dhcp interface ' | 
|  | 76 | 'on nodes with target={}'.format(tgt)) | 
|  | 77 | interfaces_pillar = k8s_actions._salt.get_pillar( | 
|  | 78 | tgt=tgt, pillar='linux:network:interface')[0] | 
|  | 79 |  | 
|  | 80 | for node_name, interfaces in interfaces_pillar.items(): | 
|  | 81 | for iface_name, iface in interfaces.items(): | 
|  | 82 | iface_name = iface.get('name', iface_name) | 
|  | 83 | default_proto = 'static' if 'address' in iface else 'dhcp' | 
|  | 84 | if iface.get('proto', default_proto) != 'dhcp': | 
|  | 85 | LOG.warning('Trying to kill dhclient for iface {0} ' | 
|  | 86 | 'on node {1}'.format(iface_name, node_name)) | 
|  | 87 | underlay.check_call( | 
|  | 88 | cmd='pkill -f "dhclient.*{}"'.format(iface_name), | 
|  | 89 | node_name=node_name, raise_on_err=False) | 
|  | 90 |  | 
|  | 91 | LOG.warning('Restarting keepalived service on controllers...') | 
|  | 92 | k8s_actions._salt.local(tgt='ctl*', fun='cmd.run', | 
|  | 93 | args='systemctl restart keepalived.service') | 
|  | 94 | # give some time to keepalived to enter in MASTER state | 
|  | 95 | time.sleep(3) | 
|  | 96 | # --- end of workaround | 
|  | 97 |  | 
|  | 98 | # install k8s | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 99 | steps_path = config.k8s_deploy.k8s_steps_path | 
|  | 100 | commands = underlay.read_template(steps_path) | 
|  | 101 | k8s_actions.install(commands) | 
| Vladimir Jigulin | fbe2e5c | 2018-08-28 21:43:57 +0400 | [diff] [blame] | 102 |  | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 103 | hardware.create_snapshot(ext.SNAPSHOT.k8s_deployed) | 
| Dennis Dmitriev | b8115f5 | 2017-12-15 13:09:56 +0200 | [diff] [blame] | 104 | salt_deployed.sync_time() | 
| Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 105 |  | 
| Artem Panchenko | 501e67e | 2017-06-14 14:59:18 +0300 | [diff] [blame] | 106 | return k8s_actions | 
| Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 107 |  | 
|  | 108 |  | 
| Dennis Dmitriev | 63f9c95 | 2018-01-25 02:07:00 +0200 | [diff] [blame] | 109 | @pytest.fixture(scope='function') | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 110 | def k8s_logs(request, func_name, k8s_actions): | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 111 | """Finalizer to extract conformance logs | 
| Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 112 |  | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 113 | Usage: | 
|  | 114 | @pytest.mark.grab_k8s_result(name=['file1', 'file2']) | 
|  | 115 | ^^^^^ | 
|  | 116 | This mark says tcp-qa to download files that counted in array as | 
|  | 117 | parameter 'name'. Files should be located at ctl01. Files will be | 
|  | 118 | downloaded to the host, where your test runs. | 
|  | 119 |  | 
|  | 120 | @pytest.mark.extract(container_system='docker', extract_from='conformance', | 
|  | 121 | files_to_extract=['report']) | 
|  | 122 | ^^^^^ | 
|  | 123 | This mark says tcp-qa to copy files from container. Docker or k8s system | 
|  | 124 | supported. | 
|  | 125 | container_system param says function what strategy should be | 
|  | 126 | used. | 
|  | 127 | extract_from param says what container should be used to copy. Note | 
|  | 128 | that we are using grep to determine container ID, so if you have multiple | 
|  | 129 | container with same substring to copy you may encounter unexpected issues. | 
|  | 130 | files_to_extract param - this is array with paths of files/dirs to copy. | 
|  | 131 |  | 
|  | 132 | @pytest.mark.merge_xunit(path='/root/report', | 
|  | 133 | output='/root/conformance_result.xml') | 
|  | 134 | ^^^^^ | 
|  | 135 | This mark will help you to merge xunit results in case if you have | 
|  | 136 | multiple reports because of multiple threads. | 
|  | 137 | path param says where xml results stored | 
|  | 138 | output param says where result will be saved | 
|  | 139 | """ | 
|  | 140 |  | 
|  | 141 | grab_k8s_result = request.keywords.get('grab_k8s_results', None) | 
|  | 142 | extract = request.keywords.get('extract', None) | 
|  | 143 | merge_xunit = request.keywords.get('merge_xunit', None) | 
| Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 144 |  | 
|  | 145 | def test_fin(): | 
|  | 146 | if hasattr(request.node, 'rep_call') and \ | 
|  | 147 | (request.node.rep_call.passed or request.node.rep_call.failed)\ | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 148 | and grab_k8s_result: | 
|  | 149 | files = utils.extract_name_from_mark(grab_k8s_result) \ | 
| Victor Ryzhenkin | 87a3142 | 2018-03-16 22:25:27 +0400 | [diff] [blame] | 150 | or "{}".format(func_name) | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 151 | if extract: | 
|  | 152 | container_system = utils.extract_name_from_mark( | 
|  | 153 | extract, 'container_system') | 
|  | 154 | extract_from = utils.extract_name_from_mark( | 
|  | 155 | extract, 'extract_from') | 
|  | 156 | files_to_extract = utils.extract_name_from_mark( | 
|  | 157 | extract, 'files_to_extract') | 
|  | 158 | for path in files_to_extract: | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 159 | k8s_actions.extract_file_to_node( | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 160 | system=container_system, container=extract_from, | 
|  | 161 | file_path=path) | 
|  | 162 | else: | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 163 | k8s_actions.extract_file_to_node() | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 164 | if merge_xunit: | 
|  | 165 | path = utils.extract_name_from_mark(merge_xunit, 'path') | 
|  | 166 | output = utils.extract_name_from_mark(merge_xunit, 'output') | 
| Dennis Dmitriev | ee5ef23 | 2018-08-31 13:53:18 +0300 | [diff] [blame] | 167 | k8s_actions.combine_xunit(path, output) | 
|  | 168 | k8s_actions.download_k8s_logs(files) | 
| Victor Ryzhenkin | 87a3142 | 2018-03-16 22:25:27 +0400 | [diff] [blame] | 169 |  | 
|  | 170 | request.addfinalizer(test_fin) | 
|  | 171 |  | 
|  | 172 |  | 
|  | 173 | @pytest.fixture(scope='function') | 
| Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 174 | def k8s_cncf_log_helper(request, func_name, underlay, k8s_deployed): | 
| Victor Ryzhenkin | 87a3142 | 2018-03-16 22:25:27 +0400 | [diff] [blame] | 175 | """Finalizer to prepare cncf tar.gz and save results from archive""" | 
|  | 176 |  | 
|  | 177 | cncf_publisher = request.keywords.get('cncf_publisher', None) | 
|  | 178 |  | 
|  | 179 | def test_fin(): | 
|  | 180 | if hasattr(request.node, 'rep_call') and \ | 
|  | 181 | (request.node.rep_call.passed or request.node.rep_call.failed)\ | 
|  | 182 | and cncf_publisher: | 
|  | 183 | files = utils.extract_name_from_mark(cncf_publisher) \ | 
|  | 184 | or "{}".format(func_name) | 
|  | 185 | k8s_deployed.extract_file_to_node( | 
|  | 186 | system='k8s', file_path='tmp/sonobuoy', | 
| Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 187 | pod_name='sonobuoy', pod_namespace='heptio-sonobuoy' | 
| Victor Ryzhenkin | 87a3142 | 2018-03-16 22:25:27 +0400 | [diff] [blame] | 188 | ) | 
|  | 189 | k8s_deployed.manage_cncf_archive() | 
|  | 190 | k8s_deployed.download_k8s_logs(files) | 
|  | 191 |  | 
| Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 192 | request.addfinalizer(test_fin) | 
| Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 193 |  | 
|  | 194 |  | 
|  | 195 | @pytest.fixture(scope='function') | 
|  | 196 | def k8s_chain_update_log_helper(request, config, k8s_deployed): | 
|  | 197 | def test_fin(): | 
|  | 198 | if hasattr(request.node, 'rep_call') and \ | 
|  | 199 | (request.node.rep_call.passed or request.node.rep_call.failed): | 
|  | 200 |  | 
|  | 201 | chain_versions = config.k8s.k8s_update_chain.split(" ") | 
|  | 202 | for version in chain_versions: | 
|  | 203 | container_name = "k8s-conformance:{}".format(version) | 
|  | 204 | tmp_report_dir = "/root/report_{}".format(version) | 
| Vladimir Jigulin | 96bdcb0 | 2018-06-08 08:28:26 +0400 | [diff] [blame] | 205 | report_path = "report_{}.xml".format(version) | 
| Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 206 | conformance_log_path = "k8s_conformance_{}.log".format(version) | 
|  | 207 |  | 
|  | 208 | k8s_deployed.extract_file_to_node( | 
|  | 209 | system='docker', container=container_name, | 
|  | 210 | out_dir=tmp_report_dir, file_path='report' | 
|  | 211 | ) | 
| Vladimir Jigulin | 96bdcb0 | 2018-06-08 08:28:26 +0400 | [diff] [blame] | 212 | k8s_deployed.combine_xunit(tmp_report_dir, | 
|  | 213 | '/root/{}'.format(report_path)) | 
| Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 214 |  | 
|  | 215 | k8s_deployed.download_k8s_logs( | 
|  | 216 | [report_path, conformance_log_path]) | 
|  | 217 |  | 
|  | 218 | request.addfinalizer(test_fin) |