Skip to content

Contributing

This guide covers the development workflow, tooling, and conventions for contributing to opnsense2otel.

This page is the canonical contribution guide. The repository root CONTRIBUTING.md is a short pointer to this page — edit only this file; do not duplicate content into the root file.

Prerequisites

  • Go - Check go.mod for the required version (currently Go 1.26)
  • Make - For build automation
  • golangci-lint - Optional; runs in CI but can be used locally
  • Docker - Optional; for container builds

Getting started

Clone the repository:

git clone https://github.com/rknightion/opnsense2otel.git
cd opnsense2otel

Build commands

Command Description
make Build the binary (static, version-embedded)
make test Run all tests: go test ./...
make lint Run gofmt and golangci-lint --fix
make sync-vendor go mod tidy && go mod vendor
make clean Format source and remove the binary

Building on macOS

The Makefile uses -extldflags "-static" which does not work on macOS. Use this instead:

CGO_ENABLED=0 go build -o opnsense2otel .

Running a single test

go test ./internal/collector/ -run TestCollector
go test ./opnsense/ -run TestFetchGateways

Project conventions

Vendor directory

The vendor directory is committed to the repository. Always run make sync-vendor after modifying go.mod:

make sync-vendor

Static binary

The build produces a fully static binary with:

  • CGO_ENABLED=0
  • -ldflags "-s -w" for stripped, optimized output
  • -trimpath and -mod=vendor for reproducibility

Version

The version is embedded at build time via -ldflags -X main.version=.... The version.txt file at the repository root is managed by release-please and tracks the current released version; GoReleaser embeds the git tag as the version in release builds, while local make builds embed local-test.

Linters

The project uses golangci-lint with:

  • misspell and revive enabled
  • unused disabled

Linting runs in CI. Locally, make lint will run gofmt formatting (the golangci-lint step may fail if the tool is not installed, which is expected).

Commit messages

This project uses conventional commits for automated changelog generation via release-please:

feat(collector): add new subsystem collector
fix(kea): handle disabled DHCP service response
docs: update README with new collector descriptions
refactor: modernize Go syntax patterns

Changelog

Release history lives entirely in CHANGELOG.md, generated by release-please from conventional commit messages. There is no separate "changes from upstream" section to maintain — do not add one to README.md; it was deliberately removed and conventional commit messages are what drive the changelog instead.

Pull request checklist

Before submitting a PR, the local equivalent of everything ci-success requires:

  • Code compiles: make or CGO_ENABLED=0 go build
  • Tests pass: go test ./... (make test)
  • Tests pass under the race detector: go test -race ./...
  • Linters pass: golangci-lint run ./... (make lint runs gofmt plus golangci-lint --fix)
  • Generated docs are current: make docs-check (run make docs first if it fails, then commit the regenerated files — never hand-edit content between <!-- docgen:begin/end --> markers)
  • Grafana dashboard/alerts are current: make grafana-check (run make dashboard and, if alert rules changed, make rules, then commit the regenerated files — see grafana/README.md)
  • Vendor is synced (if dependencies changed): make sync-vendor
  • Conventional commit messages used
  • New collectors follow the adding a collector guide

CI additionally verifies the container image builds and embeds the correct version (docker-build-verify); this can optionally be reproduced locally with docker build . but is not required before opening a PR.

Project structure

.
+-- main.go                      # Entry point
+-- internal/
|   +-- collector/               # Prometheus collectors
|   |   +-- collector.go         # Top-level collector, interface
|   |   +-- arp_table.go         # Per-subsystem collector files
|   |   +-- gateways.go
|   |   +-- ...
|   +-- options/                 # CLI flags and configuration
|   |   +-- ops.go               # OPNsense connection config
|   |   +-- exporter.go          # Server config
|   |   +-- collectors.go        # Collector enable/disable switches
|   +-- logship/                 # Syslog + Zenarmor receivers, log enrichment, OTLP log sink
|   |   +-- syslog/              # Per-program syslog parsers
|   |   +-- zenarmor/            # Zenarmor HTTP receiver
|   |   +-- enrich/              # Device/service enrichment
|   |   +-- flowlog/             # Sink for internal/flow's correlated flow logs
|   |   +-- ...
|   +-- flow/                    # NetFlow receiver, rollup, correlator
|   |   +-- netflow/             # NetFlow wire decode + listener
|   |   +-- correlate.go         # NetFlow + Zenarmor conn merge
|   |   +-- ...
|   +-- webui/                   # Operator console served at /
|   |   +-- server.go            # Route registration, self-registering tabs
|   |   +-- status.go
|   |   +-- ...
|   +-- metricsnap/              # Passive metric capture, teed at both scrape sites
|       +-- recorder.go
+-- opnsense/                    # API client
|   +-- client.go                # HTTP client, TLS, retries
|   +-- gateways.go              # Per-subsystem Fetch methods
|   +-- ...
+-- deploy/                      # Deployment manifests
|   +-- k8s/                     # Kubernetes manifests
+-- grafana/                     # Grafana dashboard builder, panels, alert rules
+-- docs/                        # Documentation (this site)
+-- vendor/                      # Vendored dependencies