Skip to content

Commit

Permalink
feat(metrics): enhance TLS configuration support in exporter
Browse files Browse the repository at this point in the history
Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
  • Loading branch information
lvlcn-t committed Nov 21, 2024
1 parent 40f379d commit f126b7b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 33 deletions.
2 changes: 0 additions & 2 deletions example/metrics/simple/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ import (

func main() {
ctx := context.Background()

// Initialize the metrics client with the given exporter, URL, token, and certificate path
client := metrics.New(metrics.Config{
Exporter: metrics.STDOUT,
CertPath: "",
})

// Initialize the open telemetry tracer with the given service name and version
Expand Down
81 changes: 50 additions & 31 deletions metrics/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@ import (
"google.golang.org/grpc/credentials"
)

// Config holds the configuration for OpenTelemetry
// Config holds the configuration for OpenTelemetry.
type Config struct {
// Exporter is the otlp exporter used to export the traces
// Exporter is the otlp exporter used to export the traces.
Exporter Exporter `yaml:"exporter" mapstructure:"exporter"`
// Url is the Url of the collector to which the traces are exported
// Url is the Url of the collector to which the traces are exported.
Url string `yaml:"url" mapstructure:"url"`
// Token is the token used to authenticate with the collector
// Token is the token used to authenticate with the collector.
Token string `yaml:"token" mapstructure:"token"`
// CertPath is the path to the tls certificate file
// TLS is the tls configuration for the exporter.
TLS TLSConfig `yaml:"tls" mapstructure:"tls"`
}

// TLSConfig holds the configuration for the tls.
type TLSConfig struct {
// Enabled is a flag to enable or disable the tls configuration.
Enabled bool `yaml:"enabled" mapstructure:"enabled"`
// CertPath is the path to a custom tls certificate file.
// If not set, the system's root certificates are used.
CertPath string `yaml:"certPath" mapstructure:"certPath"`
}

Expand All @@ -37,10 +46,8 @@ func (c Config) IsEmpty() bool {
// Validate validates the configuration.
func (c *Config) Validate() error {
err := c.Exporter.Validate()
if c.Exporter.isExporting() {
if c.Url == "" {
err = errors.Join(err, fmt.Errorf("url is required for otlp exporter %q", c.Exporter))
}
if c.Exporter.isExporting() && c.Url == "" {
err = errors.Join(err, fmt.Errorf("url is required for otlp exporter %q", c.Exporter))
}
return err
}
Expand Down Expand Up @@ -108,39 +115,45 @@ func (e Exporter) RegisterExporter(factory exporterFactory) error {

// newHTTPExporter creates a new HTTP exporter
func newHTTPExporter(ctx context.Context, config *Config) (sdktrace.SpanExporter, error) {
headers, tlsCfg, err := getCommonConfig(config)
conf, err := newExporterConfig(config)
if err != nil {
return nil, err
}

opts := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(config.Url),
otlptracehttp.WithHeaders(headers),
otlptracehttp.WithHeaders(conf.headers),
}
if tlsCfg != nil {
opts = append(opts, otlptracehttp.WithTLSClientConfig(tlsCfg))
} else {

if !config.TLS.Enabled {
opts = append(opts, otlptracehttp.WithInsecure())
return otlptracehttp.New(ctx, opts...)
}
if conf.tls != nil {
opts = append(opts, otlptracehttp.WithTLSClientConfig(conf.tls))
}

return otlptracehttp.New(ctx, opts...)
}

// newGRPCExporter creates a new gRPC exporter
func newGRPCExporter(ctx context.Context, config *Config) (sdktrace.SpanExporter, error) {
headers, tlsCfg, err := getCommonConfig(config)
conf, err := newExporterConfig(config)
if err != nil {
return nil, err
}

opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(config.Url),
otlptracegrpc.WithHeaders(headers),
otlptracegrpc.WithHeaders(conf.headers),
}
if tlsCfg != nil {
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(tlsCfg)))
} else {

if !config.TLS.Enabled {
opts = append(opts, otlptracegrpc.WithInsecure())
return otlptracegrpc.New(ctx, opts...)
}
if conf.tls != nil {
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(conf.tls)))
}

return otlptracegrpc.New(ctx, opts...)
Expand All @@ -156,33 +169,39 @@ func newNoopExporter(_ context.Context, _ *Config) (sdktrace.SpanExporter, error
return nil, nil
}

// getCommonConfig returns the common configuration for the exporters
func getCommonConfig(config *Config) (map[string]string, *tls.Config, error) {
headers := make(map[string]string)
// exporterConfig holds the common configuration for the exporters.
type exporterConfig struct {
// headers is the map of headers to be sent with the request.
headers map[string]string
// tls is the tls configuration for the exporter.
tls *tls.Config
}

// newExporterConfig returns the common configuration for the exporters
func newExporterConfig(config *Config) (exporterConfig, error) {
headers := map[string]string{}
if config.Token != "" {
headers["Authorization"] = fmt.Sprintf("Bearer %s", config.Token)
}

tlsCfg, err := getTLSConfig(config.CertPath)
tlsCfg, err := getTLSConfig(config.TLS.CertPath)
if err != nil {
return nil, nil, fmt.Errorf("failed to create TLS configuration: %w", err)
return exporterConfig{}, fmt.Errorf("failed to create TLS configuration: %w", err)
}

return headers, tlsCfg, nil
return exporterConfig{headers: headers, tls: tlsCfg}, nil
}

// fileOpener is the function used to open a file
type fileOpener func(string) (fs.File, error)

// openFile is the function used to open a file
var openFile fileOpener = func() fileOpener {
return func(name string) (fs.File, error) {
return os.Open(name) // #nosec G304 // How else to open the file?
}
}()
var openFile fileOpener = func(name string) (fs.File, error) {
return os.Open(name) // #nosec G304 // How else to open the file?
}

func getTLSConfig(certFile string) (conf *tls.Config, err error) {
if certFile == "" || certFile == "insecure" {
if certFile == "" {
return nil, nil
}

Expand Down
16 changes: 16 additions & 0 deletions metrics/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ func TestGetTLSConfig(t *testing.T) {
},
wantErr: false,
},
{
name: "success - use system cert pool",
certPath: "",
fileOpen: func(name string) (fs.File, error) {
panic("should not be called")
},
want: nil,
wantErr: false,
},
{
name: "failure - invalid cert path",
certPath: "testdata/invalid_cert.pem",
Expand Down Expand Up @@ -112,6 +121,13 @@ func TestGetTLSConfig(t *testing.T) {
return
}

if tt.want == nil {
if conf != nil {
t.Errorf("getTLSConfig() = %v, want %v", conf, tt.want)
}
return
}

// It's not possible to compare the CertPool struct directly,
// so every field is compared separately
if !cmp.Equal(conf.InsecureSkipVerify, tt.want.InsecureSkipVerify) {
Expand Down

0 comments on commit f126b7b

Please sign in to comment.