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 | |
Victor Ryzhenkin | 8ff3c3f | 2018-01-17 19:37:05 +0400 | [diff] [blame] | 15 | import os |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 16 | import requests |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 17 | import yaml |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 18 | |
| 19 | from devops.helpers import helpers |
Victor Ryzhenkin | 66d3937 | 2017-09-28 19:25:48 +0400 | [diff] [blame] | 20 | from devops.error import DevopsCalledProcessError |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 21 | |
| 22 | from tcp_tests import logger |
Victor Ryzhenkin | 66d3937 | 2017-09-28 19:25:48 +0400 | [diff] [blame] | 23 | from tcp_tests.helpers import ext |
| 24 | from tcp_tests.helpers.utils import retry |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 25 | from tcp_tests.managers.execute_commands import ExecuteCommandsMixin |
| 26 | from tcp_tests.managers.k8s import cluster |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 27 | |
| 28 | LOG = logger.logger |
| 29 | |
| 30 | |
| 31 | class K8SManager(ExecuteCommandsMixin): |
| 32 | """docstring for K8SManager""" |
| 33 | |
| 34 | __config = None |
| 35 | __underlay = None |
| 36 | |
| 37 | def __init__(self, config, underlay, salt): |
| 38 | self.__config = config |
| 39 | self.__underlay = underlay |
| 40 | self._salt = salt |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 41 | self._api = None |
Vladimir Jigulin | 174aab1 | 2019-01-28 22:17:46 +0400 | [diff] [blame] | 42 | self._controller_name = None |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 43 | self.kubectl = K8SKubectlCli(self) |
| 44 | self.virtlet = K8SVirtlet(self) |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 45 | self.conformance_node = None |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 46 | super(K8SManager, self).__init__(config=config, underlay=underlay) |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 47 | |
| 48 | def install(self, commands): |
| 49 | self.execute_commands(commands, |
| 50 | label='Install Kubernetes services') |
| 51 | self.__config.k8s.k8s_installed = True |
| 52 | self.__config.k8s.kube_host = self.get_proxy_api() |
| 53 | |
| 54 | def get_proxy_api(self): |
| 55 | k8s_proxy_ip_pillars = self._salt.get_pillar( |
vrovachev | 99228d3 | 2017-06-08 19:46:10 +0400 | [diff] [blame] | 56 | tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master', |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 57 | pillar='haproxy:proxy:listen:k8s_secure:binds:address') |
vrovachev | 99228d3 | 2017-06-08 19:46:10 +0400 | [diff] [blame] | 58 | k8s_hosts = self._salt.get_pillar( |
| 59 | tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master', |
| 60 | pillar='kubernetes:pool:apiserver:host') |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 61 | k8s_proxy_ip = set([ip |
| 62 | for item in k8s_proxy_ip_pillars |
Dina Belova | e6fdffb | 2017-09-19 13:58:34 -0700 | [diff] [blame] | 63 | for node, ip in item.items() if ip]) |
vrovachev | 99228d3 | 2017-06-08 19:46:10 +0400 | [diff] [blame] | 64 | k8s_hosts = set([ip |
Dina Belova | e6fdffb | 2017-09-19 13:58:34 -0700 | [diff] [blame] | 65 | for item in k8s_hosts |
| 66 | for node, ip in item.items() if ip]) |
vrovachev | 99228d3 | 2017-06-08 19:46:10 +0400 | [diff] [blame] | 67 | assert len(k8s_hosts) == 1, ( |
| 68 | "Found more than one Kubernetes API hosts in pillars:{0}, " |
| 69 | "expected one!").format(k8s_hosts) |
| 70 | k8s_host = k8s_hosts.pop() |
| 71 | assert k8s_host in k8s_proxy_ip, ( |
| 72 | "Kubernetes API host:{0} not found in proxies:{} " |
| 73 | "on k8s master nodes. K8s proxies are expected on " |
| 74 | "nodes with K8s master").format(k8s_host, k8s_proxy_ip) |
| 75 | return k8s_host |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 76 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 77 | def _api_init(self): |
| 78 | ca_result = self.controller_check_call( |
| 79 | 'base64 --wrap=0 /etc/kubernetes/ssl/ca-kubernetes.crt') |
| 80 | |
| 81 | self._api = cluster.K8sCluster( |
| 82 | user=self.__config.k8s_deploy.kubernetes_admin_user, |
| 83 | password=self.__config.k8s_deploy.kubernetes_admin_password, |
| 84 | ca=ca_result['stdout'][0], |
| 85 | host=self.__config.k8s.kube_host, |
| 86 | port=self.__config.k8s.kube_apiserver_port) |
| 87 | |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 88 | @property |
| 89 | def api(self): |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 90 | """ |
| 91 | :rtype: cluster.K8sCluster |
| 92 | """ |
| 93 | if self._api is None: |
| 94 | self._api_init() |
| 95 | return self._api |
Artem Panchenko | 0594cd7 | 2017-06-12 13:25:26 +0300 | [diff] [blame] | 96 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 97 | def get_controllers(self): |
| 98 | """ Return list of controllers ssh underlays """ |
Vladimir Jigulin | 34dfa94 | 2018-07-23 21:05:48 +0400 | [diff] [blame] | 99 | return [node for node in self.__config.underlay.ssh if |
| 100 | ext.UNDERLAY_NODE_ROLES.k8s_controller in node['roles']] |
| 101 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 102 | def get_masters(self): |
| 103 | """ Return list of kubernetes masters hosts fqdn """ |
| 104 | masters_fqdn = self._salt.get_pillar( |
| 105 | tgt='I@kubernetes:master', pillar='linux:network:fqdn') |
| 106 | return [self.__underlay.host_by_node_name(node_name=v) |
| 107 | for pillar in masters_fqdn for k, v in pillar.items()] |
| 108 | |
Vladimir Jigulin | 174aab1 | 2019-01-28 22:17:46 +0400 | [diff] [blame] | 109 | def renew_controller(self, controller_node_name=None): |
| 110 | """ Changes controller returned by controller_name property """ |
| 111 | if controller_node_name is not None: |
| 112 | self._controller_name = controller_node_name |
| 113 | return self._controller_name |
| 114 | names = [node['node_name'] for node in self.get_controllers()] |
| 115 | if len(names) == 1 and names[0] == self._controller_name: |
| 116 | LOG.warn( |
| 117 | "Cannot change controller because there is only 1 of them") |
| 118 | return |
| 119 | else: |
| 120 | for name in names: |
| 121 | if name != self._controller_name: |
| 122 | self._controller_name = name |
| 123 | return |
| 124 | |
Victor Ryzhenkin | 66d3937 | 2017-09-28 19:25:48 +0400 | [diff] [blame] | 125 | @property |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 126 | def controller_name(self): |
| 127 | """ Return node name of controller node that used for all actions """ |
Vladimir Jigulin | 174aab1 | 2019-01-28 22:17:46 +0400 | [diff] [blame] | 128 | if self._controller_name is None: |
| 129 | self.renew_controller() |
| 130 | return self._controller_name |
Victor Ryzhenkin | 66d3937 | 2017-09-28 19:25:48 +0400 | [diff] [blame] | 131 | |
Dennis Dmitriev | 66650fc | 2018-11-02 11:04:37 +0200 | [diff] [blame] | 132 | @property |
| 133 | def controller_minion_id(self): |
| 134 | """ Return node name of controller node that used for all actions """ |
| 135 | minion_ids = [minion_id['minion_id'] for minion_id in |
| 136 | self.get_controllers()] |
| 137 | # we want to return same controller name every time |
| 138 | minion_ids.sort() |
| 139 | return minion_ids[0] |
| 140 | |
| 141 | @property |
| 142 | def is_metallb_enabled(self): |
| 143 | ctl_tgt = self.controller_minion_id |
| 144 | LOG.debug("Controller target: {}".format(ctl_tgt)) |
| 145 | |
| 146 | result = self._salt.get_pillar( |
| 147 | tgt=ctl_tgt, |
| 148 | pillar='kubernetes:common:addons:metallb:enabled') |
| 149 | metallb = result[0].get(ctl_tgt, False) |
| 150 | LOG.info("{} kubernetes:common:addons:metallb:enabled: {}" |
| 151 | .format(ctl_tgt, bool(metallb))) |
| 152 | return metallb |
| 153 | |
| 154 | @property |
| 155 | def is_ingress_nginx_enabled(self): |
| 156 | ctl_tgt = self.controller_minion_id |
| 157 | LOG.debug("Controller target: {}".format(ctl_tgt)) |
| 158 | |
| 159 | result = self._salt.get_pillar( |
| 160 | tgt=ctl_tgt, |
| 161 | pillar='kubernetes:common:addons:ingress-nginx:enabled') |
| 162 | ingress_nginx = result[0].get(ctl_tgt, False) |
| 163 | LOG.info("{} kubernetes:common:addons:ingress-nginx:enabled: {}" |
| 164 | .format(ctl_tgt, bool(ingress_nginx))) |
| 165 | return ingress_nginx |
| 166 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 167 | def controller_check_call(self, cmd, **kwargs): |
| 168 | """ Run command on controller and return result """ |
| 169 | LOG.info("running cmd on k8s controller: {}".format(cmd)) |
Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 170 | return self.__underlay.check_call( |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 171 | cmd=cmd, node_name=self.controller_name, **kwargs) |
Artem Panchenko | 501e67e | 2017-06-14 14:59:18 +0300 | [diff] [blame] | 172 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 173 | def get_keepalived_vip(self): |
Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 174 | """ |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 175 | Return k8s VIP IP address |
Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 176 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 177 | :return: str, IP address |
Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 178 | """ |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 179 | ctl_vip_pillar = self._salt.get_pillar( |
Vladimir Jigulin | c69f4ba | 2019-01-14 15:27:44 +0400 | [diff] [blame] | 180 | tgt="I@kubernetes:control", |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 181 | pillar="_param:cluster_vip_address")[0] |
| 182 | return ctl_vip_pillar.values()[0] |
Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 183 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 184 | def run_sample_deployment(self, name, **kwargs): |
| 185 | return K8SSampleDeployment(self, name, **kwargs) |
Victor Ryzhenkin | 3ffa2b4 | 2017-10-05 16:38:44 +0400 | [diff] [blame] | 186 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 187 | def get_pod_ips_from_container(self, pod_name, exclude_local=True, |
| 188 | namespace='default'): |
| 189 | """ Get ips from container using 'ip a' |
| 190 | Required for cni-genie multi-cni cases |
| 191 | |
| 192 | :return: list of IP adresses |
Victor Ryzhenkin | 3ffa2b4 | 2017-10-05 16:38:44 +0400 | [diff] [blame] | 193 | """ |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 194 | cmd = "ip a|grep \"inet \"|awk '{{print $2}}'" |
| 195 | result = self.kubectl.cli_exec(namespace, pod_name, cmd)['stdout'] |
| 196 | ips = [line.strip().split('/')[0] for line in result] |
| 197 | if exclude_local: |
| 198 | ips = [ip for ip in ips if not ip.startswith("127.")] |
| 199 | return ips |
Victor Ryzhenkin | 3ffa2b4 | 2017-10-05 16:38:44 +0400 | [diff] [blame] | 200 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 201 | def update_k8s_version(self, tag): |
Vladimir Jigulin | 62bcf46 | 2018-05-28 18:17:01 +0400 | [diff] [blame] | 202 | """ |
| 203 | Update k8s images tag version in cluster meta and apply required |
| 204 | for update states |
| 205 | |
| 206 | :param tag: New version tag of k8s images |
| 207 | :return: |
| 208 | """ |
| 209 | master_host = self.__config.salt.salt_master_host |
| 210 | |
| 211 | def update_image_tag_meta(config, image_name): |
| 212 | image_old = config.get(image_name) |
| 213 | image_base = image_old.split(':')[0] |
| 214 | image_new = "{}:{}".format(image_base, tag) |
| 215 | LOG.info("Changing k8s '{0}' image cluster meta to '{1}'".format( |
| 216 | image_name, image_new)) |
| 217 | |
| 218 | with self.__underlay.remote(host=master_host) as r: |
| 219 | cmd = "salt-call reclass.cluster_meta_set" \ |
| 220 | " name={0} value={1}".format(image_name, image_new) |
| 221 | r.check_call(cmd) |
| 222 | return image_new |
| 223 | |
| 224 | cfg = self.__config |
| 225 | |
| 226 | update_image_tag_meta(cfg.k8s_deploy, "kubernetes_hyperkube_image") |
| 227 | update_image_tag_meta(cfg.k8s_deploy, "kubernetes_pause_image") |
| 228 | cfg.k8s.k8s_conformance_image = update_image_tag_meta( |
| 229 | cfg.k8s, "k8s_conformance_image") |
| 230 | |
| 231 | steps_path = cfg.k8s_deploy.k8s_update_steps_path |
| 232 | update_commands = self.__underlay.read_template(steps_path) |
| 233 | self.execute_commands( |
| 234 | update_commands, label="Updating kubernetes to '{}'".format(tag)) |
Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 235 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 236 | def run_conformance(self, timeout=60*60, log_out='k8s_conformance.log', |
| 237 | raise_on_err=True, node_name=None, |
| 238 | api_server='http://127.0.0.1:8080'): |
| 239 | if node_name is None: |
| 240 | node_name = self.controller_name |
| 241 | cmd = "set -o pipefail; docker run --net=host " \ |
| 242 | "-e API_SERVER='{api}' {image} | tee '{log}'".format( |
| 243 | api=api_server, log=log_out, |
| 244 | image=self.__config.k8s.k8s_conformance_image) |
| 245 | return self.__underlay.check_call( |
| 246 | cmd=cmd, node_name=node_name, timeout=timeout, |
Dennis Dmitriev | eb50ce1 | 2018-09-27 13:34:32 +0300 | [diff] [blame] | 247 | raise_on_err=raise_on_err, verbose=True) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 248 | |
| 249 | def run_virtlet_conformance(self, timeout=60 * 120, |
Dennis Dmitriev | 34fd300 | 2018-11-15 18:25:16 +0200 | [diff] [blame] | 250 | log_file='virtlet_conformance.log', |
| 251 | report_name="report.xml"): |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 252 | if self.__config.k8s.run_extended_virtlet_conformance: |
| 253 | ci_image = "cloud-images.ubuntu.com/xenial/current/" \ |
| 254 | "xenial-server-cloudimg-amd64-disk1.img" |
Dennis Dmitriev | 7cab548 | 2019-05-22 15:40:14 +0300 | [diff] [blame] | 255 | cmd = (r"set -o pipefail; " |
| 256 | r"docker run --net=host {0} /virtlet-e2e-tests " |
| 257 | r"-include-cloud-init-tests -junitOutput {3} " |
| 258 | r"-image {2} -sshuser ubuntu -memoryLimit 1024 " |
| 259 | r"-alsologtostderr -cluster-url http://127.0.0.1:8080 " |
| 260 | r"-ginkgo.focus '\[Conformance\]' " |
| 261 | r"| tee {1}".format( |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 262 | self.__config.k8s_deploy.kubernetes_virtlet_image, |
Dennis Dmitriev | 34fd300 | 2018-11-15 18:25:16 +0200 | [diff] [blame] | 263 | log_file, ci_image, report_name)) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 264 | else: |
Dennis Dmitriev | 7cab548 | 2019-05-22 15:40:14 +0300 | [diff] [blame] | 265 | cmd = (r"set -o pipefail; " |
| 266 | r"docker run --net=host {0} /virtlet-e2e-tests " |
| 267 | r"-junitOutput {2} " |
| 268 | r"-alsologtostderr -cluster-url http://127.0.0.1:8080 " |
| 269 | r"-ginkgo.focus '\[Conformance\]' " |
| 270 | r"| tee {1}".format( |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 271 | self.__config.k8s_deploy.kubernetes_virtlet_image, |
Dennis Dmitriev | 34fd300 | 2018-11-15 18:25:16 +0200 | [diff] [blame] | 272 | log_file, report_name)) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 273 | LOG.info("Executing: {}".format(cmd)) |
| 274 | with self.__underlay.remote( |
| 275 | node_name=self.controller_name) as remote: |
| 276 | result = remote.check_call(cmd, timeout=timeout) |
| 277 | stderr = result['stderr'] |
| 278 | stdout = result['stdout'] |
| 279 | LOG.info("Test results stdout: {}".format(stdout)) |
| 280 | LOG.info("Test results stderr: {}".format(stderr)) |
| 281 | return result |
| 282 | |
Vladimir Jigulin | 5161167 | 2018-11-23 03:10:22 +0400 | [diff] [blame] | 283 | def start_k8s_cncf_verification(self, timeout=60 * 180): |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 284 | """ |
| 285 | Build sonobuoy using golang docker image and install it in system |
| 286 | Then generate sonobuoy verification manifest using gen command |
| 287 | and wait for it to end using status annotation |
| 288 | TODO (vjigulin): All sonobuoy methods can be improved if we define |
| 289 | correct KUBECONFIG env variable |
| 290 | """ |
| 291 | cncf_cmd =\ |
| 292 | 'docker run --network host --name sonobuoy golang:1.11 bash -c ' \ |
| 293 | '"go get -d -v github.com/heptio/sonobuoy && ' \ |
| 294 | 'go install -v github.com/heptio/sonobuoy" && ' \ |
| 295 | 'docker cp sonobuoy:/go/bin/sonobuoy /usr/local/bin/ && ' \ |
| 296 | 'docker rm sonobuoy && ' \ |
| 297 | 'sonobuoy gen | kubectl apply -f -' |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 298 | |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 299 | self.controller_check_call(cncf_cmd, timeout=900) |
| 300 | |
| 301 | sonobuoy_pod = self.api.pods.get('sonobuoy', 'heptio-sonobuoy') |
| 302 | sonobuoy_pod.wait_running() |
| 303 | |
| 304 | def sonobuoy_status(): |
| 305 | annotations = sonobuoy_pod.read().metadata.annotations |
| 306 | json_status = annotations['sonobuoy.hept.io/status'] |
| 307 | status = yaml.safe_load(json_status)['status'] |
| 308 | if status != 'running': |
| 309 | LOG.info("CNCF status: {}".format(json_status)) |
| 310 | return yaml.safe_load(json_status)['status'] |
| 311 | |
| 312 | LOG.info("Waiting for CNCF to complete") |
| 313 | helpers.wait( |
| 314 | lambda: sonobuoy_status() == 'complete', |
Vladimir Jigulin | 5161167 | 2018-11-23 03:10:22 +0400 | [diff] [blame] | 315 | interval=120, timeout=timeout, |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 316 | timeout_msg="Timeout for CNCF reached." |
| 317 | ) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 318 | |
Victor Ryzhenkin | 57c4320 | 2018-12-28 01:48:39 +0400 | [diff] [blame] | 319 | def determine_conformance_node(self, target): |
| 320 | masters_fqdn = self._salt.get_pillar( |
| 321 | tgt='I@kubernetes:master', pillar='linux:network:fqdn') |
| 322 | node_names = [v for pillar in masters_fqdn for |
| 323 | k, v in pillar.items()] |
| 324 | return [node_name for node_name |
| 325 | in node_names |
| 326 | if node_name.startswith(target)][0] |
| 327 | |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 328 | def start_conformance_inside_pod(self, cnf_type='k8s', timeout=60 * 60): |
| 329 | """ |
| 330 | Create conformance pod and wait for results |
| 331 | :param cnf_type: k8s or virtlet. choose what conformance you want |
| 332 | :param timeout: |
| 333 | :return: |
| 334 | """ |
| 335 | if cnf_type == 'k8s': |
| 336 | pod_mark = 'conformance' |
| 337 | elif cnf_type == 'virtlet': |
| 338 | pod_mark = 'virtlet-conformance' |
| 339 | else: |
| 340 | LOG.error("Unknown conformance type or it even not set") |
| 341 | raise RuntimeError("Unknown conformance type") |
| 342 | conformance_cmd = "kubectl apply -f /srv/kubernetes/{}.yml" \ |
| 343 | "".format(pod_mark) |
| 344 | self.controller_check_call(conformance_cmd, timeout=900) |
| 345 | |
| 346 | cnf_pod = self.api.pods.get(pod_mark, pod_mark) |
| 347 | cnf_pod.wait_running() |
| 348 | |
| 349 | pod = cnf_pod.read() |
| 350 | target = "{}.".format(pod.spec.node_name) |
Victor Ryzhenkin | 57c4320 | 2018-12-28 01:48:39 +0400 | [diff] [blame] | 351 | |
| 352 | self.conformance_node = self.determine_conformance_node(target) |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 353 | |
| 354 | def cnf_status(): |
| 355 | pod = cnf_pod.read() |
| 356 | status = pod.status.phase |
| 357 | LOG.info("Conformance status: {}".format(status)) |
| 358 | return status |
| 359 | |
| 360 | LOG.info("Waiting for Conformance to complete") |
| 361 | helpers.wait( |
Aleksei Kasatkin | 84cc1e2 | 2019-01-18 16:55:51 +0100 | [diff] [blame] | 362 | lambda: cnf_status() in ('Succeeded', 'Failed'), |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 363 | interval=120, timeout=timeout, |
| 364 | timeout_msg="Timeout for Conformance reached." |
| 365 | ) |
| 366 | |
| 367 | pod = cnf_pod.read() |
| 368 | status = pod.status.phase |
Aleksei Kasatkin | 75a0803 | 2019-01-21 11:51:35 +0100 | [diff] [blame] | 369 | if status == 'Failed': |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 370 | describe = "kubectl describe po {0} -n {0}".format(pod_mark) |
| 371 | LOG.info(self.controller_check_call(describe, timeout=30)) |
| 372 | raise RuntimeError("Conformance failed") |
| 373 | |
Tatyana Leontovich | 619c336 | 2019-01-04 02:17:26 +0200 | [diff] [blame] | 374 | def get_node_name(self, tgt): |
| 375 | res = [node_name for node_name in |
| 376 | self.__underlay.node_names() if tgt in node_name] |
| 377 | assert len(res) > 0, 'Can not find node name by tgt {}'.format(tgt) |
| 378 | return res[0] |
| 379 | |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 380 | def move_file_to_root_folder(self, filepath): |
Tatyana Leontovich | 619c336 | 2019-01-04 02:17:26 +0200 | [diff] [blame] | 381 | cmd = "mv {0} /root/".format(filepath) |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 382 | if self.conformance_node: |
Tatyana Leontovich | 619c336 | 2019-01-04 02:17:26 +0200 | [diff] [blame] | 383 | short_name = self.conformance_node.split('.')[0] |
Victor Ryzhenkin | 9504688 | 2018-12-29 19:18:40 +0400 | [diff] [blame] | 384 | LOG.info("Managing results on {}".format(self.conformance_node)) |
Tatyana Leontovich | 619c336 | 2019-01-04 02:17:26 +0200 | [diff] [blame] | 385 | step = {'cmd': cmd, 'node_name': self.get_node_name( |
| 386 | tgt=short_name)} |
| 387 | LOG.info('Move {0} to /root/ on {1}'.format( |
| 388 | filepath, self.conformance_node)) |
| 389 | self.execute_command(step, 'Move files') |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 390 | else: |
| 391 | LOG.info("Node is not properly set") |
| 392 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 393 | def extract_file_to_node(self, system='docker', |
| 394 | container='virtlet', |
| 395 | file_path='report.xml', |
| 396 | out_dir='.', |
| 397 | **kwargs): |
Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 398 | """ |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 399 | Download file from docker or k8s container to node |
Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 400 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 401 | :param system: docker or k8s |
| 402 | :param container: Full name of part of name |
| 403 | :param file_path: File path in container |
| 404 | :param kwargs: Used to control pod and namespace |
| 405 | :param out_dir: Output directory |
| 406 | :return: |
Vladimir Jigulin | ee1faa5 | 2018-06-25 13:00:51 +0400 | [diff] [blame] | 407 | """ |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 408 | with self.__underlay.remote(node_name=self.controller_name) as remote: |
Aleksei Kasatkin | 75a0803 | 2019-01-21 11:51:35 +0100 | [diff] [blame] | 409 | if system == 'docker': |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 410 | cmd = ("docker ps --all | grep \"{0}\" |" |
| 411 | " awk '{{print $1}}'".format(container)) |
| 412 | result = remote.check_call(cmd, raise_on_err=False) |
| 413 | if result['stdout']: |
| 414 | container_id = result['stdout'][0].strip() |
| 415 | else: |
| 416 | LOG.info('No container found, skipping extraction...') |
| 417 | return |
| 418 | cmd = "docker start {}".format(container_id) |
| 419 | remote.check_call(cmd, raise_on_err=False) |
| 420 | cmd = "docker cp \"{0}:/{1}\" \"{2}\"".format( |
| 421 | container_id, file_path, out_dir) |
| 422 | remote.check_call(cmd, raise_on_err=False) |
| 423 | else: |
| 424 | # system is k8s |
| 425 | pod_name = kwargs.get('pod_name') |
| 426 | pod_namespace = kwargs.get('pod_namespace') |
| 427 | cmd = 'kubectl cp \"{0}/{1}:/{2}\" \"{3}\"'.format( |
| 428 | pod_namespace, pod_name, file_path, out_dir) |
| 429 | remote.check_call(cmd, raise_on_err=False) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 430 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 431 | def download_k8s_logs(self, files): |
| 432 | """ |
| 433 | Download JUnit report and conformance logs from cluster |
| 434 | :param files: |
| 435 | :return: |
| 436 | """ |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 437 | if self.conformance_node: |
| 438 | node = self.conformance_node |
| 439 | else: |
| 440 | node = self.controller_name |
| 441 | LOG.info("Trying to get logs at {}".format(node)) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 442 | master_host = self.__config.salt.salt_master_host |
| 443 | with self.__underlay.remote(host=master_host) as r: |
| 444 | for log_file in files: |
Tatyana Leontovich | 619c336 | 2019-01-04 02:17:26 +0200 | [diff] [blame] | 445 | cmd = "scp -r \"{0}:/root/{1}\" /root/".format( |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 446 | node, log_file) |
Tatyana Leontovich | 48a33c1 | 2019-01-03 02:19:25 +0200 | [diff] [blame] | 447 | r.check_call(cmd, raise_on_err=True) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 448 | LOG.info("Downloading the artifact {0}".format(log_file)) |
| 449 | r.download(destination=log_file, target=os.getcwd()) |
Dennis Dmitriev | 445b432 | 2018-10-08 14:32:07 +0300 | [diff] [blame] | 450 | self.store_server_version(os.path.join(os.getcwd(), 'env_k8s_version')) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 451 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 452 | def combine_xunit(self, path, output): |
| 453 | """ |
| 454 | Function to combine multiple xmls with test results to |
| 455 | one. |
Vladimir Jigulin | 34dfa94 | 2018-07-23 21:05:48 +0400 | [diff] [blame] | 456 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 457 | :param path: Path where xmls to combine located |
| 458 | :param output: Path to xml file where output will stored |
| 459 | :return: |
| 460 | """ |
Victor Ryzhenkin | e784bbf | 2018-12-21 03:58:00 +0400 | [diff] [blame] | 461 | if self.conformance_node: |
| 462 | node = self.conformance_node |
| 463 | else: |
| 464 | node = self.controller_name |
| 465 | LOG.info("Trying to combine xunit at {}".format(node)) |
Victor Ryzhenkin | 9504688 | 2018-12-29 19:18:40 +0400 | [diff] [blame] | 466 | cmd = ("apt-get install python-setuptools -y; " |
| 467 | "pip install " |
| 468 | "https://github.com/mogaika/xunitmerge/archive/master.zip") |
| 469 | LOG.debug('Installing xunitmerge') |
| 470 | self._salt.cmd_run(tgt=node, cmd=cmd) |
| 471 | LOG.debug('Merging xunit') |
| 472 | cmd = ("cd {0}; arg=''; " |
| 473 | "for i in $(ls | grep xml); " |
| 474 | "do arg=\"$arg $i\"; done && " |
| 475 | "xunitmerge $arg {1} || true".format(path, output)) |
| 476 | self._salt.cmd_run(tgt=node, cmd=cmd) |
Vladimir Jigulin | 34dfa94 | 2018-07-23 21:05:48 +0400 | [diff] [blame] | 477 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 478 | def manage_cncf_archive(self): |
| 479 | """ |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 480 | Function to untar archive, move files that we are needs to the |
| 481 | home folder, prepare it to downloading. |
| 482 | Will generate files on controller node: |
| 483 | e2e.log, junit_01.xml, cncf_results.tar.gz, version.txt |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 484 | :return: |
| 485 | """ |
| 486 | |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 487 | cmd =\ |
| 488 | "rm -rf cncf_results.tar.gz result && " \ |
| 489 | "mkdir result && " \ |
| 490 | "mv *_sonobuoy_*.tar.gz cncf_results.tar.gz && " \ |
| 491 | "tar -C result -xzf cncf_results.tar.gz && " \ |
| 492 | "mv result/plugins/e2e/results/e2e.log . ; " \ |
| 493 | "mv result/plugins/e2e/results/junit_01.xml . ; " \ |
| 494 | "kubectl version > version.txt" |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 495 | |
Vladimir Jigulin | 0c8dd5a | 2018-08-28 05:08:35 +0400 | [diff] [blame] | 496 | self.controller_check_call(cmd, raise_on_err=False) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 497 | |
Vladimir Jigulin | 174aab1 | 2019-01-28 22:17:46 +0400 | [diff] [blame] | 498 | @retry(100, exception=DevopsCalledProcessError, interval=20) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 499 | def nslookup(self, host, src): |
| 500 | """ Run nslookup on controller and return result """ |
| 501 | return self.controller_check_call("nslookup {0} {1}".format(host, src)) |
| 502 | |
Vladimir Jigulin | 174aab1 | 2019-01-28 22:17:46 +0400 | [diff] [blame] | 503 | @retry(100, exception=DevopsCalledProcessError, interval=20) |
Vladimir Jigulin | 57ecae9 | 2018-09-10 22:51:15 +0400 | [diff] [blame] | 504 | def curl(self, url, *args): |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 505 | """ |
| 506 | Run curl on controller and return stdout |
| 507 | |
| 508 | :param url: url to curl |
Vladimir Jigulin | 57ecae9 | 2018-09-10 22:51:15 +0400 | [diff] [blame] | 509 | :return: list of strings (with /n at end of every line) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 510 | """ |
Vladimir Jigulin | 57ecae9 | 2018-09-10 22:51:15 +0400 | [diff] [blame] | 511 | args = list(args) |
| 512 | args.append(url) |
| 513 | cmd = "curl -s -S {}".format( |
| 514 | " ".join(["'{}'".format(a.replace("'", "\\'")) for a in args])) |
| 515 | result = self.controller_check_call(cmd) |
| 516 | LOG.debug("{0}\nresult:\n{1}".format(cmd, result['stdout'])) |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 517 | return result['stdout'] |
Vladimir Jigulin | 34dfa94 | 2018-07-23 21:05:48 +0400 | [diff] [blame] | 518 | |
Dennis Dmitriev | 445b432 | 2018-10-08 14:32:07 +0300 | [diff] [blame] | 519 | def store_server_version(self, env_file_path): |
| 520 | """Store Kubernetes server version in bash source file""" |
| 521 | |
| 522 | def digits(string): |
| 523 | return ''.join(n for n in string if n.isdigit()) |
| 524 | |
| 525 | ver = self.api.api_version.get_code() |
| 526 | LOG.debug("Got Kubernetes server version:\n{0}".format(ver)) |
| 527 | |
| 528 | env_version = ("export KUBE_SERVER_VERSION={0}.{1}\n" |
| 529 | "export KUBE_SERVER_GIT_VERSION={2}\n" |
| 530 | .format(digits(ver.major), |
| 531 | digits(ver.minor), |
| 532 | ver.git_version)) |
| 533 | |
| 534 | LOG.info("Kubernetes server version is stored to {0}:\n{1}" |
| 535 | .format(env_file_path, env_version)) |
| 536 | with open(env_file_path, 'w') as kver: |
| 537 | kver.write(env_version) |
| 538 | |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 539 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 540 | class K8SKubectlCli(object): |
| 541 | """ Contain kubectl cli commands and api wrappers""" |
| 542 | def __init__(self, manager): |
| 543 | self._manager = manager |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 544 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 545 | def cli_run(self, namespace, name, image, port, replicas=1): |
| 546 | cmd = "kubectl -n {0} run {1} --image={2} --port={3} --replicas={4}".\ |
| 547 | format(namespace, name, image, port, replicas) |
| 548 | return self._manager.controller_check_call(cmd) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 549 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 550 | def run(self, namespace, name, image, port, replicas=1): |
| 551 | self.cli_run(namespace, name, image, port, replicas) |
| 552 | return self._manager.api.deployments.get( |
| 553 | namespace=namespace, name=name) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 554 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 555 | def cli_expose(self, namespace, resource_type, resource_name, |
| 556 | service_name=None, port='', service_type='ClusterIP'): |
| 557 | cmd = "kubectl -n {0} expose {1} {2} --port={3} --type={4}".format( |
| 558 | namespace, resource_type, resource_name, port, service_type) |
| 559 | if service_name is not None: |
| 560 | cmd += " --name={}".format(service_name) |
| 561 | return self._manager.controller_check_call(cmd) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 562 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 563 | def expose(self, resource, service_name=None, |
| 564 | port='', service_type='ClusterIP'): |
| 565 | self.cli_expose(resource.namespace, resource.resource_type, |
| 566 | resource.name, service_name=service_name, |
| 567 | port=port, service_type=service_type) |
| 568 | return self._manager.api.services.get( |
| 569 | namespace=resource.namespace, name=service_name or resource.name) |
| 570 | |
| 571 | def cli_exec(self, namespace, pod_name, cmd, container=''): |
| 572 | kubectl_cmd = "kubectl -n {0} exec --container={1} {2} -- {3}".format( |
| 573 | namespace, container, pod_name, cmd) |
| 574 | return self._manager.controller_check_call(kubectl_cmd) |
| 575 | |
| 576 | # def exec(...), except exec is statement in python |
| 577 | def execute(self, pod, cmd, container=''): |
| 578 | return self.cli_exec(pod.namespace, pod.name, cmd, container=container) |
| 579 | |
| 580 | def cli_annotate(self, namespace, resource_type, resource_name, |
| 581 | annotations, overwrite=False): |
| 582 | cmd = "kubectl -n {0} annotate {1} {2} {3}".format( |
| 583 | namespace, resource_type, resource_name, annotations) |
| 584 | if overwrite: |
| 585 | cmd += " --overwrite" |
| 586 | return self._manager.controller_check_call(cmd) |
| 587 | |
| 588 | def annotate(self, resource, annotations, overwrite=False): |
| 589 | return self.cli_annotate(resource.namespace, resource.resource_type, |
| 590 | resource.name, annotations, |
| 591 | overwrite=overwrite) |
| 592 | |
| 593 | |
| 594 | class K8SVirtlet(object): |
| 595 | """ Contain virtlet-related methods""" |
| 596 | def __init__(self, manager, namespace='kube-system'): |
| 597 | self._manager = manager |
| 598 | self._namespace = namespace |
| 599 | |
| 600 | def get_virtlet_node_pod(self, node_name): |
| 601 | for pod in self._manager.api.pods.list( |
| 602 | namespace=self._namespace, name_prefix='virtlet-'): |
| 603 | if pod.read().spec.node_name == node_name: |
| 604 | return pod |
| 605 | return None |
| 606 | |
| 607 | def get_pod_dom_uuid(self, pod): |
| 608 | uuid_name_map = self.virtlet_execute( |
| 609 | pod.read().spec.node_name, 'virsh list --uuid --name')['stdout'] |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 610 | for line in uuid_name_map: |
| 611 | if line.rstrip().endswith("-{}".format(pod.name)): |
| 612 | return line.split(" ")[0] |
| 613 | raise Exception("Cannot detect uuid for pod {}".format(pod.name)) |
| 614 | |
| 615 | def virsh_domstats(self, pod): |
| 616 | """ get dict of vm stats """ |
| 617 | uuid = self.get_pod_dom_uuid(pod) |
| 618 | result = self.virtlet_execute( |
| 619 | pod.read().spec.node_name, 'virsh domstats {}'.format(uuid)) |
| 620 | stats = dict() |
| 621 | for line in result['stdout']: |
| 622 | if '=' in line: |
| 623 | vals = line.strip().split('=') |
| 624 | stats[vals[0]] = vals[1] |
| 625 | return stats |
| 626 | |
| 627 | def virtlet_execute(self, node_name, cmd, container='libvirt'): |
| 628 | """ execute command inside virtlet container """ |
| 629 | pod = self.get_virtlet_node_pod(node_name) |
| 630 | return self._manager.kubectl.execute(pod, cmd, container) |
| 631 | |
| 632 | |
| 633 | class K8SSampleDeployment(object): |
| 634 | """ Wrapper for deployment run=>expose=>check frequent combination """ |
| 635 | def __init__(self, manager, name, |
| 636 | namespace=None, |
| 637 | image='gcr.io/google-samples/node-hello:1.0', |
| 638 | port=8080, |
| 639 | replicas=2): |
| 640 | namespace = namespace or manager.api.default_namespace |
| 641 | |
| 642 | self._manager = manager |
| 643 | self._port = port |
| 644 | self._deployment = \ |
| 645 | manager.kubectl.run(namespace, name, |
| 646 | image=image, port=port, replicas=replicas) |
| 647 | self._index = 1 # used to generate svc name |
| 648 | self._svc = None # hold last created svc |
| 649 | |
| 650 | def wait_ready(self, timeout=300, interval=5): |
| 651 | self._deployment.wait_ready(timeout=timeout, interval=interval) |
| 652 | return self |
| 653 | |
| 654 | def svc(self): |
| 655 | """ Return the last exposed service""" |
| 656 | return self._svc |
| 657 | |
| 658 | def expose(self, service_type='ClusterIP'): |
| 659 | service_name = "{0}-s{1}".format(self._deployment.name, self._index) |
Vladimir Jigulin | 9068915 | 2018-09-26 15:38:19 +0400 | [diff] [blame] | 660 | self._index += 1 |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 661 | self._svc = self._manager.kubectl.expose( |
| 662 | self._deployment, port=self._port, |
| 663 | service_name=service_name, service_type=service_type) |
| 664 | return self._svc |
| 665 | |
| 666 | def curl(self, svc=None, external=False): |
| 667 | if svc is None: |
| 668 | svc = self.svc() |
| 669 | url = "http://{0}:{1}".format(svc.get_ip(external), self._port) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 670 | if external: |
| 671 | return requests.get(url).text |
| 672 | else: |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 673 | return self._manager.curl(url) |
Vladimir Jigulin | a6b018b | 2018-07-18 15:19:01 +0400 | [diff] [blame] | 674 | |
Vladimir Jigulin | 4ad52a8 | 2018-08-12 05:51:30 +0400 | [diff] [blame] | 675 | def is_service_available(self, svc=None, external=False): |
| 676 | return "Hello Kubernetes!" in self.curl(svc, external=external) |
Vladimir Jigulin | 9068915 | 2018-09-26 15:38:19 +0400 | [diff] [blame] | 677 | |
| 678 | def delete(self): |
| 679 | for svc in self._manager.api.services.list_all( |
| 680 | name_prefix="{}-s".format(self._deployment.name)): |
| 681 | svc.delete() |
| 682 | self._deployment.delete() |