test_docker.py 8.66 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
        self.assertEqual(status, 0)
75
        self.assertEqual(self.host.logs(container), string + '\n')
76 77


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

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

83 84 85
        try:
            self.host.start(container)
        except Exception, e:
86
            self.assertTrue(str(e).find('Failed to create the container') >= 0)
87 88

        self.assertFalse(self.host.containers) # All containers are gone
89 90


Philip ABBET's avatar
Philip ABBET committed
91 92
    @slow
    def test_timeout(self):
93

Philip ABBET's avatar
Philip ABBET committed
94
        sleep_for = 100 # seconds
95

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

99 100
        retval = self.host.wait(container, timeout=0.5)
        self.assertTrue(retval is None)
101

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

104
        retval = self.host.wait(container)
105

Philip ABBET's avatar
Philip ABBET committed
106
        self.assertEqual(self.host.status(container), 'exited')
107 108
        self.assertEqual(retval, 137)
        self.assertEqual(self.host.logs(container), '')
109 110


Philip ABBET's avatar
Philip ABBET committed
111 112
    @slow
    def test_does_not_timeout(self):
113

Philip ABBET's avatar
Philip ABBET committed
114
        sleep_for = 0.5 # seconds
115

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

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

Philip ABBET's avatar
Philip ABBET committed
121 122
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 0)
123
        self.assertEqual(self.host.logs(container), '')
124 125


Philip ABBET's avatar
Philip ABBET committed
126 127
    @slow
    def test_memory_limit(self):
128

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

Philip ABBET's avatar
Philip ABBET committed
138 139
        container = self.host.create_container(self.test_environment, cmd)
        self.host.start(container, virtual_memory_in_megabytes=4)
140

Philip ABBET's avatar
Philip ABBET committed
141
        time.sleep(2)
142

Philip ABBET's avatar
Philip ABBET committed
143
        stats = self.host.statistics(container)
144

Philip ABBET's avatar
Philip ABBET committed
145
        status = self.host.wait(container)
146

Philip ABBET's avatar
Philip ABBET committed
147 148
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 137)
149
        self.assertEqual(self.host.logs(container).strip(), 'Before')
150 151


Philip ABBET's avatar
Philip ABBET committed
152 153
    @slow
    def test_memory_limit2(self):
154

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

Philip ABBET's avatar
Philip ABBET committed
164 165
        container = self.host.create_container(self.test_environment, cmd)
        self.host.start(container, virtual_memory_in_megabytes=100)
166

Philip ABBET's avatar
Philip ABBET committed
167
        time.sleep(2)
168

Philip ABBET's avatar
Philip ABBET committed
169
        stats = self.host.statistics(container)
170

Philip ABBET's avatar
Philip ABBET committed
171
        status = self.host.wait(container)
172

Philip ABBET's avatar
Philip ABBET committed
173 174
        assert stats['memory']['percent'] > 10, 'Memory check failed, ' \
            '%d%% <= 10%%' % stats['memory']['percent']
175

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

Philip ABBET's avatar
Philip ABBET committed
179 180
        self.assertEqual(self.host.status(container), 'exited')
        self.assertEqual(status, 0)
181
        self.assertEqual(self.host.logs(container).strip(), 'Before\nAfter')
182 183


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

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

Philip ABBET's avatar
Philip ABBET committed
189 190
        container = self.host.create_container(self.test_environment,
                                               ['python', dst_name, str(processes)])
191

192
        container.add_volume(program, os.path.join('/tmp', 'cpu_stress.py'))
193

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

Philip ABBET's avatar
Philip ABBET committed
196
        time.sleep(sleep_time)
197

Philip ABBET's avatar
Philip ABBET committed
198
        stats = self.host.statistics(container)
199

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

Philip ABBET's avatar
Philip ABBET committed
202 203 204
        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)
205

Philip ABBET's avatar
Philip ABBET committed
206 207 208
        # make sure nothing is there anymore
        self.host.kill(container)
        self.assertEqual(self.host.wait(container), 137)
209 210


Philip ABBET's avatar
Philip ABBET committed
211 212 213 214
    @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)
215 216


Philip ABBET's avatar
Philip ABBET committed
217 218 219 220
    @slow
    def test_cpulimit_at_100percent(self):
        # runs 4 processes that should consume 50% of the host CPU
        self._run_cpulimit(4, 100, 3)
221 222 223 224 225



class HostTest(unittest.TestCase):

Philip ABBET's avatar
Philip ABBET committed
226 227
    def setUp(self):
        Host.images_cache = {}
228 229


Philip ABBET's avatar
Philip ABBET committed
230 231 232
    @slow
    def test_images_cache(self):
        self.assertEqual(len(Host.images_cache), 0)
233

Philip ABBET's avatar
Philip ABBET committed
234 235
        # Might take some time
        start = time.time()
236

Philip ABBET's avatar
Philip ABBET committed
237 238
        host = Host(raise_on_errors=False)
        host.teardown()
239

Philip ABBET's avatar
Philip ABBET committed
240
        stop = time.time()
241

Philip ABBET's avatar
Philip ABBET committed
242 243
        nb_images = len(Host.images_cache)
        self.assertTrue(nb_images > 0)
244

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

Philip ABBET's avatar
Philip ABBET committed
247 248
        # Should be instantaneous
        start = time.time()
249

Philip ABBET's avatar
Philip ABBET committed
250 251
        host = Host(raise_on_errors=False)
        host.teardown()
252

Philip ABBET's avatar
Philip ABBET committed
253
        stop = time.time()
254

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

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


Philip ABBET's avatar
Philip ABBET committed
260 261 262
    @slow
    def test_images_cache_file(self):
        self.assertEqual(len(Host.images_cache), 0)
263

Philip ABBET's avatar
Philip ABBET committed
264 265
        # Might take some time
        start = time.time()
266

Philip ABBET's avatar
Philip ABBET committed
267 268 269
        host = Host(images_cache=os.path.join(tmp_prefix, 'images_cache.json'),
                    raise_on_errors=False)
        host.teardown()
270

Philip ABBET's avatar
Philip ABBET committed
271
        stop = time.time()
272

Philip ABBET's avatar
Philip ABBET committed
273 274
        nb_images = len(Host.images_cache)
        self.assertTrue(nb_images > 0)
275

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

Philip ABBET's avatar
Philip ABBET committed
278
        Host.images_cache = {}
279

Philip ABBET's avatar
Philip ABBET committed
280 281
        # Should be instantaneous
        start = time.time()
282

Philip ABBET's avatar
Philip ABBET committed
283 284 285
        host = Host(images_cache=os.path.join(tmp_prefix, 'images_cache.json'),
                    raise_on_errors=False)
        host.teardown()
286

Philip ABBET's avatar
Philip ABBET committed
287
        stop = time.time()
288

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

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