Skip to content

Commit 1766f5f

Browse files
committed
Move container-hotplug from wrapper of docker to wrapper of runc
This means: * It can now be used for other container managers, e.g. podman or k8s * We now only start the container when existing devices are attached * We no longer need to interface with docker API, and no longer have to worry about lifecycle of the pod. The options is changed from using CLI to use annotations.
1 parent 19a489c commit 1766f5f

File tree

14 files changed

+383
-1313
lines changed

14 files changed

+383
-1313
lines changed

Cargo.lock

+1-686
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,17 @@ anyhow = { version = "1", features = ["backtrace"] }
1111
log = "0.4"
1212
env_logger = "0.11"
1313
clap = { version = "4", features = ["derive"] }
14-
bytes = "1"
1514
thiserror = "1"
1615
tokio = { version = "1", features = ["full"] }
1716
tokio-stream = "0.1"
18-
tokio-util = { version = "0.7", features = ["full"] }
1917
async-stream = "0.3"
2018
udev = "0.8"
21-
bollard = "0.16"
22-
rustix = { version = "0.38", features = ["fs", "stdio", "termios", "process", "thread"] }
19+
rustix = { version = "0.38", features = ["fs", "stdio", "process", "thread", "runtime", "pipe"] }
2320
bitflags = "2"
2421
once_cell = "1"
2522
humantime = "2"
2623
serde = { version = "1", features = ["derive"] }
2724
serde_json = "1"
28-
2925
aya = { git = "https://github.com/aya-rs/aya.git" }
3026

3127
[build-dependencies]

README.md

+67-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# container-hotplug
22

3-
Hot-plug (and unplug) devices into a Docker container as they are (un)plugged.
3+
Hot-plug (and unplug) devices into a container as they are (un)plugged.
44

55
## Description
66

@@ -18,32 +18,82 @@ It then interfaces directly with the container's cgroup to grant it access to th
1818
To limit the devices the container can access, a _root device_ is specified.
1919
The container will receive access to any device descending from the root device.
2020
This is particularly useful if the root device is set to a USB hub.
21-
However, since hubs are rarely interesting, it can be specified as "the parent of device X",
21+
The hub can be specified directly, or it can be specified as "the parent of device X",
2222
e.g., we can giving a container access to all devices connected to the same hub as an Arduino board.
2323

2424
Another concern is providing a container with well known paths for the devices.
2525
On bare-metal systems this would usually be achieved with a `SYMLINK` directive in a udev rule.
2626
This program tries to provide a similar functionality for containers, allowing you to specify symlinks for certain devices.
2727

28-
This tool supports both cgroup v1 and v2.
28+
## Usage
2929

30-
## Example
30+
This tool wraps `runc` command, with the additional hotplug feature. Therefore, it can be used as a drop in replace for
31+
many container managers/orchestrators that makes use of runc as runtime. You need to ensure `runc` is available in your `PATH`
32+
so `container-hotplug` can find it.
3133

32-
Give a container access to all devices connected to the same hub as a CW310 board.
34+
It supports two annotations, `org.lowrisc.hotplug.device` and `org.lowrisc.hotplug.symlinks`.
3335

34-
1. Find the USB VID and PID of the device using `lsusb`, for a CW310 that is `2b3e:c310`
35-
2. Run (as root) the container using `container-hotplug`:
36+
For Docker, you can specify an alternative runtime by [changing /etc/docker/daemon.json](https://docs.docker.com/engine/alternative-runtimes/#youki):
37+
```json
38+
{
39+
"runtimes": {
40+
"hotplug": {
41+
"path": "/path/to/container-hotplug/binary"
42+
}
43+
}
44+
}
3645
```
37-
container-hotplug run \
38-
-d parent-of:usb:2b3e:c310 \
39-
-- -it ubuntu:22.04 bash
46+
and use it by `--runtime hotplug` and appropriate annotation, e.g.
47+
```bash
48+
sudo docker run --runtime hotplug -it --annotation org.lowrisc.hotplug.device=parent-of:usb:2b2e:c310 ubuntu:latest
4049
```
4150

42-
If you want symlinks to the `tty` devices created by interfaces 1 and 3 of the CW310, run:
51+
For podman, you can specify the path directly, by:
52+
```bash
53+
sudo podman run --runtime /path/to/container-hotplug/binary -it --annotation org.lowrisc.hotplug.device=parent-of:usb:2b2e:c310 ubuntu:latest
4354
```
44-
container-hotplug run \
45-
-d parent-of:usb:2b3e:c310 \
46-
-l usb:2b3e:c310:1=/dev/ttyACM_CW310_0 \
47-
-l usb:2b3e:c310:3=/dev/ttyACM_CW310_1 \
48-
-- -it ubuntu:22.04 bash
49-
```
55+
56+
For containerd (e.g. when using kubernetes), you can `/etc/containerd/config.toml` to add:
57+
```toml
58+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.hotplug]
59+
runtime_type = "io.containerd.runc.v2"
60+
pod_annotations = ["org.lowrisc.hotplug.*"]
61+
62+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.hotplug.options]
63+
SystemdCgroup = true
64+
BinaryName = "/path/to/container-hotplug/binary"
65+
```
66+
this would allow you to use `hotplug` as handler in k8s, e.g. add a runtime class with
67+
```yaml
68+
apiVersion: node.k8s.io/v1
69+
kind: RuntimeClass
70+
metadata:
71+
name: hotplug
72+
handler: hotplug
73+
```
74+
and use it in pod with
75+
```yaml
76+
apiVersion: v1
77+
kind: Pod
78+
metadata:
79+
name: ubuntu
80+
annotations:
81+
org.lowrisc.hotplug.device: usb:0bda:5634
82+
spec:
83+
runtimeClassName: hotplug
84+
containers:
85+
- name: ubuntu
86+
image: ubuntu:latest
87+
stdin: true
88+
tty: true
89+
```
90+
91+
If you want symlinks to the `tty` devices created by interfaces 1 and 3 of the CW310, add
92+
```
93+
--annotation org.lowrisc.hotplug.symlinks=usb:2b3e:c310:1=/dev/ttyACM_CW310_0,usb:2b3e:c310:3=/dev/ttyACM_CW310_1
94+
```
95+
to docker/podman command line or
96+
```
97+
org.lowrisc.hotplug.symlinks: usb:2b3e:c310:1=/dev/ttyACM_CW310_0,usb:2b3e:c310:3=/dev/ttyACM_CW310_1
98+
```
99+
to k8s config.

src/cli/mod.rs

-43
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,5 @@
11
pub mod device;
22
pub mod symlink;
33

4-
use clap::{Parser, Subcommand};
5-
64
pub use device::DeviceRef;
75
pub use symlink::Symlink;
8-
9-
#[derive(Parser)]
10-
pub struct Args {
11-
#[command(subcommand)]
12-
pub action: Action,
13-
}
14-
15-
#[derive(Subcommand)]
16-
#[command(max_term_width = 180)]
17-
pub enum Action {
18-
/// Wraps a call to `docker run` to allow hot-plugging devices into a
19-
/// container as they are plugged
20-
Run(Run),
21-
}
22-
23-
#[derive(clap::Args)]
24-
pub struct Run {
25-
#[arg(short = 'd', long, id = "DEVICE")]
26-
/// Root hotplug device: [[parent-of:]*]<PREFIX>:<DEVICE> {n}
27-
/// PREFIX can be: {n}
28-
/// - usb: A USB device identified as <VID>[:<PID>[:<SERIAL>]] {n}
29-
/// - syspath: A directory path in /sys/** {n}
30-
/// - devnode: A device path in /dev/** {n}
31-
/// e.g., parent-of:usb:2b3e:c310
32-
pub root_device: DeviceRef,
33-
34-
#[arg(short = 'l', long, id = "SYMLINK")]
35-
/// Create a symlink for a device: <PREFIX>:<DEVICE>=<PATH> {n}
36-
/// PREFIX can be: {n}
37-
/// - usb: A USB device identified as <VID>:<PID>:<INTERFACE> {n}
38-
/// e.g., usb:2b3e:c310:1=/dev/ttyACM_CW310_0
39-
pub symlink: Vec<Symlink>,
40-
41-
#[arg(short = 'u', long, default_value = "5", id = "CODE")]
42-
/// Exit code to return when the root device is unplugged
43-
pub root_unplugged_exit_code: u8,
44-
45-
#[arg(trailing_var_arg = true, id = "ARGS")]
46-
/// Arguments to pass to `docker run`
47-
pub docker_args: Vec<String>,
48-
}

0 commit comments

Comments
 (0)