Skip to content

Commit

Permalink
config: support HeadersList
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsains committed Jan 21, 2025
1 parent 19c1680 commit 47c5b23
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- Add support for configuring `ClientCertificate` and `ClientKey` field for OTLP exporters in `go.opentelemetry.io/contrib/config`. (#6378)
- Add support for configuring `HeadersList` field for OTLP exporters in `go.opentelemetry.io/contrib/config`. (TODO)

### Fixed

Expand Down
32 changes: 24 additions & 8 deletions config/v0.3.0/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"gopkg.in/yaml.v3"

"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/log"
nooplog "go.opentelemetry.io/otel/log/noop"
"go.opentelemetry.io/otel/metric"
Expand Down Expand Up @@ -152,14 +153,6 @@ func ParseYAML(file []byte) (*OpenTelemetryConfiguration, error) {
return &cfg, nil
}

func toStringMap(pairs []NameStringValuePair) map[string]string {
output := make(map[string]string)
for _, v := range pairs {
output[v.Name] = *v.Value
}
return output
}

// createTLSConfig creates a tls.Config from certificate files.
func createTLSConfig(caCertFile *string, clientCertFile *string, clientKeyFile *string) (*tls.Config, error) {
tlsConfig := &tls.Config{}
Expand All @@ -186,3 +179,26 @@ func createTLSConfig(caCertFile *string, clientCertFile *string, clientKeyFile *
}
return tlsConfig, nil
}

// createHeadersConfig combines the two header config fields and returns a map[string]string.
func createHeadersConfig(headers []NameStringValuePair, headersList *string) (map[string]string, error) {
result := make(map[string]string)
if headersList != nil {
headerslist, err := baggage.Parse(*headersList)
if err != nil {
return nil, fmt.Errorf("invalid headers list: %w", err)
}
for _, kv := range headerslist.Members() {
result[kv.Key()] = kv.Value()
}
}
// Headers take precedence over HeadersList, so this has to be after HeadersList is processed
if len(headers) > 0 {
for _, kv := range headers {
if kv.Value != nil {
result[kv.Name] = *kv.Value
}
}
}
return result, nil
}
93 changes: 93 additions & 0 deletions config/v0.3.0/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,99 @@ func TestCreateTLSConfig(t *testing.T) {
}
}

func TestCreateHeadersConfig(t *testing.T) {
tests := []struct {
name string
headers []NameStringValuePair
headersList *string
wantHeaders map[string]string
wantErr string
}{
{
name: "no headers",
headers: []NameStringValuePair{},
headersList: nil,
wantHeaders: map[string]string{},
},
{
name: "headerslist only",
headers: []NameStringValuePair{},
headersList: ptr("a=b,c=d"),
wantHeaders: map[string]string{
"a": "b",
"c": "d",
},
},
{
name: "headers only",
headers: []NameStringValuePair{
{
Name: "a",
Value: ptr("b"),
},
{
Name: "c",
Value: ptr("d"),
},
},
headersList: nil,
wantHeaders: map[string]string{
"a": "b",
"c": "d",
},
},
{
name: "both headers and headerslist",
headers: []NameStringValuePair{
{
Name: "a",
Value: ptr("b"),
},
},
headersList: ptr("c=d"),
wantHeaders: map[string]string{
"a": "b",
"c": "d",
},
},
{
name: "headers supercedes headerslist",
headers: []NameStringValuePair{
{
Name: "a",
Value: ptr("b"),
},
{
Name: "c",
Value: ptr("override"),
},
},
headersList: ptr("c=d"),
wantHeaders: map[string]string{
"a": "b",
"c": "override",
},
},
{
name: "invalid headerslist",
headersList: ptr("==="),
wantErr: "invalid headers list: invalid key: \"\"",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
headersMap, err := createHeadersConfig(tt.headers, tt.headersList)
if tt.wantErr != "" {
require.Error(t, err)
require.Equal(t, tt.wantErr, err.Error())
} else {
require.NoError(t, err)
}
require.Equal(t, tt.wantHeaders, headersMap)
})
}
}

func ptr[T any](v T) *T {
return &v
}
16 changes: 12 additions & 4 deletions config/v0.3.0/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,12 @@ func otlpHTTPLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
opts = append(opts, otlploghttp.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlploghttp.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 158 in config/v0.3.0/log.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/log.go#L157-L158

Added lines #L157 - L158 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlploghttp.WithHeaders(headersConfig))
}

tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey)
Expand Down Expand Up @@ -200,8 +204,12 @@ func otlpGRPCLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
opts = append(opts, otlploggrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlploggrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 210 in config/v0.3.0/log.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/log.go#L209-L210

Added lines #L209 - L210 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlploggrpc.WithHeaders(headersConfig))
}

tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey)
Expand Down
16 changes: 12 additions & 4 deletions config/v0.3.0/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,12 @@ func otlpHTTPMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet
if otlpConfig.Timeout != nil {
opts = append(opts, otlpmetrichttp.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlpmetrichttp.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 172 in config/v0.3.0/metric.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/metric.go#L171-L172

Added lines #L171 - L172 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlpmetrichttp.WithHeaders(headersConfig))
}
if otlpConfig.TemporalityPreference != nil {
switch *otlpConfig.TemporalityPreference {
Expand Down Expand Up @@ -227,8 +231,12 @@ func otlpGRPCMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
opts = append(opts, otlpmetricgrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlpmetricgrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 237 in config/v0.3.0/metric.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/metric.go#L236-L237

Added lines #L236 - L237 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlpmetricgrpc.WithHeaders(headersConfig))
}
if otlpConfig.TemporalityPreference != nil {
switch *otlpConfig.TemporalityPreference {
Expand Down
16 changes: 12 additions & 4 deletions config/v0.3.0/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,12 @@ func otlpGRPCSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
opts = append(opts, otlptracegrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 131 in config/v0.3.0/trace.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/trace.go#L130-L131

Added lines #L130 - L131 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(headersConfig))
}

tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey)
Expand Down Expand Up @@ -168,8 +172,12 @@ func otlpHTTPSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
opts = append(opts, otlptracehttp.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
}
if len(otlpConfig.Headers) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(toStringMap(otlpConfig.Headers)))
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
if err != nil {
return nil, err
}

Check warning on line 178 in config/v0.3.0/trace.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/trace.go#L177-L178

Added lines #L177 - L178 were not covered by tests
if len(headersConfig) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(headersConfig))
}

tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey)
Expand Down

0 comments on commit 47c5b23

Please sign in to comment.