Skip to content
This repository was archived by the owner on Jul 28, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Main (unreleased)

- [ENHANCEMENT] opentelemetry trace exporters can now be configured to support Oauth utilizing
the opentelemetry-collector-contrib oauth2clientauthextension. (@canuteson)

- [ENHANCEMENT] Strengthen readiness check for metrics instances. (@tpaschalis)

# v0.23.0 (2022-01-13)
Expand Down
25 changes: 25 additions & 0 deletions docs/user/configuration/traces-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ remote_write:
# the latter take precedence.
[ insecure_skip_verify: <bool> | default = false ]

# Configures opentelemetry exporters to use the OpenTelemetry auth extension `oauth2clientauthextension`.
# Can not be used in combination with `basic_auth`.
# See https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/extension/oauth2clientauthextension/README.md
oauth2:
# Configures the TLS settings specific to the oauth2 client
# The client identifier issued to the oauth client
[client_id: <string>]
# The secret string associated with the oauth client
[client_secret: <string>]
# The resource server's token endpoint URL
[token_url: <string>]
# Optional, requested permissions associated with the oauth client
[scopes: [<string>]]
# Optional, specifies the timeout fetching tokens from the token_url. Default: no timeout
[timeout: <duration>]
tls:
# Disable validation of the server certificate.
[ insecure: <bool> | default = false ]
# Path to the CA cert. For a client this verifies the server certificate. If empty uses system root CA.
[ca_file: <string>]
# Path to the TLS cert to use for TLS required connections
[cert_file: <string>]
# Path to the TLS key to use for TLS required connections
[key_file: <string>]

# Controls TLS settings of the exporter's client. See https://github.com/open-telemetry/opentelemetry-collector/blob/v0.21.0/config/configtls/README.md
# This should be used only if `insecure` is set to false
tls_config:
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/jaegerexporter v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanmetricsprocessor v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor v0.40.0
Expand Down Expand Up @@ -421,5 +422,3 @@ replace github.com/jaegertracing/jaeger => github.com/jaegertracing/jaeger v1.27

// Replacement necessary for windows_exporter so that we can use gokit logging and not the old prometheus logging
replace github.com/leoluk/perflib_exporter v0.1.0 => github.com/grafana/perflib_exporter v0.1.1-0.20211013152516-e37e14fb8b0a


8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,6 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDO
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig=
github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/aerospike/aerospike-client-go v1.27.0/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
Expand Down Expand Up @@ -1165,10 +1163,6 @@ github.com/grafana/statsd_exporter v0.18.1-0.20211118164740-8e806158da0b h1:eFIc
github.com/grafana/statsd_exporter v0.18.1-0.20211118164740-8e806158da0b/go.mod h1:N4Z1+iSqc9rnxlT1N8Qn3l65Vzb5t4Uq0jpg8nxyhio=
github.com/grafana/tail v0.0.0-20201004203643-7aa4e4a91f03 h1:fGgFrAraMB0BaPfYumu+iulfDXwHm+GFyHA4xEtBqI8=
github.com/grafana/tail v0.0.0-20201004203643-7aa4e4a91f03/go.mod h1:GIMXMPB/lRAllP5rVDvcGif87ryO2hgD7tCtHMdHrho=
github.com/grafana/windows_exporter v0.15.1-0.20211019183116-592dfa92f9fd h1:jQ9JCvwdRW32X/LP3ezXT4EnJCirq9bb3l5svQs8j7k=
github.com/grafana/windows_exporter v0.15.1-0.20211019183116-592dfa92f9fd/go.mod h1:zWjLDqyEy3ZEy1LNlR6iUJJgYCoUDJTyUbrjeLUp3ZE=
github.com/grafana/windows_exporter v0.15.1-0.20220202204425-17b8026ed2f5 h1:nGyxfTz81TvfGAZnlKdLp02MVBd/pEfYQq8RwgQQr5Y=
github.com/grafana/windows_exporter v0.15.1-0.20220202204425-17b8026ed2f5/go.mod h1:zWjLDqyEy3ZEy1LNlR6iUJJgYCoUDJTyUbrjeLUp3ZE=
github.com/grafana/windows_exporter v0.15.1-0.20220202211901-871715ba0b43 h1:gb+wDKb+9r4n3QbMzfudcHDtE9lI+kKjx+98bYphqK4=
github.com/grafana/windows_exporter v0.15.1-0.20220202211901-871715ba0b43/go.mod h1:zWjLDqyEy3ZEy1LNlR6iUJJgYCoUDJTyUbrjeLUp3ZE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
Expand Down Expand Up @@ -1782,6 +1776,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancing
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter v0.40.0/go.mod h1:8gCz0iEj986dJMmKwi/tYo8pdQj/mnBRiRkemi97XGw=
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter v0.40.0 h1:KCRIWJ8cqooPisXKhhoFPmq6Oo5nhb2X0XU/9uW6yAU=
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter v0.40.0/go.mod h1:kbjb5xSL0+VLPOaroPGXV9/aZKfHDx1NEzRGjX55avA=
github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension v0.40.0 h1:LAj9r9orM1tLKh+erijFOpmj5D7vk2RiIh73PZtY1Ko=
github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension v0.40.0/go.mod h1:KWUGnn6Ud3OS6AotkxSQt0m4U8Hm3QWQdI28cevlnQ8=
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.40.0 h1:FDoxyvSRJumeWNMMtwmyS6qz+5vDogkOAViz7EUvF6s=
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.40.0/go.mod h1:a56dESln9qTQfvLlG4iVrDSVsLzWaFS5363i8sAxqDo=
github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent v0.40.0 h1:IICNKhsUNFUw9qLmOVj4EkoTS+MYrGqAij+LgEM/f/c=
Expand Down
124 changes: 110 additions & 14 deletions pkg/traces/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ import (
"strings"
"time"

"github.com/grafana/agent/pkg/logs"
"github.com/grafana/agent/pkg/traces/automaticloggingprocessor"
"github.com/grafana/agent/pkg/traces/noopreceiver"
"github.com/grafana/agent/pkg/traces/promsdprocessor"
"github.com/grafana/agent/pkg/traces/remotewriteexporter"
"github.com/grafana/agent/pkg/traces/servicegraphprocessor"
"github.com/mitchellh/mapstructure"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/jaegerexporter"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter"
"github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension"
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor"
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanmetricsprocessor"
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor"
Expand All @@ -37,6 +33,14 @@ import (
"go.opentelemetry.io/collector/processor/batchprocessor"
"go.opentelemetry.io/collector/receiver/otlpreceiver"
"go.uber.org/multierr"

"github.com/grafana/agent/pkg/logs"
"github.com/grafana/agent/pkg/traces/automaticloggingprocessor"
"github.com/grafana/agent/pkg/traces/noopreceiver"
"github.com/grafana/agent/pkg/traces/promsdprocessor"
"github.com/grafana/agent/pkg/traces/remotewriteexporter"
"github.com/grafana/agent/pkg/traces/servicegraphprocessor"
"github.com/grafana/agent/pkg/util"
)

const (
Expand Down Expand Up @@ -160,6 +164,50 @@ var DefaultRemoteWriteConfig = RemoteWriteConfig{
Format: formatOtlp,
}

// TLSClientSetting configures the oauth2client extension TLS; compatible with configtls.TLSClientSetting
type TLSClientSetting struct {
CAFile string `yaml:"ca_file,omitempty"`
CertFile string `yaml:"cert_file,omitempty"`
KeyFile string `yaml:"key_file,omitempty"`
MinVersion string `yaml:"min_version,omitempty"`
MaxVersion string `yaml:"max_version,omitempty"`
Insecure bool `yaml:"insecure"`
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
ServerNameOverride string `yaml:"server_name_override,omitempty"`
}

// OAuth2Config configures the oauth2client extension for a remote_write exporter
// compatible with oauth2clientauthextension.Config
type OAuth2Config struct {
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
TokenURL string `yaml:"token_url"`
Scopes []string `yaml:"scopes,omitempty"`
TLS TLSClientSetting `yaml:"tls,omitempty"`
Timeout time.Duration `yaml:"timeout,omitempty"`
}

// Agent uses standard YAML unmarshalling, while the oauth2clientauthextension relies on
// mapstructure without providing YAML labels. `toOtelConfig` marshals `Oauth2Config` to configuration type expected by
// the oauth2clientauthextension Extension Factory
func (c OAuth2Config) toOtelConfig() (*oauth2clientauthextension.Config, error) {
var result *oauth2clientauthextension.Config
decoderConfig := &mapstructure.DecoderConfig{
MatchName: func(s, t string) bool { return util.CamelToSnake(s) == t },
Result: &result,
WeaklyTypedInput: true,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToSliceHookFunc(","),
mapstructure.StringToTimeDurationHookFunc(),
),
}
decoder, _ := mapstructure.NewDecoder(decoderConfig)
if err := decoder.Decode(c); err != nil {
return nil, err
}
return result, nil
}

// RemoteWriteConfig controls the configuration of an exporter
type RemoteWriteConfig struct {
Endpoint string `yaml:"endpoint,omitempty"`
Expand All @@ -171,6 +219,7 @@ type RemoteWriteConfig struct {
InsecureSkipVerify bool `yaml:"insecure_skip_verify,omitempty"`
TLSConfig *prom_config.TLSConfig `yaml:"tls_config,omitempty"`
BasicAuth *prom_config.BasicAuth `yaml:"basic_auth,omitempty"`
Oauth2 *OAuth2Config `yaml:"oauth2,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
SendingQueue map[string]interface{} `yaml:"sending_queue,omitempty"` // https://github.com/open-telemetry/opentelemetry-collector/blob/7d7ae2eb34b5d387627875c498d7f43619f37ee3/exporter/exporterhelper/queued_retry.go#L30
RetryOnFailure map[string]interface{} `yaml:"retry_on_failure,omitempty"` // https://github.com/open-telemetry/opentelemetry-collector/blob/7d7ae2eb34b5d387627875c498d7f43619f37ee3/exporter/exporterhelper/queued_retry.go#L54
Expand All @@ -181,6 +230,7 @@ func (c *RemoteWriteConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
*c = DefaultRemoteWriteConfig

type plain RemoteWriteConfig

if err := unmarshal((*plain)(c)); err != nil {
return err
}
Expand Down Expand Up @@ -253,6 +303,10 @@ func exporter(rwCfg RemoteWriteConfig) (map[string]interface{}, error) {
headers = rwCfg.Headers
}

if rwCfg.BasicAuth != nil && rwCfg.Oauth2 != nil {
return nil, fmt.Errorf("Only one auth type may be configured per exporter (basic_auth or oauth2)")
}

if rwCfg.BasicAuth != nil {
password := string(rwCfg.BasicAuth.Password)

Expand Down Expand Up @@ -318,21 +372,21 @@ func exporter(rwCfg RemoteWriteConfig) (map[string]interface{}, error) {
return exporter, nil
}

func getExporterName(protocol string, format string) (string, error) {
func getExporterName(index int, protocol string, format string) (string, error) {
switch format {
case formatOtlp:
switch protocol {
case protocolGRPC:
return "otlp", nil
return fmt.Sprintf("otlp/%d", index), nil
case protocolHTTP:
return "otlphttp", nil
return fmt.Sprintf("otlphttp/%d", index), nil
default:
return "", errors.New("unknown protocol, expected either 'http' or 'grpc'")
}
case formatJaeger:
switch protocol {
case protocolGRPC:
return "jaeger", nil
return fmt.Sprintf("jaeger/%d", index), nil
default:
return "", errors.New("unknown protocol, expected 'grpc'")
}
Expand All @@ -349,16 +403,42 @@ func (c *InstanceConfig) exporters() (map[string]interface{}, error) {
if err != nil {
return nil, err
}
exporterName, err := getExporterName(remoteWriteConfig.Protocol, remoteWriteConfig.Format)
exporterName, err := getExporterName(i, remoteWriteConfig.Protocol, remoteWriteConfig.Format)
if err != nil {
return nil, err
}
exporterName = fmt.Sprintf("%s/%d", exporterName, i)
if remoteWriteConfig.Oauth2 != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes more sense to me in the exporter() function since we build the rest of the config there.

I believe it's here b/c the exporterName is here? Perhaps we could adjust exporter() to take its own name and use it?

Oof, it also seems odd that getExporterName() doesn't take i. Tacking it on afterwards seems error prone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I agree.
66315ce

exporter["auth"] = map[string]string{"authenticator": getAuthExtensionName(exporterName)}
}
exporters[exporterName] = exporter
}
return exporters, nil
}

func getAuthExtensionName(exporterName string) string {
return fmt.Sprintf("oauth2client/%s", strings.Replace(exporterName, "/", "", -1))
}

// builds oauth2clientauth extensions required to support RemoteWriteConfigurations.
func (c *InstanceConfig) extensions() (map[string]interface{}, error) {
extensions := map[string]interface{}{}
for i, remoteWriteConfig := range c.RemoteWrite {
if remoteWriteConfig.Oauth2 == nil {
continue
}
exporterName, err := getExporterName(i, remoteWriteConfig.Protocol, remoteWriteConfig.Format)
if err != nil {
return nil, err
}
oauthConfig, err := remoteWriteConfig.Oauth2.toOtelConfig()
if err != nil {
return nil, err
}
extensions[getAuthExtensionName(exporterName)] = oauthConfig
}
return extensions, nil
}

func resolver(config map[string]interface{}) (map[string]interface{}, error) {
if len(config) == 0 {
return nil, fmt.Errorf("must configure one resolver (dns or static)")
Expand Down Expand Up @@ -435,6 +515,15 @@ func (c *InstanceConfig) otelConfig() (*config.Config, error) {
return nil, errors.New("must have at least one configured receiver")
}

extensions, err := c.extensions()
if err != nil {
return nil, err
}
extensionsNames := make([]string, 0, len(extensions))
for name := range extensions {
extensionsNames = append(extensionsNames, name)
}

exporters, err := c.exporters()
if err != nil {
return nil, err
Expand Down Expand Up @@ -603,14 +692,19 @@ func (c *InstanceConfig) otelConfig() (*config.Config, error) {

receiversMap := map[string]interface{}(c.Receivers)

otelMapStructure["extensions"] = extensions
otelMapStructure["exporters"] = exporters
otelMapStructure["processors"] = processors
otelMapStructure["receivers"] = receiversMap

// pipelines
otelMapStructure["service"] = map[string]interface{}{
serviceMap := map[string]interface{}{
"pipelines": pipelines,
}
if len(extensionsNames) > 0 {
serviceMap["extensions"] = extensionsNames
}
otelMapStructure["service"] = serviceMap

factories, err := tracingFactories()
if err != nil {
Expand All @@ -633,7 +727,9 @@ func (c *InstanceConfig) otelConfig() (*config.Config, error) {
// tracingFactories() only creates the needed factories. if we decide to add support for a new
// processor, exporter, receiver we need to add it here
func tracingFactories() (component.Factories, error) {
extensions, err := component.MakeExtensionFactoryMap()
extensions, err := component.MakeExtensionFactoryMap(
oauth2clientauthextension.NewFactory(),
)
if err != nil {
return component.Factories{}, err
}
Expand Down
Loading