From 5113cc3eeda95601bd434845205faf397244b61e Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Sun, 15 Mar 2026 00:00:33 +0000 Subject: [PATCH] build: disk-collector sidecar and associated workflows --- .github/workflows/build-disk-collector.yml | 51 ++++++++++++++++ .../{docker.yml => build-primary-image.yml} | 2 +- install/sidecar-disk-collector/Dockerfile | 6 ++ .../collect-disk-info.sh | 59 +++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build-disk-collector.yml rename .github/workflows/{docker.yml => build-primary-image.yml} (98%) create mode 100644 install/sidecar-disk-collector/Dockerfile create mode 100755 install/sidecar-disk-collector/collect-disk-info.sh diff --git a/.github/workflows/build-disk-collector.yml b/.github/workflows/build-disk-collector.yml new file mode 100644 index 0000000..7649ba5 --- /dev/null +++ b/.github/workflows/build-disk-collector.yml @@ -0,0 +1,51 @@ +name: Build Disk Collector Image + +on: + workflow_dispatch: + inputs: + version: + description: 'Semantic version to label the Docker image under (no "v" prefix, e.g. "1.2.3")' + required: true + type: string + tag_latest: + description: 'Also tag this image as :latest?' + required: false + type: boolean + default: false + +jobs: + check_authorization: + name: Check authorization to publish new Docker image + runs-on: ubuntu-latest + outputs: + isAuthorized: ${{ steps.check-auth.outputs.is_authorized }} + steps: + - name: check-auth + id: check-auth + run: echo "is_authorized=${{ contains(secrets.DEPLOYMENT_AUTHORIZED_USERS, github.triggering_actor) }}" >> $GITHUB_OUTPUT + build: + name: Build disk-collector image + needs: check_authorization + if: needs.check_authorization.outputs.isAuthorized == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: install/sidecar-disk-collector + push: true + tags: | + ghcr.io/crosstalk-solutions/project-nomad-disk-collector:${{ inputs.version }} + ghcr.io/crosstalk-solutions/project-nomad-disk-collector:v${{ inputs.version }} + ${{ inputs.tag_latest && 'ghcr.io/crosstalk-solutions/project-nomad-disk-collector:latest' || '' }} diff --git a/.github/workflows/docker.yml b/.github/workflows/build-primary-image.yml similarity index 98% rename from .github/workflows/docker.yml rename to .github/workflows/build-primary-image.yml index 1cebccf..daf0e54 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/build-primary-image.yml @@ -1,4 +1,4 @@ -name: Build Docker Image +name: Build Primary Docker Image on: workflow_dispatch: diff --git a/install/sidecar-disk-collector/Dockerfile b/install/sidecar-disk-collector/Dockerfile new file mode 100644 index 0000000..bf22f96 --- /dev/null +++ b/install/sidecar-disk-collector/Dockerfile @@ -0,0 +1,6 @@ +FROM alpine:3.20 +RUN apk add --no-cache util-linux bash +COPY collect-disk-info.sh /usr/local/bin/collect-disk-info.sh +RUN chmod +x /usr/local/bin/collect-disk-info.sh && mkdir -p /storage +WORKDIR /storage +CMD ["/usr/local/bin/collect-disk-info.sh"] diff --git a/install/sidecar-disk-collector/collect-disk-info.sh b/install/sidecar-disk-collector/collect-disk-info.sh new file mode 100755 index 0000000..0fbf805 --- /dev/null +++ b/install/sidecar-disk-collector/collect-disk-info.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Project N.O.M.A.D. - Disk Info Collector Sidecar +# +# Reads host block device and filesystem info via the /:/host:ro,rslave bind-mount. +# No special capabilities required. Writes JSON to /storage/nomad-disk-info.json, which is read by the admin container. +# Runs continually and updates the JSON data every 2 minutes. + +log() { + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" +} + +log "disk-collector sidecar starting..." + +while true; do + + # Get disk layout + DISK_LAYOUT=$(lsblk --sysroot /host --json -o NAME,SIZE,TYPE,MODEL,SERIAL,VENDOR,ROTA,TRAN 2>/dev/null) + if [[ -z "$DISK_LAYOUT" ]]; then + log "WARNING: lsblk --sysroot /host failed, using empty block devices" + DISK_LAYOUT='{"blockdevices":[]}' + fi + + # Get filesystem usage by parsing /host/proc/mounts and running df on each mountpoint + FS_JSON="[" + FIRST=1 + while IFS=' ' read -r dev mountpoint fstype opts _rest; do + # Disregard pseudo and virtual filesystems + [[ "$fstype" =~ ^(tmpfs|devtmpfs|squashfs|sysfs|proc|devpts|cgroup|cgroup2|overlay|nsfs|autofs|hugetlbfs|mqueue|pstore|fusectl|binfmt_misc)$ ]] && continue + [[ "$mountpoint" == "none" ]] && continue + + STATS=$(df -B1 "/host${mountpoint}" 2>/dev/null | awk 'NR==2{print $2,$3,$4,$5}') + [[ -z "$STATS" ]] && continue + + read -r size used avail pct <<< "$STATS" + pct="${pct/\%/}" + + [[ "$FIRST" -eq 0 ]] && FS_JSON+="," + FS_JSON+="{\"fs\":\"${dev}\",\"size\":${size},\"used\":${used},\"available\":${avail},\"use\":${pct},\"mount\":\"${mountpoint}\"}" + FIRST=0 + done < /host/proc/mounts + FS_JSON+="]" + + # Use a tmp file for atomic update + cat > /storage/nomad-disk-info.json.tmp << EOF +{ +"diskLayout": ${DISK_LAYOUT}, +"fsSize": ${FS_JSON} +} +EOF + + if mv /storage/nomad-disk-info.json.tmp /storage/nomad-disk-info.json; then + log "Disk info updated successfully." + else + log "ERROR: Failed to move temp file to /storage/nomad-disk-info.json" + fi + + sleep 120 +done