This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Prometheus exporter that fetches product version End-of-Life (EOL) information from the endoflife.date API and exposes it as Prometheus metrics. Data is fetched on-demand when Prometheus scrapes the /metrics endpoint.
- Go 1.26+
- Task for build automation
oapi-codegen(installed viatask install)
# Install dependencies and tools
task install
# Run the application locally
task run # Uses sample.config.yml, console logging
# Testing
task test # Run all tests
go test ./internal/config # Run tests for specific package
go test -v ./... # Verbose test output
# Code quality
task fmt # Format all Go code
task vet # Run go vet
task lint # Run golangci-lint
task security # Run govulncheck for vulnerabilities
task all # Run fmt, lint, vet, security, gen, test
# Build
task build # Build for current platform (output: dist/endoflife_exporter)
task build-platforms # Build for linux/darwin on amd64/arm64
task build-docker # Build Docker image
# Code generation
task gen # Regenerate Go types from OpenAPI specThe app reads sample.config.yml by default when using task run. Override with environment variables:
CONFIG_FILE=my-config.yml LOG_LEVEL=debug task runAccess metrics at http://localhost:8080/metrics
- Prometheus scrapes
/metricsendpoint collector.Exporter.Collect()is invoked- For each product in config:
- If
all_releases: true→ fetch all release cycles viaGetProductDetails() - Otherwise → fetch specific releases via
GetRelease()for each listed release
- If
- API responses converted to
ReleaseDetailsstructs - Four Prometheus metrics emitted per release cycle
main.go
- Entry point: sets up HTTP server, Prometheus registry, graceful shutdown
- Uses
kongfor CLI parsing,slogfor structured logging - Registers
collector.Exporteras Prometheus collector
internal/collector/collector.go
- Implements
prometheus.Collectorinterface - Orchestrates API calls per configured product
- Emits four metric types (product_info, latest_version_timestamp, release_cycle_timestamp, eol_from_timestamp)
internal/config/config.go
- Loads YAML configuration
- Defaults to
["latest"]release if neitherall_releasesnorreleasesspecified - Warns if both
all_releasesandreleasesare set (ignoresreleases)
pkg/endoflife/endoflife.go
- HTTP client for endoflife.date API
- Two endpoints:
/products/{name}(all releases),/products/{name}/releases/{cycle}(single release) - Converts API-generated types to internal
ReleaseDetails - Default EOL date: 2050-01-01 (Unix: 2524608000) when API returns null
pkg/endoflife/types.go
- Auto-generated from
pkg/endoflife/openapi.yamlviaoapi-codegen - DO NOT EDIT MANUALLY — regenerate with
task gen
products:
- name: mongo # Required: product slug from endoflife.date
releases: # Optional: specific release cycles (default: ["latest"])
- "8.0"
- "7.0"
- name: redis # Defaults to latest release
- name: ubuntu
all_releases: true # Fetch all release cycles (ignores 'releases' field)The pkg/endoflife/openapi.yaml spec is sourced from https://endoflife.date/docs/api/v1/.
Upstream OpenAPI version: The upstream spec uses OpenAPI 3.1, but oapi-codegen only supports OpenAPI 3.0. When updating the spec, it must be converted from 3.1 to 3.0 format.
Conversion steps:
- Download the latest spec from https://endoflife.date/docs/api/v1/
- Change
openapi: 3.1.xtoopenapi: 3.0.3 - Convert all nullable types from 3.1 array syntax to 3.0
nullable: true:type: [string, "null"]→type: string+nullable: truetype: [boolean, "null"]→type: boolean+nullable: trueanyOf: [{$ref: ...}, {type: "null"}]→allOf: [{$ref: ...}]+nullable: true
- Run
task gento regeneratetypes.go - If code breaks, check
pkg/endoflife/endoflife.gofor usage of changed field types (especially pointer vs non-pointer changes)
Known upstream issue: The API spec incorrectly defines isEoes as string in some versions, but the API returns boolean. This has been corrected in recent versions, but verify line 622-631 in openapi.yaml shows type: boolean with nullable: true.
- Unit tests use Ginkgo/Gomega framework
- Config parsing tests in
internal/config/config_test.go - Run specific test suites:
go test ./internal/config -v
All metrics use Unix timestamps (seconds since epoch).
endoflife_product_info(gauge=1): Metadata labels (is_eol, is_lts, is_maintained, latest_version, product_name, release_cycle_name)endoflife_latest_version_timestamp_seconds: When the latest patch version was releasedendoflife_release_cycle_timestamp_seconds: When the release cycle first launchedendoflife_eol_from_timestamp_seconds: When support ends (2050-01-01 if no EOL date)
Add a new metric:
- Define
prometheus.NewDescininternal/collector/collector.go(var block at top) - Add to
Exporter.Describe()method - Emit in
Exporter.Collect()loop usingprometheus.MustNewConstMetric
Change API client behavior:
- Modify
pkg/endoflife/endoflife.go(e.g., timeouts, error handling) - If API schema changes, update
openapi.yamland runtask gen
Update configuration schema:
- Modify structs in
internal/config/config.go - Update
sample.config.ymlexample - Add validation logic in
LoadConfig()if needed