new build things
5.8 KiB
🤖 AI Agent & Developer Reference Guide (AGENTS.md)
Welcome! This guide provides a detailed overview of the MikroTik Exporter project structure, design patterns, architecture, and extension instructions. It is designed to help both human developers and agentic AI systems quickly understand and maintain the codebase.
📌 Project Overview
The MikroTik Exporter is a Prometheus exporter written in Go that gathers telemetry and health statistics from MikroTik devices running RouterOS. It communicates with RouterOS devices concurrently via the RouterOS API and exposes these metrics over HTTP in the Prometheus exposition format.
🏗️ Architecture & Module Structure
graph TD
A[main.go Entrypoint] --> B[config/config.go]
A --> C[collector/collector.go Core]
C --> D[routerOSCollector Interface]
D --> E[Sub-collectors: resource, interface, bgp, dhcp, etc.]
C --> F[RouterOS API Client]
E --> F
F --> G[MikroTik Devices / RouterOS]
1. Entrypoint (main.go)
- Parses command-line flags (e.g. log formats, port, config file path).
- Loads targets/devices and feature toggles from
config. - Instantiates the core Prometheus collector and registers it.
- Serves the
/metricsendpoint using Prometheus's official HTTP handler.
2. Configuration (config/)
config.go: Parses the YAML configuration file (deviceslist and customfeatures).- Supports specifying individual devices with
address,srvrecord lookups,user,password,port, andwifiwave2tags.
3. Core Collector Engine (collector/)
collector.go: Implements theprometheus.Collectorinterface (DescribeandCollect).- Manages concurrent scraping across all configured devices using a
sync.WaitGroupand Go routines. - Dynamically builds the active sub-collector list based on enabled features.
- Automatically spins up connection clients (via
gopkg.in/routeros.v2) per device scrape.
- Manages concurrent scraping across all configured devices using a
routeros_collector.go: Defines therouterOSCollectorinterface:type routerOSCollector interface { describe(ch chan<- *prometheus.Desc) collect(ctx *collectorContext) error }collector_context.go: Passes thread-safe context (prometheus.Metricchannel, current device config, and authenticatedrouteros.Client) to each sub-collector.
4. Specialized Sub-Collectors (collector/*_collector.go)
Every metrics category has its own implementation of the routerOSCollector interface. Examples include:
resource_collector.go: Queries CPU, memory, uptime, disk space, and OS version from/system/resource/print.interface_collector.go: Queries bytes, packets, and errors per interface from/interface/print.bgp_collector.go: Gathers BGP session states from/routing/bgp/peer/print.dhcp_collector.go/dhcp_lease_collector.go: Gathers lease counts, active leases, and pools.
🛠️ How to Add a New Sub-Collector
To add support for a new RouterOS statistics category:
-
Update the Configuration: In
config/config.go, add your new feature flag (in camel-case/lowercase YAML tag matching standard conventions):MyFeature bool `yaml:"myfeature,omitempty"` -
Define the Collector: Create a new file under
collector/myfeature_collector.go. Implement therouterOSCollectorinterface:package collector import ( "github.com/prometheus/client_golang/prometheus" "gopkg.in/routeros.v2/proto" ) type myFeatureCollector struct { description *prometheus.Desc } func newMyFeatureCollector() routerOSCollector { return &myFeatureCollector{ description: prometheus.NewDesc( "mikrotik_myfeature_metric_value", "Description of my new feature metric", []string{"device"}, nil, ), } } func (c *myFeatureCollector) describe(ch chan<- *prometheus.Desc) { ch <- c.description } func (c *myFeatureCollector) collect(ctx *collectorContext) error { // Run the RouterOS command reply, err := ctx.client.Run("/interface/myfeature/print") if err != nil { return err } // Parse and expose metric for _, re := range reply.Re { val := parseVal(re.Map["some-property"]) ctx.ch <- prometheus.MustNewConstMetric(c.description, prometheus.GaugeValue, val, ctx.device.Name) } return nil } -
Register the Option: In
collector/collector.go, expose an option function:func WithMyFeature() Option { return func(c *collector) { c.collectors = append(c.collectors, newMyFeatureCollector()) } } -
Hook into Main: In
main.go, add conditional registration when parsing the loaded config features:if cfg.Features.MyFeature { opts = append(opts, collector.WithMyFeature()) }
🚀 CI/CD & Compilation (ko)
This project has been migrated from CircleCI to GitHub Actions with an optimized multi-arch containerization flow powered by ko:
- Workflow File:
.github/workflows/ci.yml - Compiler Config:
.ko.yaml - Build Targets:
linux/amd64andlinux/arm64cross-compiled directly via Go toolchain without needing local Docker engines or QEMU. - Image Repository: Published automatically to GitHub Container Registry (
ghcr.io/nshttpd/mikrotik-exporter). - Tagging Convention:
trunkbranch: Tagged with the short Git commit SHA (e.g.ab12cd3).- Releases matching
v*: Tagged with the exact semantic tag version (e.g.v1.2.3).
- Embedded Metadata: Application version metadata (
appVersionandshortSha) are injected intomain.gopackage variables during container builds vialdflagsdynamically set in.ko.yaml.