Skip to content

Commit

Permalink
Support OTEL_EXPORTER_OTLP_LOGS_INSECURE and OTEL_EXPORTER_OTLP_INSEC…
Browse files Browse the repository at this point in the history
…URE environments in grpc exporter (#5739)

Closes #5719

In this commit I add OTEL_EXPORTER_OTLP_LOGS_INSECURE and
OTEL_EXPORTER_OTLP_INSECURE env options to `otlploggrpc.Exporter`.

Now insecure option is fetched from env endpoint value
(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT/OTEL_EXPORTER_OTLP_ENDPOINT).
According to
[spec](https://opentelemetry.io/docs/specs/otel/protocol/exporter/):
> Insecure: Whether to enable client transport security for the
exporter’s gRPC connection. This option only applies to OTLP/gRPC when
an endpoint is provided without the http or https scheme - OTLP/HTTP
always uses the scheme provided for the endpoint.

So with current behavior we have several problems:
- If default endpoint is used, we can't use insecure connection (with
setting OTEL_EXPORTER_OTLP_INSECURE).
- If endpoint provided with option without scheme (e.g. `WithEndpoint`)
we can't use insecure connection with env settings.
- If endpoint provided with env variable without scheme (e.g.
`//env.endpoint:8080/`) we can't use insecure connection.

This commit fixes this.

The same problem with `otlploghttp.Exporter`, and probably it should be
fixed there too.
I'm open to suggestions on how to fix the current behavior in a more
elegant way.

---------

Co-authored-by: Sam Xie <sam@samxie.me>
Co-authored-by: Damien Mathieu <42@dmathieu.com>
  • Loading branch information
3 people committed Sep 6, 2024
1 parent fb7cc02 commit 8dca9cc
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Support `OTEL_EXPORTER_OTLP_LOGS_INSECURE` and `OTEL_EXPORTER_OTLP_INSECURE` environments in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#5739)

### Fixed

- Fix memory leak in the global `MeterProvider` when identical instruments are repeatedly created. (#5754)
Expand Down
63 changes: 50 additions & 13 deletions exporters/otlp/otlplog/otlploggrpc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ var (
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
"OTEL_EXPORTER_OTLP_ENDPOINT",
}
envInsecure = envEndpoint
envInsecure = []string{
"OTEL_EXPORTER_OTLP_LOGS_INSECURE",
"OTEL_EXPORTER_OTLP_INSECURE",
}

envHeaders = []string{
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
Expand Down Expand Up @@ -109,6 +112,7 @@ func newConfig(options []Option) config {
fallback[string](defaultEndpoint),
)
c.insecure = c.insecure.Resolve(
loadInsecureFromEnvEndpoint(envEndpoint),
getEnv[bool](envInsecure, convInsecure),
)
c.tlsCfg = c.tlsCfg.Resolve(
Expand Down Expand Up @@ -204,11 +208,7 @@ func WithEndpointURL(rawURL string) Option {
}
return fnOpt(func(c config) config {
c.endpoint = newSetting(u.Host)
if u.Scheme != "https" {
c.insecure = newSetting(true)
} else {
c.insecure = newSetting(false)
}
c.insecure = insecureFromScheme(c.insecure, u.Scheme)
return c
})
}
Expand Down Expand Up @@ -394,15 +394,39 @@ func convEndpoint(s string) (string, error) {
return u.Host, nil
}

// convInsecure parses s as a URL string and returns if the connection should
// use client transport security or not. If s is an invalid URL, false and an
// error are returned.
// convInsecure converts s from string to bool without case sensitivity.
// If s is not valid returns error.
func convInsecure(s string) (bool, error) {
u, err := url.Parse(s)
if err != nil {
return false, err
s = strings.ToLower(s)
if s != "true" && s != "false" {
return false, fmt.Errorf("can't convert %q to bool", s)
}

return s == "true", nil
}

// loadInsecureFromEnvEndpoint returns a resolver that fetches
// insecure setting from envEndpoint is it possible.
func loadInsecureFromEnvEndpoint(envEndpoint []string) resolver[bool] {
return func(s setting[bool]) setting[bool] {
if s.Set {
// Passed, valid, options have precedence.
return s
}

for _, key := range envEndpoint {
if vStr := os.Getenv(key); vStr != "" {
u, err := url.Parse(vStr)
if err != nil {
otel.Handle(fmt.Errorf("invalid %s value %s: %w", key, vStr, err))
continue
}

return insecureFromScheme(s, u.Scheme)
}
}
return s
}
return u.Scheme != "https", nil
}

// convHeaders converts the OTel environment variable header value s into a
Expand Down Expand Up @@ -529,6 +553,19 @@ func loadCertificates(certPath, keyPath string) ([]tls.Certificate, error) {
return []tls.Certificate{crt}, nil
}

// insecureFromScheme return setting if the connection should
// use client transport security or not.
// Empty scheme doesn't force insecure setting.
func insecureFromScheme(prev setting[bool], scheme string) setting[bool] {
if scheme == "https" {
return newSetting(false)
} else if len(scheme) > 0 {
return newSetting(true)
}

return prev
}

func compressorToCompression(compressor string) Compression {
c, err := convCompression(compressor)
if err != nil {
Expand Down
77 changes: 77 additions & 0 deletions exporters/otlp/otlplog/otlploggrpc/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,83 @@ func TestNewConfig(t *testing.T) {
`invalid OTEL_EXPORTER_OTLP_LOGS_TIMEOUT value 100 seconds: strconv.Atoi: parsing "100 seconds": invalid syntax`,
},
},
{
name: "OptionEndpointURLWithoutScheme",
options: []Option{
WithEndpointURL("//env.endpoint:8080/prefix"),
},
want: config{
endpoint: newSetting("env.endpoint:8080"),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
{
name: "EnvEndpointWithoutScheme",
envars: map[string]string{
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "//env.endpoint:8080/prefix",
},
want: config{
endpoint: newSetting("env.endpoint:8080"),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
{
name: "DefaultEndpointWithEnvInsecure",
envars: map[string]string{
"OTEL_EXPORTER_OTLP_LOGS_INSECURE": "true",
},
want: config{
endpoint: newSetting(defaultEndpoint),
insecure: newSetting(true),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
{
name: "EnvEndpointWithoutSchemeWithEnvInsecure",
envars: map[string]string{
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "//env.endpoint:8080/prefix",
"OTEL_EXPORTER_OTLP_LOGS_INSECURE": "true",
},
want: config{
endpoint: newSetting("env.endpoint:8080"),
insecure: newSetting(true),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
{
name: "OptionEndpointURLWithoutSchemeWithEnvInsecure",
options: []Option{
WithEndpointURL("//env.endpoint:8080/prefix"),
},
envars: map[string]string{
"OTEL_EXPORTER_OTLP_LOGS_INSECURE": "true",
},
want: config{
endpoint: newSetting("env.endpoint:8080"),
insecure: newSetting(true),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
{
name: "OptionEndpointWithEnvInsecure",
options: []Option{
WithEndpoint("env.endpoint:8080"),
},
envars: map[string]string{
"OTEL_EXPORTER_OTLP_LOGS_INSECURE": "true",
},
want: config{
endpoint: newSetting("env.endpoint:8080"),
insecure: newSetting(true),
retryCfg: newSetting(defaultRetryCfg),
timeout: newSetting(defaultTimeout),
},
},
}

for _, tc := range testcases {
Expand Down
6 changes: 6 additions & 0 deletions exporters/otlp/otlplog/otlploggrpc/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ The value should not contain a query string or fragment.
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_INSECURE, OTEL_EXPORTER_OTLP_LOGS_INSECURE (default: "false") -
setting "true" disables client transport security for the exporter's gRPC connection.
You can use this only when an endpoint is provided without scheme.
OTEL_EXPORTER_OTLP_LOGS_INSECURE takes precedence over OTEL_EXPORTER_OTLP_INSECURE.
The configuration can be overridden by [WithInsecure], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS (default: none) -
key-value pairs used as gRPC metadata associated with gRPC requests.
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
Expand Down

0 comments on commit 8dca9cc

Please sign in to comment.