Repository: pre-commit/pre-commit

Issue:
Docker inspect fails when running with Docker in Docker
When trying to run pre commits with `docker_image` language via GitLabs Docker in Docker approach the docker inspect fails, and so pre-commit throws an error when trying to get the mount path.

The [code](https://github.com/pre-commit/pre-commit/blob/fe436f1eb09dfdd67032b4f9f8dfa543fb99cf06/pre_commit/languages/docker.py#L39) for this already has a fall back of "return path" but doesn't handle the docker command failing.

Could the code for this be modified to handle the docker inspect failing?

More details of docker in docker are [here](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-the-docker-executor-with-the-docker-image-docker-in-docker)


Full logs of the failure:


### version information

```
pre-commit version: 2.13.0
sys.version:
    3.6.9 (default, Jan 26 2021, 15:33:00) 
    [GCC 8.4.0]
sys.executable: /usr/bin/python3
os.name: posix
sys.platform: linux
```

### error information

```
An unexpected error has occurred: CalledProcessError: command: ('/usr/bin/docker', 'inspect', 'runner-dmrvfsu-project-35395-concurrent-0')
return code: 1
expected return code: 0
stdout:
    []
    
stderr:
    Error: No such object: runner-dmrvfsu-project-35395-concurrent-0
    
```

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/error_handler.py", line 65, in error_handler
    yield
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/main.py", line 385, in main
    return run(args.config, store, args)
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/commands/run.py", line 410, in run
    return _run_hooks(config, hooks, skips, args, environ)
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/commands/run.py", line 288, in _run_hooks
    verbose=args.verbose, use_color=args.color,
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/commands/run.py", line 194, in _run_single_hook
    retcode, out = language.run_hook(hook, filenames, use_color)
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/languages/docker_image.py", line 19, in run_hook
    cmd = docker_cmd() + hook.cmd
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/languages/docker.py", line 107, in docker_cmd
    '-v', f'{_get_docker_path(os.getcwd())}:/src:rw,Z',
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/languages/docker.py", line 34, in _get_docker_path
    _, out, _ = cmd_output_b('docker', 'inspect', hostname)
  File "/usr/local/lib/python3.6/dist-packages/pre_commit/util.py", line 154, in cmd_output_b
    raise CalledProcessError(returncode, cmd, retcode, stdout_b, stderr_b)
pre_commit.util.CalledProcessError: command: ('/usr/bin/docker', 'inspect', 'runner-dmrvfsu-project-35395-concurrent-0')
return code: 1
expected return code: 0
stdout:
    []
    
stderr:
    Error: No such object: runner-dmrvfsu-project-35395-concurrent-0
```


Discussion:
I believe this is already fixed by #1919
Thanks, so just need to wait for the next release :) 
Looking closer at those changes the `docker inspect` command will still fail for my case
@robin-moss why?
@asottile `docker inspect` is failing because job container is run on host Docker engine (and docker socket is not mounted), so docker client is querying Docker in Docker engine, where it can't find job container.

Job container is the place where pre-commit is run.
can you provide commands I can run to validate?
@asottile I prepared test repository: https://github.com/wiget/pre-commit-in-gitlab-with-dind
It uses docker-compose to simulate how GitLab CI with Docker in Docker works.
To test this you will need docker and docker-compose installed and then run in directory when above repository is cloned:
```shell
docker-compose up -d docker
docker-compose up --build job
```
You can tweak `Dockerfile` to install `pre-commit` from git for easier testing.
can you reproduce without docker compose? I'd rather not use it if I don't need to
Sure. I added few scripts.  
`prepare.sh` will create docker network, volumes, start Docker in Docker and build job container image.  
`test.sh` will run job container showing what is wrong.  
`cleanup.sh` should remove all created resources.

Resolving patch:
diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py
--- a/pre_commit/languages/docker.py
+++ b/pre_commit/languages/docker.py
@@ -8,6 +8,7 @@
 from pre_commit.hook import Hook
 from pre_commit.languages import helpers
 from pre_commit.prefix import Prefix
+from pre_commit.util import CalledProcessError
 from pre_commit.util import clean_path_on_failure
 from pre_commit.util import cmd_output_b
 
@@ -42,7 +43,11 @@ def _get_docker_path(path: str) -> str:
 
     container_id = _get_container_id()
 
-    _, out, _ = cmd_output_b('docker', 'inspect', container_id)
+    try:
+        _, out, _ = cmd_output_b('docker', 'inspect', container_id)
+    except CalledProcessError:
+        # self-container was not visible from here (perhaps docker-in-docker)
+        return path
 
     container, = json.loads(out)
     for mount in container['Mounts']:


Test patch:
diff --git a/tests/languages/docker_test.py b/tests/languages/docker_test.py
--- a/tests/languages/docker_test.py
+++ b/tests/languages/docker_test.py
@@ -8,6 +8,7 @@
 import pytest
 
 from pre_commit.languages import docker
+from pre_commit.util import CalledProcessError
 
 DOCKER_CGROUP_EXAMPLE = b'''\
 12:hugetlb:/docker/c33988ec7651ebc867cb24755eaf637a6734088bc7eef59d5799293a9e5450f7
@@ -171,3 +172,10 @@ def test_get_docker_path_in_docker_windows(in_docker):
         path = r'c:\folder\test\something'
         expected = r'c:\users\user\test\something'
         assert docker._get_docker_path(path) == expected
+
+
+def test_get_docker_path_in_docker_docker_in_docker(in_docker):
+    # won't be able to discover "self" container in true docker-in-docker
+    err = CalledProcessError(1, (), 0, b'', b'')
+    with mock.patch.object(docker, 'cmd_output_b', side_effect=err):
+        assert docker._get_docker_path('/project') == '/project'