| 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') | 
 | 27 | def k8s_actions(config, underlay, salt_deployed): | 
 | 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 |     """ | 
 | 37 |     return k8smanager.K8SManager(config, underlay, salt_deployed) | 
 | 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') | 
| Victor Ryzhenkin | cf26c93 | 2018-03-29 20:08:21 +0400 | [diff] [blame] | 110 | def k8s_logs(request, func_name, underlay, k8s_deployed): | 
 | 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: | 
 | 159 |                     k8s_deployed.extract_file_to_node( | 
 | 160 |                         system=container_system, container=extract_from, | 
 | 161 |                         file_path=path) | 
 | 162 |             else: | 
 | 163 |                 k8s_deployed.extract_file_to_node() | 
 | 164 |             if merge_xunit: | 
 | 165 |                 path = utils.extract_name_from_mark(merge_xunit, 'path') | 
 | 166 |                 output = utils.extract_name_from_mark(merge_xunit, 'output') | 
 | 167 |                 k8s_deployed.combine_xunit(path, output) | 
| Victor Ryzhenkin | 87a3142 | 2018-03-16 22:25:27 +0400 | [diff] [blame] | 168 |             k8s_deployed.download_k8s_logs(files) | 
 | 169 |  | 
 | 170 |     request.addfinalizer(test_fin) | 
 | 171 |  | 
 | 172 |  | 
 | 173 | @pytest.fixture(scope='function') | 
 | 174 | def cncf_log_helper(request, func_name, underlay, k8s_deployed): | 
 | 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', | 
 | 187 |                 pod_name='sonobuoy', pod_namespace='sonobuoy' | 
 | 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) |