Files
Michael Munch 12b7290697 distributedtracing: export Prometheus metrics via OTLP (#8450)
* distributedtracing: export Prometheus metrics via OTLP

Add support for pushing OPA's existing Prometheus metrics to an
OpenTelemetry collector via OTLP, eliminating the need for a dedicated
scraper sidecar. Uses the OTel Prometheus bridge to read from OPA's
prometheus.Registry and export through an OTLP metric exporter (gRPC
or HTTP), reusing the same address and TLS configuration as traces.

New config fields: distributed_tracing.metrics (bool, default false)
and distributed_tracing.metrics_export_interval_ms (int, default 60000).

Fixes #7591

Signed-off-by: Michael Munch <mm.munk@gmail.com>

* metricsexport: decouple metrics export into top-level config section

Extract metrics export from distributed_tracing into its own
metrics_export config section with independent type (otlp/grpc,
otlp/http), address, and TLS settings. This allows exporting
Prometheus metrics via OTLP without enabling tracing, and to a
different endpoint than traces.

- Extract shared TLS helpers into internal/tlsutil
- Add MetricsExport field to top-level Config
- Create internal/metricsexport package with Init, config parsing
- Remove metrics fields from distributedtracing
- Update runtime to call metricsexport.Init separately
- Move e2e tests to v1/test/e2e/metricsexport
- Add Metrics Export section to configuration docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Michael Munch <mm.munk@gmail.com>

* ci: retrigger checks

Signed-off-by: Michael Munch <mm.munk@gmail.com>

* go.mod: upgrade dependencies downgraded during rebase

Modules like containerd, go-sqlbuilder, OpenTelemetry, and golang.org/x/*
were at older versions than main after a rebase. Upgrade them to match or
exceed main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Michael Munch <mm.munk@gmail.com>

* Update internal/distributedtracing/distributedtracing_test.go

Signed-off-by: Michael Munch <mm.munk@gmail.com>

---------

Signed-off-by: Michael Munch <mm.munk@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:09:51 +00:00

69 lines
1.8 KiB
Go

// Copyright 2024 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package tlsutil
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"os"
)
// LoadCertificate loads a TLS certificate from the given cert and key files.
// If both are empty, it returns nil. If only one is provided, it returns an error.
func LoadCertificate(certFile, keyFile string) (*tls.Certificate, error) {
if certFile != "" && keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return &cert, nil
}
if certFile != "" || keyFile != "" {
return nil, errors.New("tls_cert_file and tls_private_key_file must be specified together")
}
return nil, nil
}
// LoadCertPool loads a certificate pool from the given CA cert file.
// If the file is empty, it returns nil.
func LoadCertPool(caCertFile string) (*x509.CertPool, error) {
if caCertFile == "" {
return nil, nil
}
caCertPEM, err := os.ReadFile(caCertFile)
if err != nil {
return nil, fmt.Errorf("read CA cert file: %v", err)
}
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
return nil, fmt.Errorf("failed to parse CA cert %q", caCertFile)
}
return pool, nil
}
// BuildTLSConfig creates a *tls.Config based on the encryption scheme.
// For "off", it returns nil. For "mtls", a certificate is required.
func BuildTLSConfig(scheme string, skipVerify bool, cert *tls.Certificate, pool *x509.CertPool) (*tls.Config, error) {
if scheme == "off" {
return nil, nil
}
tlsConfig := &tls.Config{
RootCAs: pool,
InsecureSkipVerify: skipVerify,
}
if scheme == "mtls" {
if cert == nil {
return nil, errors.New("tls_cert_file required but not supplied")
}
tlsConfig.Certificates = []tls.Certificate{*cert}
}
return tlsConfig, nil
}