Skip to content

Commit 133e19b

Browse files
committed
Use /proc/self/mountinfo as a fallback for docker inspect if using HOSTNAME fails
Fixes #1321.
1 parent 8b27866 commit 133e19b

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

.changes/1485.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"description": "Use `/proc/self/mountinfo` as a fallback for `docker inspect` if using `HOSTNAME` fails",
3+
"issues": [1321],
4+
"type": "changed"
5+
}

src/docker/shared.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,19 +1351,76 @@ pub fn get_image(
13511351
Ok(image)
13521352
}
13531353

1354-
fn docker_read_mount_paths(
1355-
engine: &Engine,
1356-
msg_info: &mut MessageInfo,
1357-
) -> Result<Vec<MountDetail>> {
1358-
let hostname = env::var("HOSTNAME").wrap_err("HOSTNAME environment variable not found")?;
1354+
fn docker_inspect_self_mountinfo(engine: &Engine, msg_info: &mut MessageInfo) -> Result<String> {
1355+
if cfg!(not(target_os = "linux")) {
1356+
eyre::bail!("/proc/self/mountinfo is unavailable when target_os != linux");
1357+
}
1358+
1359+
// The ID for the current Docker container might be in mountinfo,
1360+
// somewhere in a mount root. Full IDs are 64-char hexadecimal
1361+
// strings, so the first matching path segment in a mount root
1362+
// containing /docker/ is likely to be what we're looking for. See:
1363+
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
1364+
// https://community.toradex.com/t/15240/4
1365+
let mountinfo = file::read("/proc/self/mountinfo")?;
1366+
let container_id = mountinfo
1367+
.lines()
1368+
.filter_map(|s| s.split(' ').nth(3))
1369+
.filter(|s| s.contains("/docker/"))
1370+
.flat_map(|s| s.split('/'))
1371+
.find(|s| s.len() == 64 && s.as_bytes().iter().all(u8::is_ascii_hexdigit))
1372+
.ok_or_else(|| eyre::eyre!("couldn't find container id in mountinfo"))?;
1373+
1374+
engine
1375+
.subcommand("inspect")
1376+
.arg(container_id)
1377+
.run_and_get_stdout(msg_info)
1378+
}
13591379

1360-
let mut docker: Command = {
1380+
fn docker_inspect_self(engine: &Engine, msg_info: &mut MessageInfo) -> Result<String> {
1381+
// Try to find the container ID by looking at HOSTNAME, and fallback to
1382+
// parsing `/proc/self/mountinfo` if HOSTNAME is unset or if there's no
1383+
// container that matches it (necessary e.g. when the container uses
1384+
// `--network=host`, which is act's default, see issue #1321).
1385+
// If `docker inspect` fails with unexpected output, skip the fallback
1386+
// and fail instantly.
1387+
if let Ok(hostname) = env::var("HOSTNAME") {
13611388
let mut command = engine.subcommand("inspect");
13621389
command.arg(hostname);
1363-
command
1364-
};
1390+
let out = command.run_and_get_output(msg_info)?;
13651391

1366-
let output = docker.run_and_get_stdout(msg_info)?;
1392+
if out.status.success() {
1393+
Ok(out.stdout()?)
1394+
} else {
1395+
let val = serde_json::from_slice::<serde_json::Value>(&out.stdout);
1396+
if let Ok(val) = val {
1397+
if let Some(array) = val.as_array() {
1398+
// `docker inspect` completed but returned an empty array, most
1399+
// likely indicating that the hostname isn't a valid container ID.
1400+
if array.is_empty() {
1401+
msg_info.debug("docker inspect found no containers matching HOSTNAME, retrying using mountinfo")?;
1402+
return docker_inspect_self_mountinfo(engine, msg_info);
1403+
}
1404+
}
1405+
}
1406+
1407+
let report = command
1408+
.status_result(msg_info, out.status, Some(&out))
1409+
.expect_err("we know the command failed")
1410+
.to_section_report();
1411+
Err(report)
1412+
}
1413+
} else {
1414+
msg_info.debug("HOSTNAME environment variable is unset")?;
1415+
docker_inspect_self_mountinfo(engine, msg_info)
1416+
}
1417+
}
1418+
1419+
fn docker_read_mount_paths(
1420+
engine: &Engine,
1421+
msg_info: &mut MessageInfo,
1422+
) -> Result<Vec<MountDetail>> {
1423+
let output = docker_inspect_self(engine, msg_info)?;
13671424
let info = serde_json::from_str(&output).wrap_err("failed to parse docker inspect output")?;
13681425
dockerinfo_parse_mounts(&info)
13691426
}
@@ -1674,9 +1731,8 @@ mod tests {
16741731

16751732
let mut msg_info = MessageInfo::default();
16761733
let engine = create_engine(&mut msg_info);
1677-
let hostname = env::var("HOSTNAME");
1678-
if engine.is_err() || hostname.is_err() {
1679-
eprintln!("could not get container engine or no hostname found");
1734+
if engine.is_err() {
1735+
eprintln!("could not get container engine");
16801736
reset_env(vars);
16811737
return Ok(());
16821738
}
@@ -1686,12 +1742,8 @@ mod tests {
16861742
reset_env(vars);
16871743
return Ok(());
16881744
}
1689-
let hostname = hostname.unwrap();
1690-
let output = engine
1691-
.subcommand("inspect")
1692-
.arg(hostname)
1693-
.run_and_get_output(&mut msg_info)?;
1694-
if !output.status.success() {
1745+
let output = docker_inspect_self(&engine, &mut msg_info);
1746+
if output.is_err() {
16951747
eprintln!("inspect failed");
16961748
reset_env(vars);
16971749
return Ok(());

0 commit comments

Comments
 (0)