summaryrefslogtreecommitdiff
path: root/test/lib/ansible_test/_internal/docker_util.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/lib/ansible_test/_internal/docker_util.py')
-rw-r--r--test/lib/ansible_test/_internal/docker_util.py58
1 files changed, 39 insertions, 19 deletions
diff --git a/test/lib/ansible_test/_internal/docker_util.py b/test/lib/ansible_test/_internal/docker_util.py
index efbebb6c6b..fbaca78630 100644
--- a/test/lib/ansible_test/_internal/docker_util.py
+++ b/test/lib/ansible_test/_internal/docker_util.py
@@ -6,15 +6,12 @@ import enum
import json
import os
import pathlib
+import re
import socket
import time
import urllib.parse
import typing as t
-from .io import (
- read_text_file,
-)
-
from .util import (
ApplicationError,
common_environment,
@@ -297,7 +294,7 @@ def detect_host_properties(args: CommonConfig) -> ContainerHostProperties:
multi_line_commands = (
' && '.join(single_line_commands),
'cat /proc/1/cgroup',
- 'cat /proc/1/mounts',
+ 'cat /proc/1/mountinfo',
)
options = ['--volume', '/sys/fs/cgroup:/probe:ro']
@@ -573,24 +570,47 @@ def get_podman_hostname() -> str:
@cache
def get_docker_container_id() -> t.Optional[str]:
"""Return the current container ID if running in a container, otherwise return None."""
- path = '/proc/self/cpuset'
+ mountinfo_path = pathlib.Path('/proc/self/mountinfo')
container_id = None
+ engine = None
+
+ if mountinfo_path.is_file():
+ # NOTE: This method of detecting the container engine and container ID relies on implementation details of each container engine.
+ # Although the implementation details have remained unchanged for some time, there is no guarantee they will continue to work.
+ # There have been proposals to create a standard mechanism for this, but none is currently available.
+ # See: https://github.com/opencontainers/runtime-spec/issues/1105
+
+ mounts = MountEntry.loads(mountinfo_path.read_text())
+
+ for mount in mounts:
+ if str(mount.path) == '/etc/hostname':
+ # Podman generates /etc/hostname in the makePlatformBindMounts function.
+ # That function ends up using ContainerRunDirectory to generate a path like: {prefix}/{container_id}/userdata/hostname
+ # NOTE: The {prefix} portion of the path can vary, so should not be relied upon.
+ # See: https://github.com/containers/podman/blob/480c7fbf5361f3bd8c1ed81fe4b9910c5c73b186/libpod/container_internal_linux.go#L660-L664
+ # See: https://github.com/containers/podman/blob/480c7fbf5361f3bd8c1ed81fe4b9910c5c73b186/vendor/github.com/containers/storage/store.go#L3133
+ # This behavior has existed for ~5 years and was present in Podman version 0.2.
+ # See: https://github.com/containers/podman/pull/248
+ if match := re.search('/(?P<id>[0-9a-f]{64})/userdata/hostname$', str(mount.root)):
+ container_id = match.group('id')
+ engine = 'Podman'
+ break
- if os.path.exists(path):
- # File content varies based on the environment:
- # No Container: /
- # Docker: /docker/c86f3732b5ba3d28bb83b6e14af767ab96abbc52de31313dcb1176a62d91a507
- # Azure Pipelines (Docker): /azpl_job/0f2edfed602dd6ec9f2e42c867f4d5ee640ebf4c058e6d3196d4393bb8fd0891
- # Podman: /../../../../../..
- contents = read_text_file(path)
-
- cgroup_path, cgroup_name = os.path.split(contents.strip())
-
- if cgroup_path in ('/docker', '/azpl_job'):
- container_id = cgroup_name
+ # Docker generates /etc/hostname in the BuildHostnameFile function.
+ # That function ends up using the containerRoot function to generate a path like: {prefix}/{container_id}/hostname
+ # NOTE: The {prefix} portion of the path can vary, so should not be relied upon.
+ # See: https://github.com/moby/moby/blob/cd8a090e6755bee0bdd54ac8a894b15881787097/container/container_unix.go#L58
+ # See: https://github.com/moby/moby/blob/92e954a2f05998dc05773b6c64bbe23b188cb3a0/daemon/container.go#L86
+ # This behavior has existed for at least ~7 years and was present in Docker version 1.0.1.
+ # See: https://github.com/moby/moby/blob/v1.0.1/daemon/container.go#L351
+ # See: https://github.com/moby/moby/blob/v1.0.1/daemon/daemon.go#L133
+ if match := re.search('/(?P<id>[0-9a-f]{64})/hostname$', str(mount.root)):
+ container_id = match.group('id')
+ engine = 'Docker'
+ break
if container_id:
- display.info('Detected execution in Docker container: %s' % container_id, verbosity=1)
+ display.info(f'Detected execution in {engine} container ID: {container_id}', verbosity=1)
return container_id