Make memory limit checking tests more solid
Use a more precise way of triggering OOM conditions
Make sure that the image is rebuilt each time with a cachebust
Don't have two copies of the OOM triggering script
MODIFY
tests/memlimit.py
tests/memlimit.py
-import subprocess
-
-def test_memlimit_nondockerfile():
"""
- Test if memory limited builds are working for non dockerfile builds
+Test that build time memory limits are actually enforced.
+
+We give the container image at least 128M of RAM (so base things like
+apt and pip can run), and then try to allocate & use 256MB in postBuild.
+This should fail!
"""
+import os
+import subprocess
+import time
+
+def does_build(builddir, mem_limit, mem_allocate_mb):
+ mem_allocate_mb_file = os.path.join(builddir, 'mem_allocate_mb')
+
+ # Cache bust so we actually do a rebuild each time this is run!
+ with open(os.path.join(builddir, 'cachebust'), 'w') as cachebust:
+ cachebust.write(str(time.time()))
try:
- subprocess.check_call([
+ with open(mem_allocate_mb_file, 'w') as f:
+ f.write(str(mem_allocate_mb))
+ try:
+ output = subprocess.check_output(
+ [
'repo2docker',
'--no-run',
- '--build-memory-limit', '4M',
- 'tests/memlimit/non-dockerfile'
- ])
- # If this doesn't throw an exception, then memory limit was
- # not enforced!
- assert False
+ '--build-memory-limit', '{}M'.format(mem_limit),
+ builddir
+ ],
+ stderr=subprocess.STDOUT,
+ ).decode()
+ print(output)
+ return True
except subprocess.CalledProcessError as e:
- assert True
+ output = e.output.decode()
+ print(output)
+ if "The command '/bin/sh -c ./postBuild' returned a non-zero code: 137" in output:
+ return False
+ else:
+ raise
+ finally:
+ os.remove(mem_allocate_mb_file)
+
-def test_memlimit_dockerfile():
+def test_memlimit_nondockerfile_fail():
"""
Test if memory limited builds are working for non dockerfile builds
"""
- try:
- subprocess.check_call([
- 'repo2docker',
- '--no-run',
- '--build-memory-limit', '4M',
- 'tests/memlimit/dockerfile'
- ])
- # If this doesn't throw an exception, then memory limit was
- # not enforced!
- assert False
- except subprocess.CalledProcessError as e:
- assert True
+ basedir = os.path.dirname(__file__)
+ assert not does_build(
+ os.path.join(basedir, 'memlimit/non-dockerfile'),
+ 128,
+ 256
+ )
+ assert does_build(
+ os.path.join(basedir, 'memlimit/non-dockerfile'),
+ 512,
+ 256
+ )
+
+
+def test_memlimit_dockerfile_fail():
+ """
+ Test if memory limited builds are working for dockerfile builds
+ """
+ basedir = os.path.dirname(__file__)
+ assert not does_build(
+ os.path.join(basedir, 'memlimit/dockerfile'),
+ 128,
+ 256
+ )
+
+ assert does_build(
+ os.path.join(basedir, 'memlimit/dockerfile'),
+ 512,
+ 256
+ )

ADD
tests/memlimit/.gitignore
+cachebust

MODIFY
tests/memlimit/dockerfile/Dockerfile
tests/memlimit/dockerfile/Dockerfile
-FROM python:3.6
+FROM ubuntu:zesty
-COPY postBuild /usr/local/bin/postBuild
+RUN apt-get update && apt-get install --yes python3
+
+COPY . .
RUN ./postBuild

MODIFY
tests/memlimit/dockerfile/postBuild
tests/memlimit/dockerfile/postBuild
#!/usr/bin/env python3
+"""
+Simplest program that tries to allocate a large amount of RAM.
-array = []
-for i in range(1024 * 1024 * 64):
- array.append(i)
+malloc lies on Linux by default, so we use memset to force the
+kernel to actually give us real memory.
+"""
+from ctypes import cdll, c_void_p, memset
+import os
+
+libc = cdll.LoadLibrary("libc.so.6")
+libc.malloc.restype = c_void_p
+
+with open('mem_allocate_mb') as f:
+ mem_allocate_mb = int(f.read().strip())
+
+size = 1024 * 1024 * mem_allocate_mb
+print("trying to allocate {}MB".format(mem_allocate_mb))
+
+ret = libc.malloc(size)
+
+memset(ret, 0, size)

DELETE
tests/memlimit/non-dockerfile/postBuild
-#!/usr/bin/env python3
-
-array = []
-for i in range(1024 * 1024 * 64):
- array.append(i)

ADD
tests/memlimit/non-dockerfile/postBuild
+../dockerfile/postBuild
\ No newline at end of file

Make memory limit checking tests more solid

- Use a more precise way of triggering OOM conditions
- Make sure that the image is rebuilt each time with a cachebust
- Don't have two copies of the OOM triggering script