From eb07c3c58118644da4c076549d5951563611b3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 17 Dec 2020 10:20:22 +0100 Subject: [PATCH] add containerd support --- README.md | 45 +++++++++++++++++++++++ src/bin/main.rs | 2 +- src/container/containerd.rs | 71 +++++++++++++++++++++++++++++++++++++ src/container/mod.rs | 4 +++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/container/containerd.rs diff --git a/README.md b/README.md index 2f3a8754..b936bc81 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ In this two minute recording you learn all the basics of cntr: * LXD * rkt * systemd-nspawn + * containerd - For other container engines cntr also takes process ids (PIDs) instead of container names. ## Installation @@ -304,6 +305,50 @@ drwx------ 22 nobody nogroup 43 Mar 13 15:09 root ... ``` +### Containerd + +For containerd integration the `ctr` binary is required. You can get a binary by running: + +``` console +$ GOPATH=$(mktemp -d) +$ go get github.com/containerd/containerd/cmd/ctr +$ $GOPATH/bin/ctr --help +``` + +Put the resulting `ctr` binary in your `$PATH` + +1: Start container +```console +$ ctr images pull docker.io/library/busybox:latest +$ ctr run docker.io/library/busybox:latest boxbusy +$ ctr tasks lists +TASK PID STATUS +boxbusy 24310 RUNNING +``` + +2: Attach +```console +$ cntr attach boxbusy +``` + +It's also possible to run cntr from a container itself. +This repository contains a example Dockerfile for that: + +```console +$ docker build -f Dockerfile.example . -t cntr +$ docker save cntr > cntr.tar +$ ctr images import --base-name cntr ./cntr.tar +``` + +In this example we attach to containerd by process id. The proccess id of a task is given in `ctr tasks list`. + +```console +$ ctr run --privileged --with-ns pid:/proc/1/ns/pid --tty docker.io/library/cntr:latest cntr /usr/bin/cntr attach 31523 /bin/sh +``` + +To resolve containerd names one also would need to add the `ctr` binary (~12mb) to the Dockerfile. + + # How it works Cntr is container-agnostic: Instead of interfacing with container engines, it diff --git a/src/bin/main.rs b/src/bin/main.rs index 23aca0b7..9c77f08c 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -50,7 +50,7 @@ fn parse_attach_args(args: Vec) -> cntr::AttachOptions { ap.refer(&mut container_type).add_option( &["-t", "--type"], Store, - "Container type (process_id|rkt|docker|nspawn|lxc|lxd|command), default: all except command)", + "Container type (process_id|rkt|docker|nspawn|lxc|lxd|command|containerd), default: all except command)", ); ap.refer(&mut container_name).required().add_argument( "id", diff --git a/src/container/containerd.rs b/src/container/containerd.rs new file mode 100644 index 00000000..3dfe76ea --- /dev/null +++ b/src/container/containerd.rs @@ -0,0 +1,71 @@ +use libc::pid_t; +use nix::unistd::Pid; +use std::process::Command; + +use crate::cmd; +use crate::container::Container; +use crate::types::{Error, Result}; + +#[derive(Clone, Debug)] +pub struct Containerd {} + +impl Container for Containerd { + fn lookup(&self, container_id: &str) -> Result { + let command = format!("ctr task list"); + let output = tryfmt!( + Command::new("ctr").args(&["task", "list"]).output(), + "Running '{}' failed", + command + ); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return errfmt!(format!( + "Failed to list containers. '{}' exited with {}: {}", + command, + output.status, + stderr.trim_end() + )); + } + + // $ ctr task list + // TASK PID STATUS + // v2 17515 RUNNING + // v1 14602 RUNNING + let mut lines = output.stdout.split(|&c| c == b'\n'); + lines.next(); // skip header + let pid_str = lines.find_map(|line| { + let line_str = String::from_utf8_lossy(&line); + let cols = line_str.split_whitespace().collect::>(); + if cols.len() != 3 { + return None; + } + + if cols[0] == container_id { + Some(String::from(cols[1])) + } else { + None + } + }); + match pid_str { + Some(pid_str) => { + let pid = tryfmt!( + pid_str.parse::(), + "read invalid pid from ctr task list: '{}'", + pid_str + ); + Ok(Pid::from_raw(pid)) + } + None => { + errfmt!(format!("No container with id {} found", container_id)) + } + } + } + fn check_required_tools(&self) -> Result<()> { + if cmd::which("ctr").is_some() { + Ok(()) + } else { + errfmt!("ctr not found") + } + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index 40333748..062bd590 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -4,6 +4,7 @@ use std::fmt::Debug; use crate::types::{Error, Result}; mod command; +mod containerd; mod docker; mod lxc; mod lxd; @@ -24,6 +25,7 @@ pub const AVAILABLE_CONTAINER_TYPES: &[&str] = &[ "lxc", "lxd", "command", + "containerd", ]; fn default_order() -> Vec> { @@ -34,6 +36,7 @@ fn default_order() -> Vec> { Box::new(nspawn::Nspawn {}), Box::new(lxc::Lxc {}), Box::new(lxd::Lxd {}), + Box::new(containerd::Containerd {}), ]; containers .into_iter() @@ -49,6 +52,7 @@ pub fn lookup_container_type(name: &str) -> Option> { "nspawn" => Box::new(nspawn::Nspawn {}), "lxc" => Box::new(lxc::Lxc {}), "lxd" => Box::new(lxd::Lxd {}), + "containerd" => Box::new(containerd::Containerd {}), "command" => Box::new(command::Command {}), _ => return None, })