Skip to content

Commit

Permalink
[Do Not Merge] POC optional hook
Browse files Browse the repository at this point in the history
  • Loading branch information
mackjmr committed Oct 10, 2024
1 parent 5ac1607 commit 38cd183
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 24 deletions.
22 changes: 22 additions & 0 deletions collector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
receivers:
otlp:
protocols:
http:
endpoint: localhost:4317
cors:
allowed_origins:
- http://test.com
allowed_headers:
- Example-Header
max_age: 7200

exporters:
otlphttp:
endpoint: localhost:4318
max_idle_conns: 567

service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlphttp]
60 changes: 36 additions & 24 deletions config/confighttp/confighttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/config/internal"
"go.opentelemetry.io/collector/confmap/optional"
"go.opentelemetry.io/collector/extension/auth"
)

Expand Down Expand Up @@ -71,11 +72,11 @@ type ClientConfig struct {

// MaxIdleConns is used to set a limit to the maximum idle HTTP connections the client can keep open.
// By default, it is set to 100.
MaxIdleConns *int `mapstructure:"max_idle_conns"`
MaxIdleConns optional.Optional[int] `mapstructure:"max_idle_conns"`

// MaxIdleConnsPerHost is used to set a limit to the maximum idle HTTP connections the host can keep open.
// By default, it is set to [http.DefaultTransport.MaxIdleConnsPerHost].
MaxIdleConnsPerHost *int `mapstructure:"max_idle_conns_per_host"`
MaxIdleConnsPerHost optional.Optional[int] `mapstructure:"max_idle_conns_per_host"`

// MaxConnsPerHost limits the total number of connections per host, including connections in the dialing,
// active, and idle states.
Expand Down Expand Up @@ -122,11 +123,14 @@ func NewDefaultClientConfig() ClientConfig {
defaultTransport := http.DefaultTransport.(*http.Transport)

return ClientConfig{
ReadBufferSize: defaultTransport.ReadBufferSize,
WriteBufferSize: defaultTransport.WriteBufferSize,
Headers: map[string]configopaque.String{},
MaxIdleConns: &defaultTransport.MaxIdleConns,
MaxIdleConnsPerHost: &defaultTransport.MaxIdleConnsPerHost,
ReadBufferSize: defaultTransport.ReadBufferSize,
WriteBufferSize: defaultTransport.WriteBufferSize,
Headers: map[string]configopaque.String{},
MaxIdleConns: optional.Optional[int]{
Value: 12345,
HasValue: true,
},
MaxIdleConnsPerHost: optional.Optional[int]{},
MaxConnsPerHost: &defaultTransport.MaxConnsPerHost,
IdleConnTimeout: &defaultTransport.IdleConnTimeout,
}
Expand All @@ -149,12 +153,14 @@ func (hcs *ClientConfig) ToClient(ctx context.Context, host component.Host, sett
transport.WriteBufferSize = hcs.WriteBufferSize
}

if hcs.MaxIdleConns != nil {
transport.MaxIdleConns = *hcs.MaxIdleConns
fmt.Printf("MaxIdleConns: %+v\n", hcs.MaxIdleConns)
if hcs.MaxIdleConns.HasValue {
transport.MaxIdleConns = hcs.MaxIdleConns.Value
}

if hcs.MaxIdleConnsPerHost != nil {
transport.MaxIdleConnsPerHost = *hcs.MaxIdleConnsPerHost
fmt.Printf("MaxIdleConnsPerHost: %+v\n", hcs.MaxIdleConnsPerHost)
if hcs.MaxIdleConnsPerHost.HasValue {
transport.MaxIdleConnsPerHost = hcs.MaxIdleConnsPerHost.Value
}

if hcs.MaxConnsPerHost != nil {
Expand Down Expand Up @@ -279,10 +285,10 @@ type ServerConfig struct {
TLSSetting *configtls.ServerConfig `mapstructure:"tls"`

// CORS configures the server for HTTP cross-origin resource sharing (CORS).
CORS *CORSConfig `mapstructure:"cors"`
CORS optional.Optional[CORSConfig] `mapstructure:"cors"`

// Auth for this receiver
Auth *AuthConfig `mapstructure:"auth"`
Auth optional.Optional[AuthConfig] `mapstructure:"auth"`

// MaxRequestBodySize sets the maximum request body size in bytes. Default: 20MiB.
MaxRequestBodySize int64 `mapstructure:"max_request_body_size"`
Expand Down Expand Up @@ -334,9 +340,12 @@ type ServerConfig struct {
func NewDefaultServerConfig() ServerConfig {
tlsDefaultServerConfig := configtls.NewDefaultServerConfig()
return ServerConfig{
ResponseHeaders: map[string]configopaque.String{},
TLSSetting: &tlsDefaultServerConfig,
CORS: NewDefaultCORSConfig(),
ResponseHeaders: map[string]configopaque.String{},
TLSSetting: &tlsDefaultServerConfig,
CORS: optional.Optional[CORSConfig]{
Value: *NewDefaultCORSConfig(),
HasValue: true,
},
WriteTimeout: 30 * time.Second,
ReadHeaderTimeout: 1 * time.Minute,
IdleTimeout: 1 * time.Minute,
Expand Down Expand Up @@ -433,26 +442,29 @@ func (hss *ServerConfig) ToServer(_ context.Context, host component.Host, settin
handler = maxRequestBodySizeInterceptor(handler, hss.MaxRequestBodySize)
}

if hss.Auth != nil {
server, err := hss.Auth.GetServerAuthenticator(context.Background(), host.GetExtensions())
fmt.Printf("AUTH: %+v\n", hss.Auth)
if hss.Auth.HasValue {
server, err := hss.Auth.Value.GetServerAuthenticator(context.Background(), host.GetExtensions())
if err != nil {
return nil, err
}

handler = authInterceptor(handler, server, hss.Auth.RequestParameters)
handler = authInterceptor(handler, server, hss.Auth.Value.RequestParameters)
}

if hss.CORS != nil && len(hss.CORS.AllowedOrigins) > 0 {
fmt.Printf("CORS: %+v\n", hss.CORS)
if hss.CORS.HasValue && len(hss.CORS.Value.AllowedOrigins) > 0 {
co := cors.Options{
AllowedOrigins: hss.CORS.AllowedOrigins,
AllowedOrigins: hss.CORS.Value.AllowedOrigins,
AllowCredentials: true,
AllowedHeaders: hss.CORS.AllowedHeaders,
MaxAge: hss.CORS.MaxAge,
AllowedHeaders: hss.CORS.Value.AllowedHeaders,
MaxAge: hss.CORS.Value.MaxAge,
}
handler = cors.New(co).Handler(handler)
}
if hss.CORS != nil && len(hss.CORS.AllowedOrigins) == 0 && len(hss.CORS.AllowedHeaders) > 0 {
if hss.CORS.HasValue && len(hss.CORS.Value.AllowedOrigins) == 0 && len(hss.CORS.Value.AllowedHeaders) > 0 {
settings.Logger.Warn("The CORS configuration specifies allowed headers but no allowed origins, and is therefore ignored.")

}

if hss.ResponseHeaders != nil {
Expand Down
9 changes: 9 additions & 0 deletions config/confighttp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
go.opentelemetry.io/collector/config/configtelemetry v0.111.0
go.opentelemetry.io/collector/config/configtls v1.17.0
go.opentelemetry.io/collector/config/internal v0.111.0
go.opentelemetry.io/collector/confmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/extension/auth v0.111.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0
go.opentelemetry.io/otel v1.30.0
Expand All @@ -31,8 +32,14 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/providers/confmap v0.1.0 // indirect
github.com/knadh/koanf/v2 v2.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/collector/extension v0.111.0 // indirect
go.opentelemetry.io/collector/pdata v1.17.0 // indirect
Expand All @@ -50,6 +57,8 @@ require (

replace go.opentelemetry.io/collector/config/configauth => ../configauth

replace go.opentelemetry.io/collector/confmap => ../../confmap

replace go.opentelemetry.io/collector/config/configcompression => ../configcompression

replace go.opentelemetry.io/collector/config/configopaque => ../configopaque
Expand Down
12 changes: 12 additions & 0 deletions config/confighttp/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions confmap/confmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func decodeConfig(m *Conf, result any, errorUnused bool, skipTopLevelUnmarshaler
WeaklyTypedInput: false,
MatchName: caseSensitiveMatchName,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
optionalHookFunc(),
useExpandValue(),
expandNilStructPointersHookFunc(),
mapstructure.StringToSliceHookFunc(","),
Expand Down Expand Up @@ -431,6 +432,40 @@ func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue {
}
}

// optionalHookFunc applies logic when the to.Type is optional.Optional. When decoding primitive types,
// we use reflect.Value's FieldByName API (https://pkg.go.dev/reflect#Value.FieldByName) and Set (https://pkg.go.dev/reflect#Value.Set) APIs
// in order to set the Value and HasValue. Set cannot be used for struct field types, as the set value must be assignable to the target type,
// so for structs we use optionals custom unmarshaller in order to cast the map to the struct.
//
// This logic relies on the fact that the hook only gets called when the value is explicitely set in the config,
// so if we are in this hook, we know we can set HasValue to true.
func optionalHookFunc() mapstructure.DecodeHookFuncValue {
return func(from reflect.Value, to reflect.Value) (any, error) {
// As the type is generic, we check for the prefix.
if strings.HasPrefix(to.Type().String(), "optional.Optional") {
// the optional.Optional field is a struct
if _, ok := from.Interface().(map[string]any); ok {
unmarshaler, ok := to.Addr().Interface().(Unmarshaler)
if !ok {
return from.Interface(), nil
}
c := NewFromStringMap(from.Interface().(map[string]any))
if err := unmarshaler.Unmarshal(c); err != nil {
return nil, err
}
return to.Interface(), nil
}

// the optional.Optional field is a primitive type
to.FieldByName("Value").Set(from)
to.FieldByName("HasValue").SetBool(true)
return to.Interface(), nil
}

return from.Interface(), nil
}
}

// Provides a mechanism for individual structs to define their own unmarshal logic,
// by implementing the Unmarshaler interface, unless skipTopLevelUnmarshaler is
// true and the struct matches the top level object being unmarshaled.
Expand Down
16 changes: 16 additions & 0 deletions confmap/optional/optional.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package optional

import "go.opentelemetry.io/collector/confmap"

type Optional[T any] struct {
Value T
HasValue bool
}

func (o *Optional[T]) Unmarshal(conf *confmap.Conf) error {
o.HasValue = true
if err := conf.Unmarshal(&o.Value); err != nil {
return err
}
return nil
}

0 comments on commit 38cd183

Please sign in to comment.