Repository: borgbackup/borg

Issue:
borg benchmark fails if borg-test-data directory already exists
## Is this a BUG / ISSUE report or a QUESTION?
BUG

#### Your borg version (borg -V).
borg 1.1.10
#### Operating system (distribution) and version.
Fedora 30

## Describe the problem you're observing.
`borg benchmark crud [REPO] [PATH]` fails to run a second time if the `borg-test-data` directory already exists.
#### Can you reproduce the problem?
Run borg benchmark crud twice. The second time make sure to specify the same [PATH] without having deleted the `borg-test-data` directory from the previous run and it will fail. If specifying a different (new) [PATH] it works.

#### Include any warning/errors/backtraces from the system logs
```
Local Exception
Traceback (most recent call last):
  File "/usr/lib64/python3.7/site-packages/borg/archiver.py", line 4501, in main
    exit_code = archiver.run(args)
  File "/usr/lib64/python3.7/site-packages/borg/archiver.py", line 4433, in run
    return set_ec(func(args))
  File "/usr/lib64/python3.7/site-packages/borg/archiver.py", line 476, in do_benchmark_crud
    with test_files(args.path, count, size, random) as path:
  File "/usr/lib64/python3.7/contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "/usr/lib64/python3.7/site-packages/borg/archiver.py", line 451, in test_files
    os.makedirs(path)
  File "/usr/lib64/python3.7/os.py", line 221, in makedirs
    mkdir(name, mode)
FileExistsError: [Errno 17] File exists: '/tmp/borg-test-data'
```




Discussion:
guess we could delete the test-data directory at the end of the test run.

can you make a PR?
Sometimes `borg-test-data` is deleted, other times it isn't. I haven't figured it out. Maybe it has to do with environments?
Sounds weird.
https://github.com/borgbackup/borg/blob/e299ad824c519ce949fc3a1a749fda010fed220a/src/borg/archiver.py#L434-L444

The directory should get deleted, except of course the benchmark is interrupted e.g. by Ctrl-C. We could wrap the `yield; shutil.rmtree(path)` in a try-finally clause.

@adatum I did not encounter the error. Does it happen randomly or can you give us an easy way to reproduce it?
It was consistent not random, but I am not being able to reproduce the issue right now. Is it possible for exported environment variables to interfere? 

I don't remember having used Ctrl-C before first encountering the error, although I suppose it is possible. I can confirm Ctrl-C does create the error.
try/finally sounds good. guess the `try` should be before it creates the directory on disk.
Hmm, no.

`test_files` is a context manager, so it already does that (always execute the rmtree, even in case of exceptions).

of course, if you kill it badly, it won't close the contextmanager.

Resolving patch:
diff --git a/src/borg/archiver/benchmark_cmd.py b/src/borg/archiver/benchmark_cmd.py
--- a/src/borg/archiver/benchmark_cmd.py
+++ b/src/borg/archiver/benchmark_cmd.py
@@ -2,7 +2,7 @@
 from contextlib import contextmanager
 import functools
 import os
-import shutil
+import tempfile
 import time
 
 from ..constants import *  # NOQA
@@ -60,9 +60,7 @@ def measurement_run(repo, path):
 
         @contextmanager
         def test_files(path, count, size, random):
-            try:
-                path = os.path.join(path, "borg-test-data")
-                os.makedirs(path)
+            with tempfile.TemporaryDirectory(prefix="borg-test-data-", dir=path) as path:
                 z_buff = None if random else memoryview(zeros)[:size] if size <= len(zeros) else b"\0" * size
                 for i in range(count):
                     fname = os.path.join(path, "file_%d" % i)
@@ -70,8 +68,6 @@ def test_files(path, count, size, random):
                     with SyncFile(fname, binary=True) as fd:  # used for posix_fadvise's sake
                         fd.write(data)
                 yield path
-            finally:
-                shutil.rmtree(path)
 
         if "_BORG_BENCHMARK_CRUD_TEST" in os.environ:
             tests = [("Z-TEST", 1, 1, False), ("R-TEST", 1, 1, True)]


Test patch: