Skip to content

Commit

Permalink
Refactor OTLP exporter env config to be shared across all exporters (#…
Browse files Browse the repository at this point in the history
…2608)

* setup global envconfig package for otlp exporter

* use envconfig in otlpmetrics package

* fix lint

* add changelog entry

* Update exporters/otlp/internal/envconfig/envconfig.go

Co-authored-by: Chester Cheung <cheung.zhy.csu@gmail.com>

* fix lint

Co-authored-by: Chester Cheung <cheung.zhy.csu@gmail.com>
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
  • Loading branch information
3 people committed Mar 3, 2022
1 parent 18f4cb8 commit 24414b2
Show file tree
Hide file tree
Showing 9 changed files with 627 additions and 446 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -31,6 +31,7 @@ This update is a breaking change of the unstable Metrics API. Code instrumented
- The metrics API has been significantly changed. (#2587)
- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` config. (#2639)
- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640)
- Introduce new internal envconfig package for OTLP exporters (#2608)

### Fixed

Expand Down
148 changes: 148 additions & 0 deletions exporters/otlp/internal/envconfig/envconfig.go
@@ -0,0 +1,148 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/internal/envconfig"

import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"time"
)

// ConfigFn is the generic function used to set a config
type ConfigFn func(*EnvOptionsReader)

// EnvOptionsReader reads the required environment variables
type EnvOptionsReader struct {
GetEnv func(string) string
ReadFile func(string) ([]byte, error)
Namespace string
}

// Apply runs every ConfigFn
func (e *EnvOptionsReader) Apply(opts ...ConfigFn) {
for _, o := range opts {
o(e)
}
}

// GetEnvValue gets an OTLP environment variable value of the specified key
// using the GetEnv function.
// This function prepends the OTLP specified namespace to all key lookups.
func (e *EnvOptionsReader) GetEnvValue(key string) (string, bool) {
v := strings.TrimSpace(e.GetEnv(keyWithNamespace(e.Namespace, key)))
return v, v != ""
}

// WithString retrieves the specified config and passes it to ConfigFn as a string
func WithString(n string, fn func(string)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
fn(v)
}
}
}

// WithDuration retrieves the specified config and passes it to ConfigFn as a duration
func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
if d, err := strconv.Atoi(v); err == nil {
fn(time.Duration(d) * time.Millisecond)
}
}
}
}

// WithHeaders retrieves the specified config and passes it to ConfigFn as a map of HTTP headers
func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
fn(stringToHeader(v))
}
}
}

// WithURL retrieves the specified config and passes it to ConfigFn as a net/url.URL
func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
if u, err := url.Parse(v); err == nil {
fn(u)
}
}
}
}

// WithTLSConfig retrieves the specified config and passes it to ConfigFn as a crypto/tls.Config
func WithTLSConfig(n string, fn func(*tls.Config)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
if b, err := e.ReadFile(v); err == nil {
if c, err := createTLSConfig(b); err == nil {
fn(c)
}
}
}
}
}

func keyWithNamespace(ns, key string) string {
if ns == "" {
return key
}
return fmt.Sprintf("%s_%s", ns, key)
}

func stringToHeader(value string) map[string]string {
headersPairs := strings.Split(value, ",")
headers := make(map[string]string)

for _, header := range headersPairs {
nameValue := strings.SplitN(header, "=", 2)
if len(nameValue) < 2 {
continue
}
name, err := url.QueryUnescape(nameValue[0])
if err != nil {
continue
}
trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(nameValue[1])
if err != nil {
continue
}
trimmedValue := strings.TrimSpace(value)

headers[trimmedName] = trimmedValue
}

return headers
}

func createTLSConfig(certBytes []byte) (*tls.Config, error) {
cp := x509.NewCertPool()
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
return nil, errors.New("failed to append certificate to the cert pool")
}

return &tls.Config{
RootCAs: cp,
}, nil
}

0 comments on commit 24414b2

Please sign in to comment.