Repository: lightstep/otel-launcher-go
Pull request #61 (2021-07-15 17:54:54)

generic otlp endpoint support
Description: add WithHeaders and rework token handling to support it
Testing: current unit tests all pass, none added as this is a demo hack
Documentation: README and godoc are updated with the new functionality

This is a demo hack to start making it possible to use otel-launcher-go work with non-Lightstep providers. I'm submitting this draft pull request to see if there is interest in continuing this work, or if it's out of scope for the project. I'm happy to rework the approach and resubmit if this is interesting. I think maybe WithHeaders should be WithHeader(k, v string), but for now it works as a prototype.

I am trying to add otel support to some OSS code while avoiding the usual boilerplate the launcher contains. Portability to different providers might not be very important to many users of this launcher, but I think if we want to integrate it in OSS projects, it needs to at least work with standard OTLP/gRPC services.

No problem if y'all aren't interested, but it seems wasteful to duplicate the API, nice code,and testing work y'all have done.

tobert: Example PR: https://github.com/tinkerbell/tink/pull/514
codeboten: Thanks for the submission @tobert, it would be great to have this become more generic.

This code was all implemented with the idea that it would eventually either be donated back to otel as some generic configuration/validation layer, or become obsolete as the otel go project provides a way to implement defaults/config validation.

If you're still keen on pursuing it, we'd be happy to help move it forward.

Patch:
diff --git a/README.md b/README.md
index fac5571..33aef3a 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,24 @@ func main() {
 }
 ```
 
+For non-lightstep providers, you can set headers directly instead.
+
+```go
+import "github.com/lightstep/otel-launcher-go/launcher"
+
+func main() {
+    otel := launcher.ConfigureOpentelemetry(
+        launcher.WithServiceName("service-name"),
+        launcher.WithHeaders(map[string]string{
+            "service-auth-key": "value",
+            "service-useful-field": "testing",
+        })
+    )
+    defer otel.Shutdown()
+}
+```
+
+
 Additional options
 
 ### Configuration Options
@@ -40,6 +58,7 @@ Additional options
 |------------------|------------------|--------|-------|
 |WithServiceName            |LS_SERVICE_NAME                    |y       |-                               |
 |WithServiceVersion         |LS_SERVICE_VERSION                 |n       |unknown                         |
+|WithHeaders                |OTEL_EXPORTER_OTLP_HEADERS         |n       |{}                              |
 |WithSpanExporterEndpoint   |OTEL_EXPORTER_OTLP_SPAN_ENDPOINT   |n       |ingest.lightstep.com:443        |
 |WithSpanExporterInsecure   |OTEL_EXPORTER_OTLP_SPAN_INSECURE   |n       |false                           |
 |WithMetricExporterEndpoint |OTEL_EXPORTER_OTLP_METRIC_ENDPOINT |n       |ingest.lightstep.com:443        |
diff --git a/go.mod b/go.mod
index 2fa88d1..77bde0e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,23 +1,16 @@
 module github.com/lightstep/otel-launcher-go
 
-go 1.14
+go 1.16
 
 require (
+	github.com/lightstep/otel-launcher-go/pipelines v0.0.0-00010101000000-000000000000
 	github.com/sethvargo/go-envconfig v0.3.2
 	github.com/stretchr/testify v1.7.0
 	go.opentelemetry.io/collector v0.28.0
-	go.opentelemetry.io/contrib/instrumentation/host v0.23.0
-	go.opentelemetry.io/contrib/instrumentation/runtime v0.23.0
-	go.opentelemetry.io/contrib/propagators/b3 v0.23.0
-	go.opentelemetry.io/contrib/propagators/ot v0.23.0
 	go.opentelemetry.io/otel v1.0.0
-	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.23.0
-	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.23.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0
 	go.opentelemetry.io/otel/metric v0.23.0
 	go.opentelemetry.io/otel/sdk v1.0.0
-	go.opentelemetry.io/otel/sdk/metric v0.23.0
 	go.opentelemetry.io/otel/trace v1.0.0
-	google.golang.org/grpc v1.40.0
 )
+
+replace github.com/lightstep/otel-launcher-go/pipelines => ./pipelines
diff --git a/launcher/config.go b/launcher/config.go
index df50952..b4e664d 100644
--- a/launcher/config.go
+++ b/launcher/config.go
@@ -23,36 +23,29 @@ import (
 	"os"
 	"time"
 
+	"github.com/lightstep/otel-launcher-go/pipelines"
 	"github.com/sethvargo/go-envconfig"
 	"go.opentelemetry.io/collector/translator/conventions"
-	hostMetrics "go.opentelemetry.io/contrib/instrumentation/host"
-	runtimeMetrics "go.opentelemetry.io/contrib/instrumentation/runtime"
-	"go.opentelemetry.io/contrib/propagators/b3"
-	"go.opentelemetry.io/contrib/propagators/ot"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
-	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
-	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
-	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
-	metricglobal "go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/propagation"
-	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
-	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
-	selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"
 	"go.opentelemetry.io/otel/sdk/resource"
-	"go.opentelemetry.io/otel/sdk/trace"
 	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/encoding/gzip"
 )
 
+const lightstepAccessTokenHeader = "lightstep-access-token"
+
 type Option func(*Config)
 
 // WithAccessToken configures the lightstep access token
+// remain compatible with the Lightstep-only launcher for now...
 func WithAccessToken(accessToken string) Option {
+	// this will actually get used
 	return func(c *Config) {
-		c.AccessToken = accessToken
+		// duplicating this isn't great but for now let's just get it working
+		if c.Headers == nil {
+			c.Headers = make(map[string]string)
+		}
+		c.Headers["lightstep-access-token"] = accessToken
 	}
 }
 
@@ -84,6 +77,18 @@ func WithServiceVersion(version string) Option {
 	}
 }
 
+// WithHeaders configures OTLP/gRPC connection headers
+func WithHeaders(headers map[string]string) Option {
+	return func(c *Config) {
+		if c.Headers == nil {
+			c.Headers = make(map[string]string)
+		}
+		for k, v := range headers {
+			c.Headers[k] = v
+		}
+	}
+}
+
 // WithLogLevel configures the logging level for OpenTelemetry
 func WithLogLevel(loglevel string) Option {
 	return func(c *Config) {
@@ -184,17 +189,17 @@ const (
 )
 
 type Config struct {
-	SpanExporterEndpoint           string   `env:"OTEL_EXPORTER_OTLP_SPAN_ENDPOINT,default=ingest.lightstep.com:443"`
-	SpanExporterEndpointInsecure   bool     `env:"OTEL_EXPORTER_OTLP_SPAN_INSECURE,default=false"`
-	ServiceName                    string   `env:"LS_SERVICE_NAME"`
-	ServiceVersion                 string   `env:"LS_SERVICE_VERSION,default=unknown"`
-	MetricExporterEndpoint         string   `env:"OTEL_EXPORTER_OTLP_METRIC_ENDPOINT,default=ingest.lightstep.com:443"`
-	MetricExporterEndpointInsecure bool     `env:"OTEL_EXPORTER_OTLP_METRIC_INSECURE,default=false"`
-	MetricsEnabled                 bool     `env:"LS_METRICS_ENABLED,default=true"`
-	AccessToken                    string   `env:"LS_ACCESS_TOKEN"`
-	LogLevel                       string   `env:"OTEL_LOG_LEVEL,default=info"`
-	Propagators                    []string `env:"OTEL_PROPAGATORS,default=b3"`
-	MetricReportingPeriod          string   `env:"OTEL_EXPORTER_OTLP_METRIC_PERIOD,default=30s"`
+	SpanExporterEndpoint           string            `env:"OTEL_EXPORTER_OTLP_SPAN_ENDPOINT,default=ingest.lightstep.com:443"`
+	SpanExporterEndpointInsecure   bool              `env:"OTEL_EXPORTER_OTLP_SPAN_INSECURE,default=false"`
+	ServiceName                    string            `env:"LS_SERVICE_NAME"`
+	ServiceVersion                 string            `env:"LS_SERVICE_VERSION,default=unknown"`
+	Headers                        map[string]string `env:"OTEL_EXPORTER_OTLP_HEADERS"`
+	MetricExporterEndpoint         string            `env:"OTEL_EXPORTER_OTLP_METRIC_ENDPOINT,default=ingest.lightstep.com:443"`
+	MetricExporterEndpointInsecure bool              `env:"OTEL_EXPORTER_OTLP_METRIC_INSECURE,default=false"`
+	MetricsEnabled                 bool              `env:"LS_METRICS_ENABLED,default=true"`
+	LogLevel                       string            `env:"OTEL_LOG_LEVEL,default=info"`
+	Propagators                    []string          `env:"OTEL_PROPAGATORS,default=b3"`
+	MetricReportingPeriod          string            `env:"OTEL_EXPORTER_OTLP_METRIC_PERIOD,default=30s"`
 	resourceAttributes             map[string]string
 	Resource                       *resource.Resource
 	logger                         Logger
@@ -212,6 +217,13 @@ func checkEndpointDefault(value, defValue string) error {
 	return nil
 }
 
+func accessToken(c Config) string {
+	if c.Headers == nil {
+		return ""
+	}
+	return c.Headers[lightstepAccessTokenHeader]
+}
+
 func validateConfiguration(c Config) error {
 	if len(c.ServiceName) == 0 {
 		serviceNameSet := false
@@ -228,7 +240,7 @@ func validateConfiguration(c Config) error {
 		}
 	}
 
-	accessTokenLen := len(c.AccessToken)
+	accessTokenLen := len(accessToken(c))
 	if accessTokenLen == 0 {
 		if err := checkEndpointDefault(c.SpanExporterEndpoint, DefaultSpanExporterEndpoint); err != nil {
 			return err
@@ -239,9 +251,11 @@ func validateConfiguration(c Config) error {
 		}
 	}
 
+	// TODO(@tobert) will probably break on some providers but seems fine for my use cases right now
 	if accessTokenLen > 0 && (accessTokenLen != 32 && accessTokenLen != 84 && accessTokenLen != 104) {
 		return fmt.Errorf("invalid configuration: access token length incorrect. Ensure token is set correctly")
 	}
+
 	return nil
 }
 
@@ -269,30 +283,6 @@ type Launcher struct {
 	shutdownFuncs []func() error
 }
 
-// configurePropagators configures B3 propagation by default
-func configurePropagators(c *Config) error {
-	propagatorsMap := map[string]propagation.TextMapPropagator{
-		"b3":           b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)),
-		"baggage":      propagation.Baggage{},
-		"tracecontext": propagation.TraceContext{},
-		"ottrace":      ot.OT{},
-	}
-	var props []propagation.TextMapPropagator
-	for _, key := range c.Propagators {
-		prop := propagatorsMap[key]
-		if prop != nil {
-			props = append(props, prop)
-		}
-	}
-	if len(props) == 0 {
-		return fmt.Errorf("invalid configuration: unsupported propagators. Supported options: b3,baggage,tracecontext,ottrace")
-	}
-	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
-		props...,
-	))
-	return nil
-}
-
 func newResource(c *Config) *resource.Resource {
 	r := resource.Environment()
 
@@ -350,73 +340,18 @@ func newResource(c *Config) *resource.Resource {
 	return r
 }
 
-func newTraceExporter(accessToken, endpoint string, insecure bool) (*otlptrace.Exporter, error) {
-	headers := map[string]string{
-		"lightstep-access-token": accessToken,
-	}
-
-	secureOption := otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
-	if insecure {
-		secureOption = otlptracegrpc.WithInsecure()
-	}
-	return otlptrace.New(
-		context.Background(),
-		otlptracegrpc.NewClient(
-			secureOption,
-			otlptracegrpc.WithEndpoint(endpoint),
-			otlptracegrpc.WithHeaders(headers),
-			otlptracegrpc.WithCompressor(gzip.Name),
-		),
-	)
-}
-
-func newMetricsExporter(accessToken, endpoint string, insecure bool) (*otlpmetric.Exporter, error) {
-	headers := map[string]string{
-		"lightstep-access-token": accessToken,
-	}
-
-	secureOption := otlpmetricgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
-	if insecure {
-		secureOption = otlpmetricgrpc.WithInsecure()
-	}
-	return otlpmetric.New(
-		context.Background(),
-		otlpmetricgrpc.NewClient(
-			secureOption,
-			otlpmetricgrpc.WithEndpoint(endpoint),
-			otlpmetricgrpc.WithHeaders(headers),
-			otlpmetricgrpc.WithCompressor(gzip.Name),
-		),
-	)
-}
-
 func setupTracing(c Config) (func() error, error) {
 	if c.SpanExporterEndpoint == "" {
 		c.logger.Debugf("tracing is disabled by configuration: no endpoint set")
 		return nil, nil
 	}
-	spanExporter, err := newTraceExporter(c.AccessToken, c.SpanExporterEndpoint, c.SpanExporterEndpointInsecure)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create span exporter: %v", err)
-	}
-
-	bsp := trace.NewBatchSpanProcessor(spanExporter)
-	tp := trace.NewTracerProvider(
-		trace.WithSampler(trace.AlwaysSample()),
-		trace.WithSpanProcessor(bsp),
-		trace.WithResource(c.Resource),
-	)
-
-	if err = configurePropagators(&c); err != nil {
-		return nil, err
-	}
-
-	otel.SetTracerProvider(tp)
-
-	return func() error {
-		_ = bsp.Shutdown(context.Background())
-		return spanExporter.Shutdown(context.Background())
-	}, nil
+	return pipelines.NewTracePipeline(pipelines.PipelineConfig{
+		Endpoint:    c.SpanExporterEndpoint,
+		Insecure:    c.SpanExporterEndpointInsecure,
+		Headers:     c.Headers,
+		Resource:    c.Resource,
+		Propagators: c.Propagators,
+	})
 }
 
 type setupFunc func(Config) (func() error, error)
@@ -426,51 +361,13 @@ func setupMetrics(c Config) (func() error, error) {
 		c.logger.Debugf("metrics are disabled by configuration: no endpoint set")
 		return nil, nil
 	}
-	metricExporter, err := newMetricsExporter(c.AccessToken, c.MetricExporterEndpoint, c.MetricExporterEndpointInsecure)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create metric exporter: %v", err)
-	}
-
-	period := controller.DefaultPeriod
-	if c.MetricReportingPeriod != "" {
-		period, err = time.ParseDuration(c.MetricReportingPeriod)
-		if err != nil {
-			return nil, fmt.Errorf("invalid metric reporting period: %v", err)
-		}
-		if period <= 0 {
-
-			return nil, fmt.Errorf("invalid metric reporting period: %v", c.MetricReportingPeriod)
-		}
-	}
-	pusher := controller.New(
-		processor.New(
-			selector.NewWithInexpensiveDistribution(),
-			metricExporter,
-		),
-		controller.WithExporter(metricExporter),
-		controller.WithResource(c.Resource),
-		controller.WithCollectPeriod(period),
-	)
-
-	if err = pusher.Start(context.Background()); err != nil {
-		return nil, fmt.Errorf("failed to start controller: %v", err)
-	}
-
-	provider := pusher.MeterProvider()
-
-	if err = runtimeMetrics.Start(runtimeMetrics.WithMeterProvider(provider)); err != nil {
-		return nil, fmt.Errorf("failed to start runtime metrics: %v", err)
-	}
-
-	if err = hostMetrics.Start(hostMetrics.WithMeterProvider(provider)); err != nil {
-		return nil, fmt.Errorf("failed to start host metrics: %v", err)
-	}
-
-	metricglobal.SetMeterProvider(provider)
-	return func() error {
-		_ = pusher.Stop(context.Background())
-		return metricExporter.Shutdown(context.Background())
-	}, nil
+	return pipelines.NewMetricsPipeline(pipelines.PipelineConfig{
+		Endpoint:        c.MetricExporterEndpoint,
+		Insecure:        c.MetricExporterEndpointInsecure,
+		Headers:         c.Headers,
+		Resource:        c.Resource,
+		ReportingPeriod: c.MetricReportingPeriod,
+	})
 }
 
 func ConfigureOpentelemetry(opts ...Option) Launcher {
@@ -483,6 +380,15 @@ func ConfigureOpentelemetry(opts ...Option) Launcher {
 		c.logger.Debugf(string(s))
 	}
 
+	if c.Headers == nil {
+		c.Headers = map[string]string{}
+	}
+
+	token := os.Getenv("LS_ACCESS_TOKEN")
+	if len(token) > 0 && len(c.Headers[lightstepAccessTokenHeader]) == 0 {
+		c.Headers[lightstepAccessTokenHeader] = token
+	}
+
 	err := validateConfiguration(c)
 	if err != nil {
 		c.logger.Fatalf("configuration error: %v", err)
@@ -495,6 +401,7 @@ func ConfigureOpentelemetry(opts ...Option) Launcher {
 	ls := Launcher{
 		config: c,
 	}
+
 	for _, setup := range []setupFunc{setupTracing, setupMetrics} {
 		shutdown, err := setup(c)
 		if err != nil {
diff --git a/pipelines/common.go b/pipelines/common.go
new file mode 100644
index 0000000..e062f66
--- /dev/null
+++ b/pipelines/common.go
@@ -0,0 +1,28 @@
+// Copyright Lightstep 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 pipelines
+
+import "go.opentelemetry.io/otel/sdk/resource"
+
+type PipelineConfig struct {
+	Endpoint        string
+	Insecure        bool
+	Headers         map[string]string
+	Resource        *resource.Resource
+	ReportingPeriod string
+	Propagators     []string
+}
+
+type PipelineSetupFunc func(PipelineConfig) (func() error, error)
diff --git a/pipelines/go.mod b/pipelines/go.mod
new file mode 100644
index 0000000..15f9eed
--- /dev/null
+++ b/pipelines/go.mod
@@ -0,0 +1,30 @@
+module github.com/lightstep/otel-launcher-go/pipelines
+
+go 1.16
+
+require (
+	go.opentelemetry.io/contrib/instrumentation/host v0.23.0
+	go.opentelemetry.io/contrib/instrumentation/runtime v0.23.0
+	go.opentelemetry.io/contrib/propagators/b3 v0.23.0
+	go.opentelemetry.io/contrib/propagators/ot v0.23.0
+	go.opentelemetry.io/otel v1.0.0
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.23.0
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.23.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0
+	go.opentelemetry.io/otel/metric v0.23.0
+	go.opentelemetry.io/otel/sdk v1.0.0
+	go.opentelemetry.io/otel/sdk/metric v0.23.0
+	google.golang.org/grpc v1.40.0
+)
+
+require (
+	github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/go-ole/go-ole v1.2.5 // indirect
+	github.com/shirou/gopsutil v3.21.5+incompatible // indirect
+	github.com/tklauser/go-sysconf v0.3.5 // indirect
+	golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
+	google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f // indirect
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
+)
diff --git a/pipelines/go.sum b/pipelines/go.sum
new file mode 100644
index 0000000..696e56e
--- /dev/null
+++ b/pipelines/go.sum
@@ -0,0 +1,195 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
+github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
+github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
+github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
+github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
+github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
+go.opentelemetry.io/contrib v0.23.0 h1:MgRuo0JZZX8J9WLRjyd7OpTSbaLOdQXXJa6SnZvlWLM=
+go.opentelemetry.io/contrib v0.23.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
+go.opentelemetry.io/contrib/instrumentation/host v0.23.0 h1:A2/tyKbKkWy8fbll7qnQBtVRPlxBmeIiib1kPKsuC3A=
+go.opentelemetry.io/contrib/instrumentation/host v0.23.0/go.mod h1:vmScLmx4vgYCbizvC7fj2ox2deIoACLWp/RXEF34ras=
+go.opentelemetry.io/contrib/instrumentation/runtime v0.23.0 h1:QSjPJH+LRo+85AUCxF+3ZkQtrHRWt+9AFbUiwy5FTZE=
+go.opentelemetry.io/contrib/instrumentation/runtime v0.23.0/go.mod h1:FcLiNlij4dNsaDtt7Rp/9dR+kgnNuV6WQ2Ujc8bcRD0=
+go.opentelemetry.io/contrib/propagators/b3 v0.23.0 h1:sxitmDBHZS3Z2lw81kDntCRFt4dQglu9EOhBPNHN9CY=
+go.opentelemetry.io/contrib/propagators/b3 v0.23.0/go.mod h1:tAJUf0uW1xrgQ1QgOZqLXFbn3DNFO4tTmh0Nqe3kSzw=
+go.opentelemetry.io/contrib/propagators/ot v0.23.0 h1:QD69MmaJwLOaLj8uNmxpJa52v/jSsX4ZU/BV8acpb68=
+go.opentelemetry.io/contrib/propagators/ot v0.23.0/go.mod h1:uhAU1z0UtdOTHWPfmpAOlikY/vK79cGwrteYDkcxvGU=
+go.opentelemetry.io/otel v1.0.0-RC3/go.mod h1:Ka5j3ua8tZs4Rkq4Ex3hwgBgOchyPVq5S6P2lz//nKQ=
+go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
+go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.23.0 h1:vKIEsT6IJU0NYd+iZccjgCmk80zsa7dTiC2Bu7U1jz0=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.23.0/go.mod h1:pe9oOWRaZyapdajWCn64fnl76v3cmTEmNBgh7MkKvwE=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.23.0 h1:JSsJID+KU3G8wxynfHIlWaefOvYngDjnrmtHOGb1sb0=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.23.0/go.mod h1:aSP5oMNaAfOYq+sRydHANZ0vBYLyZR/3lR9pru9aPLk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0 h1:Vv4wbLEjheCTPV07jEav7fyUpJkyftQK7Ss2G7qgdSo=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0/go.mod h1:3VqVbIbjAycfL1C7sIu/Uh/kACIUPWHztt8ODYwR3oM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0 h1:B9VtEB1u41Ohnl8U6rMCh1jjedu8HwFh4D0QeB+1N+0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0/go.mod h1:zhEt6O5GGJ3NCAICr4hlCPoDb2GQuh4Obb4gZBgkoQQ=
+go.opentelemetry.io/otel/internal/metric v0.23.0 h1:mPfzm9Iqhw7G2nDBmUAjFTfPqLZPbOW2k7QI57ITbaI=
+go.opentelemetry.io/otel/internal/metric v0.23.0/go.mod h1:z+RPiDJe30YnCrOhFGivwBS+DU1JU/PiLKkk4re2DNY=
+go.opentelemetry.io/otel/metric v0.23.0 h1:mYCcDxi60P4T27/0jchIDFa1WHEfQeU3zH9UEMpnj2c=
+go.opentelemetry.io/otel/metric v0.23.0/go.mod h1:G/Nn9InyNnIv7J6YVkQfpc0JCfKBNJaERBGw08nqmVQ=
+go.opentelemetry.io/otel/sdk v1.0.0-RC3/go.mod h1:78H6hyg2fka0NYT9fqGuFLvly2yCxiBXDJAgLKo/2Us=
+go.opentelemetry.io/otel/sdk v1.0.0 h1:BNPMYUONPNbLneMttKSjQhOTlFLOD9U22HNG1KrIN2Y=
+go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM=
+go.opentelemetry.io/otel/sdk/export/metric v0.23.0 h1:7NeoKPPx6NdZBVHLEp/LY5Lq85Ff1WNZnuJkuRy+azw=
+go.opentelemetry.io/otel/sdk/export/metric v0.23.0/go.mod h1:SuMiREmKVRIwFKq73zvGTvwFpxb/ZAYkMfyqMoOtDqs=
+go.opentelemetry.io/otel/sdk/metric v0.23.0 h1:xlZhPbiue1+jjSFEth94q9QCmX8Q24mOtue9IAmlVyI=
+go.opentelemetry.io/otel/sdk/metric v0.23.0/go.mod h1:wa0sKK13eeIFW+0OFjcC3S1i7FTRRiLAXe1kjBVbhwg=
+go.opentelemetry.io/otel/trace v1.0.0-RC3/go.mod h1:VUt2TUYd8S2/ZRX09ZDFZQwn2RqfMB5MzO17jBojGxo=
+go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
+go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4=
+go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
+golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f h1:YRBxgxUW6GFi+AKsn8WGA9k1SZohK+gGuEqdeT5aoNQ=
+google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pipelines/metrics.go b/pipelines/metrics.go
new file mode 100644
index 0000000..c8443ae
--- /dev/null
+++ b/pipelines/metrics.go
@@ -0,0 +1,96 @@
+// Copyright Lightstep 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 pipelines
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	hostMetrics "go.opentelemetry.io/contrib/instrumentation/host"
+	runtimeMetrics "go.opentelemetry.io/contrib/instrumentation/runtime"
+	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
+	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
+	metricglobal "go.opentelemetry.io/otel/metric/global"
+	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
+	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
+	selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"
+	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/encoding/gzip"
+)
+
+func NewMetricsPipeline(c PipelineConfig) (func() error, error) {
+	metricExporter, err := newMetricsExporter(c.Endpoint, c.Insecure, c.Headers)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create metric exporter: %v", err)
+	}
+
+	period := controller.DefaultPeriod
+	if c.ReportingPeriod != "" {
+		period, err = time.ParseDuration(c.ReportingPeriod)
+		if err != nil {
+			return nil, fmt.Errorf("invalid metric reporting period: %v", err)
+		}
+		if period <= 0 {
+
+			return nil, fmt.Errorf("invalid metric reporting period: %v", c.ReportingPeriod)
+		}
+	}
+	pusher := controller.New(
+		processor.New(
+			selector.NewWithInexpensiveDistribution(),
+			metricExporter,
+		),
+		controller.WithExporter(metricExporter),
+		controller.WithResource(c.Resource),
+		controller.WithCollectPeriod(period),
+	)
+
+	if err = pusher.Start(context.Background()); err != nil {
+		return nil, fmt.Errorf("failed to start controller: %v", err)
+	}
+
+	provider := pusher.MeterProvider()
+
+	if err = runtimeMetrics.Start(runtimeMetrics.WithMeterProvider(provider)); err != nil {
+		return nil, fmt.Errorf("failed to start runtime metrics: %v", err)
+	}
+
+	if err = hostMetrics.Start(hostMetrics.WithMeterProvider(provider)); err != nil {
+		return nil, fmt.Errorf("failed to start host metrics: %v", err)
+	}
+
+	metricglobal.SetMeterProvider(provider)
+	return func() error {
+		_ = pusher.Stop(context.Background())
+		return metricExporter.Shutdown(context.Background())
+	}, nil
+}
+
+func newMetricsExporter(endpoint string, insecure bool, headers map[string]string) (*otlpmetric.Exporter, error) {
+	secureOption := otlpmetricgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
+	if insecure {
+		secureOption = otlpmetricgrpc.WithInsecure()
+	}
+	return otlpmetric.New(
+		context.Background(),
+		otlpmetricgrpc.NewClient(
+			secureOption,
+			otlpmetricgrpc.WithEndpoint(endpoint),
+			otlpmetricgrpc.WithHeaders(headers),
+			otlpmetricgrpc.WithCompressor(gzip.Name),
+		),
+	)
+}
diff --git a/pipelines/trace.go b/pipelines/trace.go
new file mode 100644
index 0000000..82d1c94
--- /dev/null
+++ b/pipelines/trace.go
@@ -0,0 +1,95 @@
+// Copyright Lightstep 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 pipelines
+
+import (
+	"context"
+	"fmt"
+
+	"go.opentelemetry.io/contrib/propagators/b3"
+	"go.opentelemetry.io/contrib/propagators/ot"
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
+	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
+	"go.opentelemetry.io/otel/propagation"
+	"go.opentelemetry.io/otel/sdk/trace"
+	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/encoding/gzip"
+)
+
+func NewTracePipeline(c PipelineConfig) (func() error, error) {
+	spanExporter, err := newTraceExporter(c.Endpoint, c.Insecure, c.Headers)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create span exporter: %v", err)
+	}
+
+	bsp := trace.NewBatchSpanProcessor(spanExporter)
+	tp := trace.NewTracerProvider(
+		trace.WithSampler(trace.AlwaysSample()),
+		trace.WithSpanProcessor(bsp),
+		trace.WithResource(c.Resource),
+	)
+
+	if err = configurePropagators(c); err != nil {
+		return nil, err
+	}
+
+	otel.SetTracerProvider(tp)
+
+	return func() error {
+		_ = bsp.Shutdown(context.Background())
+		return spanExporter.Shutdown(context.Background())
+	}, nil
+}
+
+func newTraceExporter(endpoint string, insecure bool, headers map[string]string) (*otlptrace.Exporter, error) {
+	secureOption := otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
+	if insecure {
+		secureOption = otlptracegrpc.WithInsecure()
+	}
+	return otlptrace.New(
+		context.Background(),
+		otlptracegrpc.NewClient(
+			secureOption,
+			otlptracegrpc.WithEndpoint(endpoint),
+			otlptracegrpc.WithHeaders(headers),
+			otlptracegrpc.WithCompressor(gzip.Name),
+		),
+	)
+}
+
+// configurePropagators configures B3 propagation by default
+func configurePropagators(c PipelineConfig) error {
+	propagatorsMap := map[string]propagation.TextMapPropagator{
+		"b3":           b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)),
+		"baggage":      propagation.Baggage{},
+		"tracecontext": propagation.TraceContext{},
+		"ottrace":      ot.OT{},
+	}
+	var props []propagation.TextMapPropagator
+	for _, key := range c.Propagators {
+		prop := propagatorsMap[key]
+		if prop != nil {
+			props = append(props, prop)
+		}
+	}
+	if len(props) == 0 {
+		return fmt.Errorf("invalid configuration: unsupported propagators. Supported options: b3,baggage,tracecontext,ottrace")
+	}
+	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
+		props...,
+	))
+	return nil
+}
diff --git a/tools/go.mod b/tools/go.mod
index a49fd81..d83f891 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -1,6 +1,6 @@
 module github.com/lightstep/opentelemetry-go/tools
 
-go 1.14
+go 1.16
 
 require (
 	github.com/client9/misspell v0.3.4
diff --git a/tools/go.sum b/tools/go.sum
index 6306217..1de3e3e 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -85,7 +85,6 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
 github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=


Test patch:
diff --git a/launcher/config_test.go b/launcher/config_test.go
index 90266f7..8b14fcc 100644
--- a/launcher/config_test.go
+++ b/launcher/config_test.go
@@ -314,7 +314,6 @@ func TestDefaultConfig(t *testing.T) {
 		MetricExporterEndpointInsecure: false,
 		MetricReportingPeriod:          "30s",
 		MetricsEnabled:                 true,
-		AccessToken:                    "",
 		LogLevel:                       "info",
 		Propagators:                    []string{"b3"},
 		Resource:                       resource.NewWithAttributes(semconv.SchemaURL, attributes...),
@@ -350,7 +349,6 @@ func TestEnvironmentVariables(t *testing.T) {
 		MetricExporterEndpoint:         "metrics-url",
 		MetricExporterEndpointInsecure: true,
 		MetricReportingPeriod:          "30s",
-		AccessToken:                    "token",
 		LogLevel:                       "debug",
 		Propagators:                    []string{"b3", "w3c"},
 		Resource:                       resource.NewWithAttributes(semconv.SchemaURL, attributes...),
@@ -397,7 +395,7 @@ func TestConfigurationOverrides(t *testing.T) {
 		MetricExporterEndpoint:         "override-metrics-url",
 		MetricExporterEndpointInsecure: false,
 		MetricReportingPeriod:          "30s",
-		AccessToken:                    "override-access-token",
+		Headers:                        map[string]string{"lightstep-access-token": "override-access-token"},
 		LogLevel:                       "info",
 		Propagators:                    []string{"b3"},
 		Resource:                       resource.NewWithAttributes(semconv.SchemaURL, attributes...),