Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Top-most EditorConfig file
root = true

[*]
indent_style = tab
indent_size = 4
14 changes: 7 additions & 7 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
- "v*.*.*"
pull_request:
branches:
- "main"
- "master"

jobs:
push-store-image:
Expand All @@ -21,25 +21,25 @@ jobs:

steps:
- name: "Checkout Repository"
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: "Login to GitHub Container Registry"
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}

- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3

- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{github.actor}}/${{github.repository}}
tags: |
Expand All @@ -52,7 +52,7 @@ jobs:
type=sha

- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@

FROM golang:1.19

ENV DEBIAN_FRONTEND noninteractive
ENV DEBIAN_FRONTEND=noninteractive

WORKDIR /go/src/github.com/allfro/device-volume-driver

COPY . .

RUN CGO_ENABLED=1 GOOS=linux go build -ldflags "-linkmode external -extldflags -static" -o /dvd

FROM alpine
FROM docker:latest

RUN apk add bash

WORKDIR /

COPY --from=0 /dvd /dvd
COPY /entrypoint.sh /entrypoint.sh

ENTRYPOINT ["/dvd"]

ENTRYPOINT ["/entrypoint.sh"]
18 changes: 3 additions & 15 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
version: "3.8"


services:
dmm:
image: docker
entrypoint: docker
command: |
run
-i
--name device-manager
--restart always
--privileged
--cgroupns=host
--pid=host
--userns=host
-v /sys:/host/sys
-v /var/run/docker.sock:/var/run/docker.sock
ndouba/device-mapping-manager
image: ghcr.io/allfro/allfro/device-mapping-manager:nightly
environment:
DOCKER_IAMGE: ghcr.io/allfro/allfro/device-mapping-manager:nightly # Set this to the same as 'image' above
volumes:
- /var/run/docker.sock:/var/run/docker.sock
58 changes: 58 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash

set -e


script_dir=$(cd $(dirname ${BASH_SOURCE[@]}) && pwd)
container_name="swarm-device-manager"


# All printed log lines from this script should be formatted with this function
print_log() {
local timestamp="$(date +'%Y-%m-%d %H:%M:%S %z')"
local pid="$$"
local level="$1"
local message="${@:2}"
echo "[${timestamp}] [${pid}] [${level^^}] ${message}"
}


# Ensure docker socket is available
if [ ! -S /var/run/docker.sock ]; then
print_log "error" "Missing Docker socket. Ensure you run this container mounting '/var/run/docker.sock'. Exit!"
exit 1
fi


# Re-run this as a docker container with elevated privileges
if [ ! -d /host/sys ]; then
# Ensure image version is available
if [ "X${DOCKER_IAMGE}" = "X" ]; then
print_log "error" "Missing required 'DOCKER_IAMGE' variable. Exit!"
exit 1
fi

# Check if container is already running. Stop it
if docker ps --filter "name=^${container_name}$" | grep -q "${container_name}"; then
docker stop "${container_name}" &> /dev/null || true
fi

# Run docker container
print_log "info" "Running privileged container '${container_name}'..."
exec docker run \
--rm \
-i \
--name "${container_name}" \
--privileged \
--cgroupns=host \
--pid=host \
--userns=host \
-v /sys:/host/sys \
-v /var/run/docker.sock:/var/run/docker.sock \
"${DOCKER_IAMGE:?}"
fi


# Run service
print_log "info" "Running main service..."
exec /dvd
127 changes: 79 additions & 48 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,31 @@ func listenForMounts() {

defer cli.Close()

// Scan all running containers on startup
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{All: false})
if err != nil {
log.Fatal(err)
}

for _, container := range containers {
info, err := cli.ContainerInspect(ctx, container.ID)
if err != nil {
log.Println(err)
continue
}

pid := info.State.Pid
version, err := cgroup.GetDeviceCGroupVersion("/", pid)
if err != nil {
log.Println(err)
continue
}

log.Printf("Checking mounts for process %d\n", pid)
processMounts(container.ID, info.Mounts, pid, version)
}

// Monitor container start events
msgs, errs := cli.Events(
ctx,
types.EventsOptions{Filters: filters.NewArgs(filters.Arg("event", "start"))},
Expand All @@ -96,56 +121,62 @@ func listenForMounts() {
}

log.Printf("Checking mounts for process %d\n", pid)
processMounts(msg.Actor.ID, info.Mounts, pid, version)
}
}
}
}

for _, mount := range info.Mounts {
log.Printf(
"%s/%v requested a volume mount for %s at %s\n",
msg.Actor.ID, info.State.Pid, mount.Source, mount.Destination,
)

if !strings.HasPrefix(mount.Source, "/dev") {
log.Printf("%s is not a device... skipping\n", mount.Source)
continue
}

api, err := cgroup.New(version)
cgroupPath, sysfsPath, err := api.GetDeviceCGroupMountPath("/", pid)

if err != nil {
log.Println(err)
break
}

cgroupPath = path.Join(rootPath, sysfsPath, cgroupPath)

log.Printf("The cgroup path for process %d is at %v\n", pid, cgroupPath)

if fileInfo, err := os.Stat(mount.Source); err != nil {
log.Println(err)
continue
} else {
if fileInfo.IsDir() {
err := filepath.Walk(mount.Source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if info.IsDir() {
return nil
} else if err = applyDeviceRules(api, path, cgroupPath, pid); err != nil {
log.Println(err)
}
return nil
})
if err != nil {
log.Println(err)
}
} else {
if err = applyDeviceRules(api, mount.Source, cgroupPath, pid); err != nil {
log.Println(err)
}
}
}
func processMounts(containerId string, mounts []types.MountPoint, pid int, version int) {
api, err := cgroup.New(version)
if err != nil {
log.Println(err)
return
}

for _, mount := range mounts {
log.Printf(
"%s/%v requested a volume mount for %s at %s\n",
containerId, pid, mount.Source, mount.Destination,
)

if !strings.HasPrefix(mount.Source, "/dev") {
log.Printf("%s is not a device... skipping\n", mount.Source)
continue
}

cgroupPath, sysfsPath, err := api.GetDeviceCGroupMountPath("/", pid)
if err != nil {
log.Println(err)
continue
}

cgroupPath = path.Join(rootPath, sysfsPath, cgroupPath)

log.Printf("The cgroup path for process %d is at %v\n", pid, cgroupPath)

if fileInfo, err := os.Stat(mount.Source); err != nil {
log.Println(err)
continue
} else {
if fileInfo.IsDir() {
err := filepath.Walk(mount.Source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if info.IsDir() {
return nil
} else if err = applyDeviceRules(api, path, cgroupPath, pid); err != nil {
log.Println(err)
}
return nil
})
if err != nil {
log.Println(err)
}
} else {
if err = applyDeviceRules(api, mount.Source, cgroupPath, pid); err != nil {
log.Println(err)
}
}
}
Expand Down