blob: 2d22dc13a05a03ca2f2ba50e887ca82cdfaf9d3e [file] [log] [blame]
Jan Tattermusch7897ae92017-06-07 22:57:36 +02001# Copyright 2015 gRPC authors.
Jan Tattermusch91ad0182015-10-01 09:22:03 -07002#
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
Jan Tattermusch91ad0182015-10-01 09:22:03 -07006#
Jan Tattermusch7897ae92017-06-07 22:57:36 +02007# http://www.apache.org/licenses/LICENSE-2.0
Jan Tattermusch91ad0182015-10-01 09:22:03 -07008#
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Jan Tattermusch91ad0182015-10-01 09:22:03 -070014"""Helpers to run docker instances as jobs."""
15
siddharthshukla0589e532016-07-07 16:08:01 +020016from __future__ import print_function
17
Jan Tattermusch91ad0182015-10-01 09:22:03 -070018import tempfile
19import time
20import uuid
21import os
22import subprocess
23
Jan Tattermusch5c79a312016-12-20 11:02:50 +010024import jobset
25
Jan Tattermusch91ad0182015-10-01 09:22:03 -070026_DEVNULL = open(os.devnull, 'w')
27
Jan Tattermusche2686282015-10-08 16:27:07 -070028
29def random_name(base_name):
ncteisen05687c32017-12-11 16:54:47 -080030 """Randomizes given base name."""
31 return '%s_%s' % (base_name, uuid.uuid4())
Jan Tattermusche2686282015-10-08 16:27:07 -070032
33
34def docker_kill(cid):
ncteisen05687c32017-12-11 16:54:47 -080035 """Kills a docker container. Returns True if successful."""
36 return subprocess.call(
37 ['docker', 'kill', str(cid)],
38 stdin=subprocess.PIPE,
39 stdout=_DEVNULL,
40 stderr=subprocess.STDOUT) == 0
Jan Tattermusch91ad0182015-10-01 09:22:03 -070041
42
Jan Tattermusch98c0be52015-10-09 14:33:34 -070043def docker_mapped_port(cid, port, timeout_seconds=15):
ncteisen05687c32017-12-11 16:54:47 -080044 """Get port mapped to internal given internal port for given container."""
45 started = time.time()
46 while time.time() - started < timeout_seconds:
47 try:
48 output = subprocess.check_output(
49 'docker port %s %s' % (cid, port), stderr=_DEVNULL, shell=True)
50 return int(output.split(':', 2)[1])
51 except subprocess.CalledProcessError as e:
52 pass
Mehrdad Afshari87cd9942018-01-02 14:40:00 -080053 raise Exception('Failed to get exposed port %s for container %s.' % (port,
54 cid))
Jan Tattermusch91ad0182015-10-01 09:22:03 -070055
56
Eric Gribkoff22afddf2017-03-15 15:43:43 -070057def wait_for_healthy(cid, shortname, timeout_seconds):
ncteisen05687c32017-12-11 16:54:47 -080058 """Wait timeout_seconds for the container to become healthy"""
59 started = time.time()
60 while time.time() - started < timeout_seconds:
61 try:
62 output = subprocess.check_output(
63 [
64 'docker', 'inspect', '--format="{{.State.Health.Status}}"',
65 cid
66 ],
67 stderr=_DEVNULL)
68 if output.strip('\n') == 'healthy':
69 return
70 except subprocess.CalledProcessError as e:
71 pass
72 time.sleep(1)
73 raise Exception('Timed out waiting for %s (%s) to pass health check' %
74 (shortname, cid))
Eric Gribkoff22afddf2017-03-15 15:43:43 -070075
76
Jan Tattermusch91ad0182015-10-01 09:22:03 -070077def finish_jobs(jobs):
ncteisen05687c32017-12-11 16:54:47 -080078 """Kills given docker containers and waits for corresponding jobs to finish"""
79 for job in jobs:
80 job.kill(suppress_failure=True)
Jan Tattermusch91ad0182015-10-01 09:22:03 -070081
ncteisen05687c32017-12-11 16:54:47 -080082 while any(job.is_running() for job in jobs):
83 time.sleep(1)
Jan Tattermusch91ad0182015-10-01 09:22:03 -070084
85
86def image_exists(image):
ncteisen05687c32017-12-11 16:54:47 -080087 """Returns True if given docker image exists."""
88 return subprocess.call(
89 ['docker', 'inspect', image],
90 stdin=subprocess.PIPE,
91 stdout=_DEVNULL,
92 stderr=subprocess.STDOUT) == 0
Jan Tattermusch91ad0182015-10-01 09:22:03 -070093
94
95def remove_image(image, skip_nonexistent=False, max_retries=10):
ncteisen05687c32017-12-11 16:54:47 -080096 """Attempts to remove docker image with retries."""
97 if skip_nonexistent and not image_exists(image):
98 return True
99 for attempt in range(0, max_retries):
100 if subprocess.call(
101 ['docker', 'rmi', '-f', image],
102 stdin=subprocess.PIPE,
103 stdout=_DEVNULL,
104 stderr=subprocess.STDOUT) == 0:
105 return True
106 time.sleep(2)
107 print('Failed to remove docker image %s' % image)
108 return False
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700109
110
111class DockerJob:
ncteisen05687c32017-12-11 16:54:47 -0800112 """Encapsulates a job"""
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700113
ncteisen05687c32017-12-11 16:54:47 -0800114 def __init__(self, spec):
115 self._spec = spec
116 self._job = jobset.Job(
117 spec, newline_on_success=True, travis=True, add_env={})
118 self._container_name = spec.container_name
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700119
ncteisen05687c32017-12-11 16:54:47 -0800120 def mapped_port(self, port):
121 return docker_mapped_port(self._container_name, port)
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700122
ncteisen05687c32017-12-11 16:54:47 -0800123 def wait_for_healthy(self, timeout_seconds):
124 wait_for_healthy(self._container_name, self._spec.shortname,
125 timeout_seconds)
Eric Gribkoff22afddf2017-03-15 15:43:43 -0700126
ncteisen05687c32017-12-11 16:54:47 -0800127 def kill(self, suppress_failure=False):
128 """Sends kill signal to the container."""
129 if suppress_failure:
130 self._job.suppress_failure_message()
131 return docker_kill(self._container_name)
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700132
ncteisen05687c32017-12-11 16:54:47 -0800133 def is_running(self):
134 """Polls a job and returns True if given job is still running."""
135 return self._job.state() == jobset._RUNNING