test_docker.py 9.01 KB
Newer Older
André Anjos's avatar
André Anjos committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

###############################################################################
#                                                                             #
# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
# Contact: beat.support@idiap.ch                                              #
#                                                                             #
# This file is part of the beat.core module of the BEAT platform.             #
#                                                                             #
# Commercial License Usage                                                    #
# Licensees holding valid commercial BEAT licenses may use this file in       #
# accordance with the terms contained in a written agreement between you      #
# and Idiap. For further information contact tto@idiap.ch                     #
#                                                                             #
# Alternatively, this file may be used under the terms of the GNU Affero      #
# Public License version 3 as published by the Free Software and appearing    #
# in the file LICENSE.AGPL included in the packaging of this file.            #
# The BEAT platform is distributed in the hope that it will be useful, but    #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
#                                                                             #
# You should have received a copy of the GNU Affero Public License along      #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
#                                                                             #
###############################################################################


"""Asynchronous process I/O with the Subprocess module
"""

import os
import sys
import time
35
import unittest
André Anjos's avatar
André Anjos committed
36
import pkg_resources
37
import time
André Anjos's avatar
André Anjos committed
38

39 40
import docker
import requests
André Anjos's avatar
André Anjos committed
41

42
from ..dock import Host
43
from . import tmp_prefix
44
from .utils import slow
André Anjos's avatar
André Anjos committed
45

46

47 48
class AsyncTest(unittest.TestCase):

Philip ABBET's avatar
Philip ABBET committed
49 50 51 52
    @classmethod
    def setUpClass(cls):
        cls.host = Host(raise_on_errors=False)
        cls.test_environment = cls.host.full_environment_name('Python 2.7')
53 54


Philip ABBET's avatar
Philip ABBET committed
55 56 57
    @classmethod
    def tearDownClass(cls):
        cls.host.teardown()
58 59


Philip ABBET's avatar
Philip ABBET committed
60 61 62
    def tearDown(self):
        self.host.teardown()
        assert not self.host.containers # All containers are gone
63 64


Philip ABBET's avatar
Philip ABBET committed
65 66
    @slow
    def test_echo(self):
67

Philip ABBET's avatar
Philip ABBET committed
68
        string = "hello, world"
69

Philip ABBET's avatar
Philip ABBET committed
70 71 72
        container = self.host.create_container('debian:8.4', ["echo", string])
        self.host.start(container)
        status = self.host.wait(container)
73

Philip ABBET's avatar
Philip ABBET committed
74 75 76
        self.assertEqual(status, 0)
        self.assertEqual(self.host.stdout(container), string + '\n')
        self.assertEqual(self.host.stderr(container), '')
77 78


Philip ABBET's avatar
Philip ABBET committed
79 80
    @slow
    def test_non_existing(self):
81

Philip ABBET's avatar
Philip ABBET committed
82
        container = self.host.create_container('debian:8.4', ["sdfsdfdsf329909092"])
83

Philip ABBET's avatar
Philip ABBET committed
84
        self.assertRaises(docker.errors.NotFound, self.host.start, container)
85

Philip ABBET's avatar
Philip ABBET committed
86
        assert not self.host.containers # All containers are gone
87 88


Philip ABBET's avatar
Philip ABBET committed
89 90
    @slow
    def test_timeout(self):
91

Philip ABBET's avatar
Philip ABBET committed
92
        sleep_for = 100 # seconds
93

Philip ABBET's avatar
Philip ABBET committed
94 95
        container = self.host.create_container('debian:8.4', ["sleep", str(sleep_for)])
        self.host.start(container)
96

Philip ABBET's avatar
Philip ABBET committed
97 98 99 100 101
        try:
            retval = self.host.wait(container, timeout=0.5)
            assert False, "timeout never occurred after %d seconds" % sleep_for
        except requests.exceptions.ReadTimeout as e:
            self.assertEqual(self.host.status(container), 'running')
102

Philip ABBET's avatar
Philip ABBET committed
103
        self.host.kill(container)
104

Philip ABBET's avatar
Philip ABBET committed
105
        status = self.host.wait(container)
106

Philip ABBET's avatar
Philip ABBET committed
107 108 109 110
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 137)
        self.assertEqual(self.host.stdout(container), '')
        self.assertEqual(self.host.stderr(container), '')
111 112


Philip ABBET's avatar
Philip ABBET committed
113 114
    @slow
    def test_does_not_timeout(self):
115

Philip ABBET's avatar
Philip ABBET committed
116
        sleep_for = 0.5 # seconds
117

Philip ABBET's avatar
Philip ABBET committed
118 119
        container = self.host.create_container('debian:8.4', ["sleep", str(sleep_for)])
        self.host.start(container)
120

Philip ABBET's avatar
Philip ABBET committed
121
        status = self.host.wait(container, timeout=5) # Should not timeout
122

Philip ABBET's avatar
Philip ABBET committed
123 124 125 126
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 0)
        self.assertEqual(self.host.stdout(container), '')
        self.assertEqual(self.host.stderr(container), '')
127 128


Philip ABBET's avatar
Philip ABBET committed
129 130
    @slow
    def test_memory_limit(self):
131

Philip ABBET's avatar
Philip ABBET committed
132 133 134 135 136 137 138 139
        cmd = ['python', '-c', '; '.join([
            "print('Before')",
            "import sys; sys.stdout.flush()",
            "d = '0' * (10 * 1024 * 1024)",
            "import time; time.sleep(5)",
            "print('After')",
          ])
        ]
140

Philip ABBET's avatar
Philip ABBET committed
141 142
        container = self.host.create_container(self.test_environment, cmd)
        self.host.start(container, virtual_memory_in_megabytes=4)
143

Philip ABBET's avatar
Philip ABBET committed
144
        time.sleep(2)
145

Philip ABBET's avatar
Philip ABBET committed
146
        stats = self.host.statistics(container)
147

Philip ABBET's avatar
Philip ABBET committed
148
        status = self.host.wait(container)
149

Philip ABBET's avatar
Philip ABBET committed
150 151 152 153
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 137)
        self.assertEqual(self.host.stdout(container).strip(), 'Before')
        self.assertEqual(self.host.stderr(container), '')
154 155


Philip ABBET's avatar
Philip ABBET committed
156 157
    @slow
    def test_memory_limit2(self):
158

Philip ABBET's avatar
Philip ABBET committed
159 160 161 162 163 164 165 166
        cmd = ['python', '-c', '; '.join([
            "print('Before')",
            "import sys; sys.stdout.flush()",
            "d = '0' * (10 * 1024 * 1024)",
            "import time; time.sleep(5)",
            "print('After')",
          ])
        ]
167

Philip ABBET's avatar
Philip ABBET committed
168 169
        container = self.host.create_container(self.test_environment, cmd)
        self.host.start(container, virtual_memory_in_megabytes=100)
170

Philip ABBET's avatar
Philip ABBET committed
171
        time.sleep(2)
172

Philip ABBET's avatar
Philip ABBET committed
173
        stats = self.host.statistics(container)
174

Philip ABBET's avatar
Philip ABBET committed
175
        status = self.host.wait(container)
176

Philip ABBET's avatar
Philip ABBET committed
177 178
        assert stats['memory']['percent'] > 10, 'Memory check failed, ' \
            '%d%% <= 10%%' % stats['memory']['percent']
179

Philip ABBET's avatar
Philip ABBET committed
180 181
        assert stats['memory']['percent'] < 15, 'Memory check failed, ' \
            '%d%% >= 15%%' % stats['memory']['percent']
182

Philip ABBET's avatar
Philip ABBET committed
183 184 185 186
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 0)
        self.assertEqual(self.host.stdout(container).strip(), 'Before\nAfter')
        self.assertEqual(self.host.stderr(container), '')
187 188


Philip ABBET's avatar
Philip ABBET committed
189
    def _run_cpulimit(self, processes, max_cpu_percent, sleep_time):
190

Philip ABBET's avatar
Philip ABBET committed
191 192
        program = pkg_resources.resource_filename(__name__, 'cpu_stress.py')
        dst_name = os.path.join('/tmp', os.path.basename(program))
193

Philip ABBET's avatar
Philip ABBET committed
194 195
        container = self.host.create_container(self.test_environment,
                                               ['python', dst_name, str(processes)])
196

Philip ABBET's avatar
Philip ABBET committed
197
        container.copy_path(program, '/tmp')
198

Philip ABBET's avatar
Philip ABBET committed
199
        self.host.start(container, max_cpu_percent=max_cpu_percent)
200

Philip ABBET's avatar
Philip ABBET committed
201
        time.sleep(sleep_time)
202

Philip ABBET's avatar
Philip ABBET committed
203
        stats = self.host.statistics(container)
204

Philip ABBET's avatar
Philip ABBET committed
205
        self.assertEqual(self.host.status(container), 'running')
206

Philip ABBET's avatar
Philip ABBET committed
207 208 209
        percent = stats['cpu']['percent']
        assert percent < (1.1 * max_cpu_percent), \
               "%.2f%% is more than 20%% off the expected ceiling at %d%%!" % (percent, max_cpu_percent)
210

Philip ABBET's avatar
Philip ABBET committed
211 212 213
        # make sure nothing is there anymore
        self.host.kill(container)
        self.assertEqual(self.host.wait(container), 137)
214 215


Philip ABBET's avatar
Philip ABBET committed
216 217 218 219
    @slow
    def test_cpulimit_at_20percent(self):
        # runs 1 process that should consume at most 20% of the host CPU
        self._run_cpulimit(1, 20, 3)
220 221


Philip ABBET's avatar
Philip ABBET committed
222 223 224 225
    @slow
    def test_cpulimit_at_100percent(self):
        # runs 4 processes that should consume 50% of the host CPU
        self._run_cpulimit(4, 100, 3)
226 227 228 229 230



class HostTest(unittest.TestCase):

Philip ABBET's avatar
Philip ABBET committed
231 232
    def setUp(self):
        Host.images_cache = {}
233 234


Philip ABBET's avatar
Philip ABBET committed
235 236 237
    @slow
    def test_images_cache(self):
        self.assertEqual(len(Host.images_cache), 0)
238

Philip ABBET's avatar
Philip ABBET committed
239 240
        # Might take some time
        start = time.time()
241

Philip ABBET's avatar
Philip ABBET committed
242 243
        host = Host(raise_on_errors=False)
        host.teardown()
244

Philip ABBET's avatar
Philip ABBET committed
245
        stop = time.time()
246

Philip ABBET's avatar
Philip ABBET committed
247 248
        nb_images = len(Host.images_cache)
        self.assertTrue(nb_images > 0)
249

Philip ABBET's avatar
Philip ABBET committed
250
        self.assertTrue(stop - start > 2.0)
251

Philip ABBET's avatar
Philip ABBET committed
252 253
        # Should be instantaneous
        start = time.time()
254

Philip ABBET's avatar
Philip ABBET committed
255 256
        host = Host(raise_on_errors=False)
        host.teardown()
257

Philip ABBET's avatar
Philip ABBET committed
258
        stop = time.time()
259

Philip ABBET's avatar
Philip ABBET committed
260
        self.assertEqual(len(Host.images_cache), nb_images)
261

Philip ABBET's avatar
Philip ABBET committed
262
        self.assertTrue(stop - start < 1.0)
263 264


Philip ABBET's avatar
Philip ABBET committed
265 266 267
    @slow
    def test_images_cache_file(self):
        self.assertEqual(len(Host.images_cache), 0)
268

Philip ABBET's avatar
Philip ABBET committed
269 270
        # Might take some time
        start = time.time()
271

Philip ABBET's avatar
Philip ABBET committed
272 273 274
        host = Host(images_cache=os.path.join(tmp_prefix, 'images_cache.json'),
                    raise_on_errors=False)
        host.teardown()
275

Philip ABBET's avatar
Philip ABBET committed
276
        stop = time.time()
277

Philip ABBET's avatar
Philip ABBET committed
278 279
        nb_images = len(Host.images_cache)
        self.assertTrue(nb_images > 0)
280

Philip ABBET's avatar
Philip ABBET committed
281
        self.assertTrue(stop - start > 2.0)
282

Philip ABBET's avatar
Philip ABBET committed
283
        Host.images_cache = {}
284

Philip ABBET's avatar
Philip ABBET committed
285 286
        # Should be instantaneous
        start = time.time()
287

Philip ABBET's avatar
Philip ABBET committed
288 289 290
        host = Host(images_cache=os.path.join(tmp_prefix, 'images_cache.json'),
                    raise_on_errors=False)
        host.teardown()
291

Philip ABBET's avatar
Philip ABBET committed
292
        stop = time.time()
293

Philip ABBET's avatar
Philip ABBET committed
294
        self.assertEqual(len(Host.images_cache), nb_images)
295

Philip ABBET's avatar
Philip ABBET committed
296
        self.assertTrue(stop - start < 1.0)