Skip to content

Commit 38f5ace

Browse files
committed
Add check-tmp step to local repo tests
- check-tmp checks disk usage in a collection of directories, and fails if they exceed a certain size, reporting on contents - re-use images and skip build stage in verify/check-tmp tests. Should save some time, even though the build cache would have been used. This way, we don't even check.
1 parent f6ecb28 commit 38f5ace

File tree

2 files changed

+125
-7
lines changed

2 files changed

+125
-7
lines changed

tests/check-tmp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to check for leftover files
4+
5+
Checks a collection of temporary or cache directories,
6+
to ensure we aren't wasting image size by forgetting cleanup steps.
7+
8+
This script is run in every local repo image we test
9+
"""
10+
11+
import os
12+
import sys
13+
from subprocess import check_output
14+
from textwrap import indent
15+
16+
# directories larger than this are considered a failure
17+
# a few little files here aren't a problem
18+
THRESHOLD = 1 # in MB
19+
20+
MB = 1024 * 1024
21+
22+
# the paths to check
23+
# all of these locations
24+
# should be cleaned up
25+
# missing is okay
26+
PATHS = [
27+
"/tmp/",
28+
"~/",
29+
"~/.cache/",
30+
# not running with read permissions on root
31+
# "/root/",
32+
]
33+
34+
35+
def du(path):
36+
"""Return disk usage in megabytes of a path"""
37+
# -ks: get total size, reported in kilobytes
38+
out = check_output(["du", "-Hks", path])
39+
return int(out.split(None, 1)[0]) / 1024
40+
41+
42+
def check_dir_size(path):
43+
"""Check the size of a directory
44+
45+
Returns:
46+
47+
True: directory size is below THRESHOLD or is missing
48+
False: directory is larger than THRESHOLD
49+
"""
50+
path = os.path.expanduser(path)
51+
52+
if not os.path.exists(path):
53+
print(f"{path}: missing OK")
54+
return True
55+
56+
size_mb = du(path)
57+
print(f"{path}: {size_mb:.1f} MB", end=" ")
58+
if size_mb <= THRESHOLD:
59+
print("OK")
60+
return True
61+
else:
62+
print("FAIL")
63+
# check size of files one-level deep (du only reports dirs)
64+
for name in os.listdir(path):
65+
subpath = os.path.join(path, name)
66+
if os.path.isfile(subpath):
67+
file_sz = os.stat(subpath).st_size / MB
68+
if file_sz > 0.1:
69+
print(f" {file_sz:.1f}M {subpath}")
70+
# get report on all subdirs that are at least 100k
71+
print(
72+
indent(
73+
check_output(["du", "-Hh", "-t", "100000", path]).decode("utf8"), " "
74+
)
75+
)
76+
return False
77+
78+
79+
def main():
80+
results = [check_dir_size(path) for path in PATHS]
81+
if not all(results):
82+
sys.exit(1)
83+
84+
85+
if __name__ == "__main__":
86+
main()

tests/conftest.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222
import requests
2323
import subprocess
2424
import time
25-
2625
from tempfile import TemporaryDirectory
2726

27+
28+
import escapism
2829
import pytest
2930
import yaml
3031

3132
from repo2docker.__main__ import make_r2d
3233

34+
TESTS_DIR = os.path.abspath(os.path.dirname(__file__))
35+
3336

3437
def pytest_collect_file(parent, path):
3538
if path.basename == "verify":
@@ -38,12 +41,18 @@ def pytest_collect_file(parent, path):
3841
return RemoteRepoList.from_parent(parent, fspath=path)
3942

4043

41-
def make_test_func(args):
44+
def make_test_func(args, skip_build=False):
4245
"""Generate a test function that runs repo2docker"""
4346

4447
def test():
4548
app = make_r2d(args)
4649
app.initialize()
50+
if skip_build:
51+
52+
def build_noop():
53+
print("Skipping build")
54+
55+
app.skip_build = build_noop
4756
if app.run_cmd:
4857
# verify test, run it
4958
app.start()
@@ -184,14 +193,14 @@ def repo_with_submodule():
184193
class Repo2DockerTest(pytest.Function):
185194
"""A pytest.Item for running repo2docker"""
186195

187-
def __init__(self, name, parent, args=None):
196+
def __init__(self, name, parent, args=None, skip_build=False):
188197
self.args = args
189198
self.save_cwd = os.getcwd()
190-
f = parent.obj = make_test_func(args)
199+
f = parent.obj = make_test_func(args, skip_build=skip_build)
191200
super().__init__(name, parent, callobj=f)
192201

193202
def reportinfo(self):
194-
return self.parent.fspath, None, ""
203+
return (self.parent.fspath, None, "")
195204

196205
def repr_failure(self, excinfo):
197206
err = excinfo.value
@@ -217,11 +226,34 @@ def collect(self):
217226
extra_args = yaml.safe_load(f)
218227
args += extra_args
219228

229+
print(self.fspath.basename, self.fspath.dirname, str(self.fspath))
230+
# re-use image name for multiple tests of the same image
231+
# so we don't run through the build twice
232+
rel_repo_dir = os.path.relpath(self.fspath.dirname, TESTS_DIR)
233+
image_name = f"r2d-tests-{escapism.escape(rel_repo_dir, escape_char='-').lower()}-{int(time.time())}"
234+
args.append(f"--image-name={image_name}")
220235
args.append(self.fspath.dirname)
221-
222236
yield Repo2DockerTest.from_parent(self, name="build", args=args)
237+
238+
yield Repo2DockerTest.from_parent(
239+
self,
240+
name=self.fspath.basename,
241+
args=args + ["./verify"],
242+
skip_build=True,
243+
)
244+
245+
# mount the tests dir as a volume
246+
check_tmp_args = (
247+
args[:-1]
248+
+ ["--volume", f"{TESTS_DIR}:/io/tests"]
249+
+ [args[-1], "/io/tests/check-tmp"]
250+
)
251+
223252
yield Repo2DockerTest.from_parent(
224-
self, name=self.fspath.basename, args=args + ["./verify"]
253+
self,
254+
name="check-tmp",
255+
args=check_tmp_args,
256+
skip_build=True,
225257
)
226258

227259

0 commit comments

Comments
 (0)