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
38 changes: 34 additions & 4 deletions clamav-exporter/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
FROM python:3.11-slim
# Build stage
FROM golang:1.21-alpine AS builder

WORKDIR /app

COPY exporter.py /app/
# Install build dependencies
RUN apk add --no-cache git

RUN chmod +x /app/exporter.py
# Copy go mod files
COPY go.mod ./

# Download dependencies
RUN go mod download

# Copy source code
COPY main.go ./

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o clamav-exporter .

# Runtime stage
FROM alpine:latest

RUN apk --no-cache add ca-certificates

# Create a non-root user and group
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copy the binary from builder and set ownership
COPY --from=builder --chown=appuser:appgroup /app/clamav-exporter .

# Switch to non-root user
USER appuser

# Expose exporter port
EXPOSE 9810

CMD ["python3", "/app/exporter.py"]
# Run the service
CMD ["./clamav-exporter"]
22 changes: 22 additions & 0 deletions clamav-exporter/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.PHONY: build run test vet clean docker-build

BINARY_NAME=clamav-exporter
DOCKER_IMAGE=opengovmail-clamav-exporter

build:
go build -o $(BINARY_NAME) main.go

run:
go run main.go

test:
go test ./...

vet:
go vet ./...

clean:
rm -f $(BINARY_NAME)

docker-build:
docker build -t $(DOCKER_IMAGE) .
78 changes: 78 additions & 0 deletions clamav-exporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ClamAV Prometheus Exporter (Go)

Go port of `exporter.py`. Tracks `OpenGovMail/OpenGovMail#291`.

## Why Go

Single large Python file is harder to maintain and heavier at runtime.
This port keeps exact metric parity and removes the `python:3.11-slim`
dependency (static binary on `alpine`, non-root, ~27MB vs ~150MB base).

## Metrics parity with exporter.py

Endpoints:

- `GET /metrics` -> `200 text/plain; version=0.0.4`, 13 metrics
- `GET /health` -> `200 OK`
- anything else -> `404`

Metrics: `clamav_up`, `clamav_state`, `clamav_pools_total`,
`clamav_threads_live`, `clamav_threads_idle`, `clamav_queue_items`,
`clamav_memory_heap_mb`, `clamav_memory_used_mb`, `clamav_signature_version`,
`clamav_version{version="x.y.z"}`, `clamav_viruses_found_total`,
`clamav_files_scanned_total`, `clamav_database_updated`.

Connection behaviour matches Python: probe Unix sockets
`/var/run/clamav/clamd.ctl`, `clamd.sock`, `clamd.socket`, `/tmp/clamd.socket`,
fallback to TCP `CLAMAV_TCP_HOST:CLAMAV_TCP_PORT` (`clamav:3310`), `z<CMD>\0`
protocol, 5s timeout. Logs: `/var/log/clamav/clamd.log` and `freshclam.log`
(same fallbacks as Python).

## Build and run

```bash
go build -o clamav-exporter main.go
STARTUP_DELAY_SECONDS=0 ./clamav-exporter
curl -s localhost:9810/health
curl -s localhost:9810/metrics
```

Environment:

| Variable | Default | Purpose |
|---|---|---|
| `CLAMAV_TCP_HOST` | `clamav` | TCP fallback host |
| `CLAMAV_TCP_PORT` | `3310` | TCP fallback port |
| `EXPORTER_PORT` | `9810` | HTTP listen port |
| `STARTUP_DELAY_SECONDS` | `10` | Wait for ClamAV, set `0` for tests |

Tests:

```bash
go test ./...
go vet ./...
```

Docker (same build context as before, compose unchanged):

```bash
docker build -t opengovmail-clamav-exporter .
docker run --rm -p 9810:9810 \
-v clamav_logs:/var/log/clamav:ro \
opengovmail-clamav-exporter
```

## Memory comparison (measured 2026-09-10, no ClamAV backend reachable)

Method: host process RSS via `ps -o rss` idle after one `/metrics` scrape,
then again after 50 consecutive scrapes. Both exporters in the same
no-backend condition (connection failures logged, metrics still served).

| Metric | Python (`exporter.py` on `python3`) | Go (this exporter) |
|---|---|---|
| Docker image size | `python:3.11-slim` base 198MB | `opengovmail-clamav-exporter` 27.1MB |
| Process RSS idle | 23,512 KB (~23.0 MB) | 10,084 KB (~9.8 MB) |
| Process RSS after 50 scrapes | 23,512 KB (~23.0 MB) | 10,912 KB (~10.7 MB) |

Result: Go uses ~57% less resident memory and the image is ~86% smaller.
No GNU `/usr/bin/time` on the test host, so `ps` RSS was used instead.
3 changes: 3 additions & 0 deletions clamav-exporter/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/lsflk/opengovmail-clamav-exporter

go 1.21
Loading