#![forbid(unsafe_code)]
#![warn(missing_docs, clippy::all)]
pub fn is_docker() -> bool {
#[cfg(target_os = "linux")]
{
if let Some(forced) = env_override() {
return forced;
}
dockerenv_exists()
|| containerenv_exists()
|| mountinfo_indicates_container()
|| cgroup_indicates_container()
}
#[cfg(not(target_os = "linux"))]
{
false
}
}
pub fn is_container() -> bool {
is_docker()
}
#[cfg(target_os = "linux")]
fn env_override() -> Option<bool> {
std::env::var("ISDOCKER")
.ok()
.and_then(|v| parse_env_override(&v))
}
#[cfg(target_os = "linux")]
fn parse_env_override(val: &str) -> Option<bool> {
let lower = val.to_ascii_lowercase();
match lower.as_str() {
"1" | "true" | "yes" => Some(true),
"0" | "false" | "no" => Some(false),
_ => None,
}
}
#[cfg(target_os = "linux")]
fn dockerenv_exists() -> bool {
std::path::Path::new("/.dockerenv").exists()
}
#[cfg(target_os = "linux")]
fn containerenv_exists() -> bool {
std::path::Path::new("/run/.containerenv").exists()
}
#[cfg(target_os = "linux")]
fn mountinfo_indicates_container() -> bool {
match std::fs::read_to_string("/proc/self/mountinfo") {
Ok(contents) => contains_overlay_marker(&contents),
Err(_) => false,
}
}
#[cfg(target_os = "linux")]
fn contains_overlay_marker(contents: &str) -> bool {
contents.contains("/docker/containers/")
|| contents.contains("/var/lib/docker/overlay2/")
|| contents.contains("/var/lib/containers/storage/overlay")
}
#[cfg(target_os = "linux")]
fn cgroup_indicates_container() -> bool {
match std::fs::read_to_string("/proc/self/cgroup") {
Ok(contents) => contains_cgroup_marker(&contents),
Err(_) => false,
}
}
#[cfg(target_os = "linux")]
fn contains_cgroup_marker(contents: &str) -> bool {
contents.contains("docker")
|| contents.contains("kubepods")
|| contents.contains("containerd")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_bool() {
let _ = is_docker();
}
#[test]
fn is_container_matches_is_docker() {
assert_eq!(is_docker(), is_container());
}
#[cfg(target_os = "linux")]
#[test]
fn env_override_truthy() {
assert_eq!(parse_env_override("1"), Some(true));
assert_eq!(parse_env_override("true"), Some(true));
assert_eq!(parse_env_override("TRUE"), Some(true));
assert_eq!(parse_env_override("True"), Some(true));
assert_eq!(parse_env_override("yes"), Some(true));
assert_eq!(parse_env_override("Yes"), Some(true));
assert_eq!(parse_env_override("YES"), Some(true));
}
#[cfg(target_os = "linux")]
#[test]
fn env_override_falsy() {
assert_eq!(parse_env_override("0"), Some(false));
assert_eq!(parse_env_override("false"), Some(false));
assert_eq!(parse_env_override("FALSE"), Some(false));
assert_eq!(parse_env_override("False"), Some(false));
assert_eq!(parse_env_override("no"), Some(false));
assert_eq!(parse_env_override("No"), Some(false));
assert_eq!(parse_env_override("NO"), Some(false));
}
#[cfg(target_os = "linux")]
#[test]
fn env_override_unrecognised() {
assert_eq!(parse_env_override(""), None);
assert_eq!(parse_env_override("maybe"), None);
assert_eq!(parse_env_override("2"), None);
assert_eq!(parse_env_override("docker"), None);
}
#[cfg(target_os = "linux")]
#[test]
fn dockerenv_exists_does_not_panic() {
let _ = dockerenv_exists();
}
#[cfg(target_os = "linux")]
#[test]
fn containerenv_exists_does_not_panic() {
let _ = containerenv_exists();
}
#[cfg(target_os = "linux")]
#[test]
fn overlay_marker_docker_overlay2() {
assert!(contains_overlay_marker(
"36 1 0:30 / / rw - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/abc123/diff"
));
}
#[cfg(target_os = "linux")]
#[test]
fn overlay_marker_docker_containers() {
assert!(contains_overlay_marker(
"100 80 0:50 / /etc/hostname rw - ext4 /dev/sda1 rw /docker/containers/abc123/hostname"
));
}
#[cfg(target_os = "linux")]
#[test]
fn overlay_marker_podman() {
assert!(contains_overlay_marker(
"36 1 0:30 / / rw - overlay overlay rw,lowerdir=/var/lib/containers/storage/overlay/abc/diff"
));
}
#[cfg(target_os = "linux")]
#[test]
fn overlay_marker_negative() {
assert!(!contains_overlay_marker(
"36 1 0:30 / / rw - ext4 /dev/sda1 rw"
));
assert!(!contains_overlay_marker(""));
}
#[cfg(target_os = "linux")]
#[test]
fn cgroup_marker_positive() {
assert!(contains_cgroup_marker("12:blkio:/docker/abc123"));
assert!(contains_cgroup_marker(
"11:cpu:/kubepods/besteffort/pod123"
));
assert!(contains_cgroup_marker(
"10:memory:/containerd/tasks/abc"
));
}
#[cfg(target_os = "linux")]
#[test]
fn cgroup_marker_negative() {
assert!(!contains_cgroup_marker(
"11:cpu:/user.slice/user-1000.slice/session-1.scope"
));
assert!(!contains_cgroup_marker("0::/"));
assert!(!contains_cgroup_marker(""));
}
#[cfg(target_os = "linux")]
#[test]
fn cgroup_indicates_container_does_not_panic() {
let _ = cgroup_indicates_container();
}
#[cfg(target_os = "linux")]
#[test]
fn mountinfo_indicates_container_does_not_panic() {
let _ = mountinfo_indicates_container();
}
#[cfg(not(target_os = "linux"))]
#[test]
fn always_false_on_non_linux() {
assert!(!is_docker());
assert!(!is_container());
}
}