blob: c260926519e84140faa0cfbd9c9340ce6c6a4a7f [file] [log] [blame]
Artem Panchenko0594cd72017-06-12 13:25:26 +03001# 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 Ryzhenkin8ff3c3f2018-01-17 19:37:05 +040015import os
Vladimir Jigulina6b018b2018-07-18 15:19:01 +040016import requests
Artem Panchenko0594cd72017-06-12 13:25:26 +030017
18from devops.helpers import helpers
Victor Ryzhenkin66d39372017-09-28 19:25:48 +040019from devops.error import DevopsCalledProcessError
Artem Panchenko0594cd72017-06-12 13:25:26 +030020
21from tcp_tests import logger
Victor Ryzhenkin66d39372017-09-28 19:25:48 +040022from tcp_tests.helpers import ext
23from tcp_tests.helpers.utils import retry
Artem Panchenko0594cd72017-06-12 13:25:26 +030024from tcp_tests.managers.execute_commands import ExecuteCommandsMixin
25from tcp_tests.managers.k8s import cluster
Artem Panchenko0594cd72017-06-12 13:25:26 +030026
27LOG = logger.logger
28
29
30class K8SManager(ExecuteCommandsMixin):
31 """docstring for K8SManager"""
32
33 __config = None
34 __underlay = None
35
36 def __init__(self, config, underlay, salt):
37 self.__config = config
38 self.__underlay = underlay
39 self._salt = salt
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040040 self._api = None
41 self.kubectl = K8SKubectlCli(self)
42 self.virtlet = K8SVirtlet(self)
43 super(K8SManager, self).__init__(config=config, underlay=underlay)
Artem Panchenko0594cd72017-06-12 13:25:26 +030044
45 def install(self, commands):
46 self.execute_commands(commands,
47 label='Install Kubernetes services')
48 self.__config.k8s.k8s_installed = True
49 self.__config.k8s.kube_host = self.get_proxy_api()
50
51 def get_proxy_api(self):
52 k8s_proxy_ip_pillars = self._salt.get_pillar(
vrovachev99228d32017-06-08 19:46:10 +040053 tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master',
Artem Panchenko0594cd72017-06-12 13:25:26 +030054 pillar='haproxy:proxy:listen:k8s_secure:binds:address')
vrovachev99228d32017-06-08 19:46:10 +040055 k8s_hosts = self._salt.get_pillar(
56 tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master',
57 pillar='kubernetes:pool:apiserver:host')
Artem Panchenko0594cd72017-06-12 13:25:26 +030058 k8s_proxy_ip = set([ip
59 for item in k8s_proxy_ip_pillars
Dina Belovae6fdffb2017-09-19 13:58:34 -070060 for node, ip in item.items() if ip])
vrovachev99228d32017-06-08 19:46:10 +040061 k8s_hosts = set([ip
Dina Belovae6fdffb2017-09-19 13:58:34 -070062 for item in k8s_hosts
63 for node, ip in item.items() if ip])
vrovachev99228d32017-06-08 19:46:10 +040064 assert len(k8s_hosts) == 1, (
65 "Found more than one Kubernetes API hosts in pillars:{0}, "
66 "expected one!").format(k8s_hosts)
67 k8s_host = k8s_hosts.pop()
68 assert k8s_host in k8s_proxy_ip, (
69 "Kubernetes API host:{0} not found in proxies:{} "
70 "on k8s master nodes. K8s proxies are expected on "
71 "nodes with K8s master").format(k8s_host, k8s_proxy_ip)
72 return k8s_host
Artem Panchenko0594cd72017-06-12 13:25:26 +030073
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040074 def _api_init(self):
75 ca_result = self.controller_check_call(
76 'base64 --wrap=0 /etc/kubernetes/ssl/ca-kubernetes.crt')
77
78 self._api = cluster.K8sCluster(
79 user=self.__config.k8s_deploy.kubernetes_admin_user,
80 password=self.__config.k8s_deploy.kubernetes_admin_password,
81 ca=ca_result['stdout'][0],
82 host=self.__config.k8s.kube_host,
83 port=self.__config.k8s.kube_apiserver_port)
84
Artem Panchenko0594cd72017-06-12 13:25:26 +030085 @property
86 def api(self):
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040087 """
88 :rtype: cluster.K8sCluster
89 """
90 if self._api is None:
91 self._api_init()
92 return self._api
Artem Panchenko0594cd72017-06-12 13:25:26 +030093
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040094 def get_controllers(self):
95 """ Return list of controllers ssh underlays """
Vladimir Jigulin34dfa942018-07-23 21:05:48 +040096 return [node for node in self.__config.underlay.ssh if
97 ext.UNDERLAY_NODE_ROLES.k8s_controller in node['roles']]
98
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040099 def get_masters(self):
100 """ Return list of kubernetes masters hosts fqdn """
101 masters_fqdn = self._salt.get_pillar(
102 tgt='I@kubernetes:master', pillar='linux:network:fqdn')
103 return [self.__underlay.host_by_node_name(node_name=v)
104 for pillar in masters_fqdn for k, v in pillar.items()]
105
Victor Ryzhenkin66d39372017-09-28 19:25:48 +0400106 @property
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400107 def controller_name(self):
108 """ Return node name of controller node that used for all actions """
109 names = [node['node_name'] for node in self.get_controllers()]
110 # we want to return same controller name every time
111 names.sort()
112 return names[0]
Victor Ryzhenkin66d39372017-09-28 19:25:48 +0400113
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400114 def controller_check_call(self, cmd, **kwargs):
115 """ Run command on controller and return result """
116 LOG.info("running cmd on k8s controller: {}".format(cmd))
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400117 return self.__underlay.check_call(
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400118 cmd=cmd, node_name=self.controller_name, **kwargs)
Artem Panchenko501e67e2017-06-14 14:59:18 +0300119
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400120 def get_keepalived_vip(self):
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400121 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400122 Return k8s VIP IP address
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400123
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400124 :return: str, IP address
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400125 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400126 ctl_vip_pillar = self._salt.get_pillar(
127 tgt="I@kubernetes:control:enabled:True",
128 pillar="_param:cluster_vip_address")[0]
129 return ctl_vip_pillar.values()[0]
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400130
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400131 def run_sample_deployment(self, name, **kwargs):
132 return K8SSampleDeployment(self, name, **kwargs)
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400133
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400134 def get_pod_ips_from_container(self, pod_name, exclude_local=True,
135 namespace='default'):
136 """ Get ips from container using 'ip a'
137 Required for cni-genie multi-cni cases
138
139 :return: list of IP adresses
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400140 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400141 cmd = "ip a|grep \"inet \"|awk '{{print $2}}'"
142 result = self.kubectl.cli_exec(namespace, pod_name, cmd)['stdout']
143 ips = [line.strip().split('/')[0] for line in result]
144 if exclude_local:
145 ips = [ip for ip in ips if not ip.startswith("127.")]
146 return ips
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400147
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400148 def update_k8s_version(self, tag):
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400149 """
150 Update k8s images tag version in cluster meta and apply required
151 for update states
152
153 :param tag: New version tag of k8s images
154 :return:
155 """
156 master_host = self.__config.salt.salt_master_host
157
158 def update_image_tag_meta(config, image_name):
159 image_old = config.get(image_name)
160 image_base = image_old.split(':')[0]
161 image_new = "{}:{}".format(image_base, tag)
162 LOG.info("Changing k8s '{0}' image cluster meta to '{1}'".format(
163 image_name, image_new))
164
165 with self.__underlay.remote(host=master_host) as r:
166 cmd = "salt-call reclass.cluster_meta_set" \
167 " name={0} value={1}".format(image_name, image_new)
168 r.check_call(cmd)
169 return image_new
170
171 cfg = self.__config
172
173 update_image_tag_meta(cfg.k8s_deploy, "kubernetes_hyperkube_image")
174 update_image_tag_meta(cfg.k8s_deploy, "kubernetes_pause_image")
175 cfg.k8s.k8s_conformance_image = update_image_tag_meta(
176 cfg.k8s, "k8s_conformance_image")
177
178 steps_path = cfg.k8s_deploy.k8s_update_steps_path
179 update_commands = self.__underlay.read_template(steps_path)
180 self.execute_commands(
181 update_commands, label="Updating kubernetes to '{}'".format(tag))
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400182
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400183 def run_conformance(self, timeout=60*60, log_out='k8s_conformance.log',
184 raise_on_err=True, node_name=None,
185 api_server='http://127.0.0.1:8080'):
186 if node_name is None:
187 node_name = self.controller_name
188 cmd = "set -o pipefail; docker run --net=host " \
189 "-e API_SERVER='{api}' {image} | tee '{log}'".format(
190 api=api_server, log=log_out,
191 image=self.__config.k8s.k8s_conformance_image)
192 return self.__underlay.check_call(
193 cmd=cmd, node_name=node_name, timeout=timeout,
194 raise_on_err=raise_on_err)
195
196 def run_virtlet_conformance(self, timeout=60 * 120,
197 log_file='virtlet_conformance.log'):
198 if self.__config.k8s.run_extended_virtlet_conformance:
199 ci_image = "cloud-images.ubuntu.com/xenial/current/" \
200 "xenial-server-cloudimg-amd64-disk1.img"
201 cmd = ("set -o pipefail; "
202 "docker run --net=host {0} /virtlet-e2e-tests "
203 "-include-cloud-init-tests -junitOutput report.xml "
204 "-image {2} -sshuser ubuntu -memoryLimit 1024 "
205 "-alsologtostderr -cluster-url http://127.0.0.1:8080 "
206 "-ginkgo.focus '\[Conformance\]' "
207 "| tee {1}".format(
208 self.__config.k8s_deploy.kubernetes_virtlet_image,
209 log_file, ci_image))
210 else:
211 cmd = ("set -o pipefail; "
212 "docker run --net=host {0} /virtlet-e2e-tests "
213 "-junitOutput report.xml "
214 "-alsologtostderr -cluster-url http://127.0.0.1:8080 "
215 "-ginkgo.focus '\[Conformance\]' "
216 "| tee {1}".format(
217 self.__config.k8s_deploy.kubernetes_virtlet_image,
218 log_file))
219 LOG.info("Executing: {}".format(cmd))
220 with self.__underlay.remote(
221 node_name=self.controller_name) as remote:
222 result = remote.check_call(cmd, timeout=timeout)
223 stderr = result['stderr']
224 stdout = result['stdout']
225 LOG.info("Test results stdout: {}".format(stdout))
226 LOG.info("Test results stderr: {}".format(stderr))
227 return result
228
229 def start_k8s_cncf_verification(self, timeout=60 * 90):
230 cncf_cmd = ("curl -L https://raw.githubusercontent.com/cncf/"
231 "k8s-conformance/master/sonobuoy-conformance.yaml"
232 " | kubectl apply -f -")
233 with self.__underlay.remote(
234 node_name=self.controller_name) as remote:
235 remote.check_call(cncf_cmd, timeout=60)
236 self.wait_pod_phase('sonobuoy', 'Running',
237 namespace='sonobuoy', timeout=120)
238 wait_cmd = ('kubectl logs -n sonobuoy sonobuoy | '
239 'grep "sonobuoy is now blocking"')
240
241 expected = [0, 1]
242 helpers.wait(
243 lambda: remote.check_call(
244 wait_cmd, expected=expected).exit_code == 0,
245 interval=30, timeout=timeout,
246 timeout_msg="Timeout for CNCF reached."
247 )
248
249 def extract_file_to_node(self, system='docker',
250 container='virtlet',
251 file_path='report.xml',
252 out_dir='.',
253 **kwargs):
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400254 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400255 Download file from docker or k8s container to node
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400256
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400257 :param system: docker or k8s
258 :param container: Full name of part of name
259 :param file_path: File path in container
260 :param kwargs: Used to control pod and namespace
261 :param out_dir: Output directory
262 :return:
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400263 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400264 with self.__underlay.remote(
265 node_name=self.controller_name) as remote:
266 if system is 'docker':
267 cmd = ("docker ps --all | grep \"{0}\" |"
268 " awk '{{print $1}}'".format(container))
269 result = remote.check_call(cmd, raise_on_err=False)
270 if result['stdout']:
271 container_id = result['stdout'][0].strip()
272 else:
273 LOG.info('No container found, skipping extraction...')
274 return
275 cmd = "docker start {}".format(container_id)
276 remote.check_call(cmd, raise_on_err=False)
277 cmd = "docker cp \"{0}:/{1}\" \"{2}\"".format(
278 container_id, file_path, out_dir)
279 remote.check_call(cmd, raise_on_err=False)
280 else:
281 # system is k8s
282 pod_name = kwargs.get('pod_name')
283 pod_namespace = kwargs.get('pod_namespace')
284 cmd = 'kubectl cp \"{0}/{1}:/{2}\" \"{3}\"'.format(
285 pod_namespace, pod_name, file_path, out_dir)
286 remote.check_call(cmd, raise_on_err=False)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400287
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400288 def download_k8s_logs(self, files):
289 """
290 Download JUnit report and conformance logs from cluster
291 :param files:
292 :return:
293 """
294 master_host = self.__config.salt.salt_master_host
295 with self.__underlay.remote(host=master_host) as r:
296 for log_file in files:
297 cmd = "rsync -r \"{0}:/root/{1}\" /root/".format(
298 self.controller_name, log_file)
299 r.check_call(cmd, raise_on_err=False)
300 LOG.info("Downloading the artifact {0}".format(log_file))
301 r.download(destination=log_file, target=os.getcwd())
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400302
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400303 def combine_xunit(self, path, output):
304 """
305 Function to combine multiple xmls with test results to
306 one.
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400307
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400308 :param path: Path where xmls to combine located
309 :param output: Path to xml file where output will stored
310 :return:
311 """
312 with self.__underlay.remote(node_name=self.controller_name) as r:
313 cmd = ("apt-get install python-setuptools -y; "
314 "pip install "
315 "https://github.com/mogaika/xunitmerge/archive/master.zip")
316 LOG.debug('Installing xunitmerge')
317 r.check_call(cmd, raise_on_err=False)
318 LOG.debug('Merging xunit')
319 cmd = ("cd {0}; arg = ''; "
320 "for i in $(ls | grep xml); "
321 "do arg=\"$arg $i\"; done && "
322 "xunitmerge $arg {1}".format(path, output))
323 r.check_call(cmd, raise_on_err=False)
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400324
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400325 def manage_cncf_archive(self):
326 """
327 Function to untar archive, move files, that we are needs to the
328 home folder, prepare it to downloading and clean the trash.
329 Will generate files: e2e.log, junit_01.xml, cncf_results.tar.gz
330 and version.txt
331 :return:
332 """
333
334 # Namespace and pod name may be hardcoded since this function is
335 # very specific for cncf and cncf is not going to change
336 # those launch pod name and namespace.
337 get_tar_name_cmd = ("kubectl logs -n sonobuoy sonobuoy | "
338 "grep 'Results available' | "
339 "sed 's/.*\///' | tr -d '\"'")
340
341 with self.__underlay.remote(
342 node_name=self.controller_name) as remote:
343 tar_name = remote.check_call(get_tar_name_cmd)['stdout'][0].strip()
344 untar = "mkdir result && tar -C result -xzf {0}".format(tar_name)
345 remote.check_call(untar, raise_on_err=False)
346 manage_results = ("mv result/plugins/e2e/results/e2e.log . && "
347 "mv result/plugins/e2e/results/junit_01.xml . ;"
348 "kubectl version > version.txt")
349 remote.check_call(manage_results, raise_on_err=False)
350 cleanup_host = "rm -rf result"
351 remote.check_call(cleanup_host, raise_on_err=False)
352 # This one needed to use download fixture, since I don't know
353 # how possible apply fixture arg dynamically from test.
354 rename_tar = "mv {0} cncf_results.tar.gz".format(tar_name)
355 remote.check_call(rename_tar, raise_on_err=False)
356
357 @retry(300, exception=DevopsCalledProcessError)
358 def nslookup(self, host, src):
359 """ Run nslookup on controller and return result """
360 return self.controller_check_call("nslookup {0} {1}".format(host, src))
361
362 @retry(300, exception=DevopsCalledProcessError)
363 def curl(self, url):
364 """
365 Run curl on controller and return stdout
366
367 :param url: url to curl
368 :return: response string
369 """
370 result = self.controller_check_call("curl -s -S \"{}\"".format(url))
371 LOG.debug("curl \"{0}\" result: {1}".format(url, result['stdout']))
372 return result['stdout']
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400373
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400374
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400375class K8SKubectlCli(object):
376 """ Contain kubectl cli commands and api wrappers"""
377 def __init__(self, manager):
378 self._manager = manager
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400379
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400380 def cli_run(self, namespace, name, image, port, replicas=1):
381 cmd = "kubectl -n {0} run {1} --image={2} --port={3} --replicas={4}".\
382 format(namespace, name, image, port, replicas)
383 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400384
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400385 def run(self, namespace, name, image, port, replicas=1):
386 self.cli_run(namespace, name, image, port, replicas)
387 return self._manager.api.deployments.get(
388 namespace=namespace, name=name)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400389
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400390 def cli_expose(self, namespace, resource_type, resource_name,
391 service_name=None, port='', service_type='ClusterIP'):
392 cmd = "kubectl -n {0} expose {1} {2} --port={3} --type={4}".format(
393 namespace, resource_type, resource_name, port, service_type)
394 if service_name is not None:
395 cmd += " --name={}".format(service_name)
396 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400397
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400398 def expose(self, resource, service_name=None,
399 port='', service_type='ClusterIP'):
400 self.cli_expose(resource.namespace, resource.resource_type,
401 resource.name, service_name=service_name,
402 port=port, service_type=service_type)
403 return self._manager.api.services.get(
404 namespace=resource.namespace, name=service_name or resource.name)
405
406 def cli_exec(self, namespace, pod_name, cmd, container=''):
407 kubectl_cmd = "kubectl -n {0} exec --container={1} {2} -- {3}".format(
408 namespace, container, pod_name, cmd)
409 return self._manager.controller_check_call(kubectl_cmd)
410
411 # def exec(...), except exec is statement in python
412 def execute(self, pod, cmd, container=''):
413 return self.cli_exec(pod.namespace, pod.name, cmd, container=container)
414
415 def cli_annotate(self, namespace, resource_type, resource_name,
416 annotations, overwrite=False):
417 cmd = "kubectl -n {0} annotate {1} {2} {3}".format(
418 namespace, resource_type, resource_name, annotations)
419 if overwrite:
420 cmd += " --overwrite"
421 return self._manager.controller_check_call(cmd)
422
423 def annotate(self, resource, annotations, overwrite=False):
424 return self.cli_annotate(resource.namespace, resource.resource_type,
425 resource.name, annotations,
426 overwrite=overwrite)
427
428
429class K8SVirtlet(object):
430 """ Contain virtlet-related methods"""
431 def __init__(self, manager, namespace='kube-system'):
432 self._manager = manager
433 self._namespace = namespace
434
435 def get_virtlet_node_pod(self, node_name):
436 for pod in self._manager.api.pods.list(
437 namespace=self._namespace, name_prefix='virtlet-'):
438 if pod.read().spec.node_name == node_name:
439 return pod
440 return None
441
442 def get_pod_dom_uuid(self, pod):
443 uuid_name_map = self.virtlet_execute(
444 pod.read().spec.node_name, 'virsh list --uuid --name')['stdout']
445 LOG.info("HEHEHEH {}".format(uuid_name_map))
446 LOG.info("MDAMDMAD {}".format(pod.name))
447 for line in uuid_name_map:
448 if line.rstrip().endswith("-{}".format(pod.name)):
449 return line.split(" ")[0]
450 raise Exception("Cannot detect uuid for pod {}".format(pod.name))
451
452 def virsh_domstats(self, pod):
453 """ get dict of vm stats """
454 uuid = self.get_pod_dom_uuid(pod)
455 result = self.virtlet_execute(
456 pod.read().spec.node_name, 'virsh domstats {}'.format(uuid))
457 stats = dict()
458 for line in result['stdout']:
459 if '=' in line:
460 vals = line.strip().split('=')
461 stats[vals[0]] = vals[1]
462 return stats
463
464 def virtlet_execute(self, node_name, cmd, container='libvirt'):
465 """ execute command inside virtlet container """
466 pod = self.get_virtlet_node_pod(node_name)
467 return self._manager.kubectl.execute(pod, cmd, container)
468
469
470class K8SSampleDeployment(object):
471 """ Wrapper for deployment run=>expose=>check frequent combination """
472 def __init__(self, manager, name,
473 namespace=None,
474 image='gcr.io/google-samples/node-hello:1.0',
475 port=8080,
476 replicas=2):
477 namespace = namespace or manager.api.default_namespace
478
479 self._manager = manager
480 self._port = port
481 self._deployment = \
482 manager.kubectl.run(namespace, name,
483 image=image, port=port, replicas=replicas)
484 self._index = 1 # used to generate svc name
485 self._svc = None # hold last created svc
486
487 def wait_ready(self, timeout=300, interval=5):
488 self._deployment.wait_ready(timeout=timeout, interval=interval)
489 return self
490
491 def svc(self):
492 """ Return the last exposed service"""
493 return self._svc
494
495 def expose(self, service_type='ClusterIP'):
496 service_name = "{0}-s{1}".format(self._deployment.name, self._index)
497 self._svc = self._manager.kubectl.expose(
498 self._deployment, port=self._port,
499 service_name=service_name, service_type=service_type)
500 return self._svc
501
502 def curl(self, svc=None, external=False):
503 if svc is None:
504 svc = self.svc()
505 url = "http://{0}:{1}".format(svc.get_ip(external), self._port)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400506 if external:
507 return requests.get(url).text
508 else:
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400509 return self._manager.curl(url)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400510
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400511 def is_service_available(self, svc=None, external=False):
512 return "Hello Kubernetes!" in self.curl(svc, external=external)