From 8ce36d7934dd8bb90b2d72369177a62217e3fbf3 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:47:21 -0700 Subject: [PATCH 1/5] middleware: extension API --- extension/extensionmiddleware/Makefile | 1 + extension/extensionmiddleware/README.md | 27 ++++++++++++++++++++ extension/extensionmiddleware/client.go | 22 ++++++++++++++++ extension/extensionmiddleware/go.mod | 13 ++++++++++ extension/extensionmiddleware/go.sum | 34 +++++++++++++++++++++++++ extension/extensionmiddleware/server.go | 22 ++++++++++++++++ 6 files changed, 119 insertions(+) create mode 100644 extension/extensionmiddleware/Makefile create mode 100644 extension/extensionmiddleware/README.md create mode 100644 extension/extensionmiddleware/client.go create mode 100644 extension/extensionmiddleware/go.mod create mode 100644 extension/extensionmiddleware/go.sum create mode 100644 extension/extensionmiddleware/server.go diff --git a/extension/extensionmiddleware/Makefile b/extension/extensionmiddleware/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/extension/extensionmiddleware/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/extension/extensionmiddleware/README.md b/extension/extensionmiddleware/README.md new file mode 100644 index 000000000000..7f6b554e2e86 --- /dev/null +++ b/extension/extensionmiddleware/README.md @@ -0,0 +1,27 @@ +# OpenTelemetry Collector Middleware Extension API + +This package implements interfaces for injecting middleware behavior +in OpenTelemetry Collector exporters and receivers. See [the +associated `configmiddleware` package](../../config/configmiddleware/README.md) +for referring to middleware extensions in component configurations. + +## Overview + +Middleware extensions can be configured on gRPC and HTTP connections, +on both the client and server side. + +## Interfaces + +1. **HTTPClient**: The extension returns a function to create new `http.RoundTripper`s. + +2. **HTTPServer**: The extension returns a function to create new `http.Handler`s. + +3. **GRPCClient**: The extension returns `[]grpc.DialOption`. + +4. **GRPCServer**: The extension returns `[]grpc.ServerOption`. + +Each interface has a single function, which is typically called when a +component configures its network connection. An error is returned if +the named extension cannot be configured. + +Note that this module is tested through `configmiddleware`. diff --git a/extension/extensionmiddleware/client.go b/extension/extensionmiddleware/client.go new file mode 100644 index 000000000000..7b70bf82b0a5 --- /dev/null +++ b/extension/extensionmiddleware/client.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package extensionmiddleware // import "go.opentelemetry.io/collector/extension/extensionmiddleware" + +import ( + "net/http" + + "google.golang.org/grpc" +) + +// HTTPClient is an interface for HTTP client middleware extensions. +type HTTPClient interface { + // GetHTTPRoundTripper wraps the provided client RoundTripper. + GetHTTPRoundTripper(http.RoundTripper) (http.RoundTripper, error) +} + +// GRPCClient is an interface for gRPC client middleware extensions. +type GRPCClient interface { + // GetGRPCClientOptions returns the gRPC dial options to use for client connections. + GetGRPCClientOptions() ([]grpc.DialOption, error) +} diff --git a/extension/extensionmiddleware/go.mod b/extension/extensionmiddleware/go.mod new file mode 100644 index 000000000000..af068abd11e5 --- /dev/null +++ b/extension/extensionmiddleware/go.mod @@ -0,0 +1,13 @@ +module go.opentelemetry.io/collector/extension/extensionmiddleware + +go 1.23.0 + +require google.golang.org/grpc v1.71.0 + +require ( + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/protobuf v1.36.4 // indirect +) diff --git a/extension/extensionmiddleware/go.sum b/extension/extensionmiddleware/go.sum new file mode 100644 index 000000000000..04d7b93609a7 --- /dev/null +++ b/extension/extensionmiddleware/go.sum @@ -0,0 +1,34 @@ +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/extension/extensionmiddleware/server.go b/extension/extensionmiddleware/server.go new file mode 100644 index 000000000000..46fdb105126e --- /dev/null +++ b/extension/extensionmiddleware/server.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package extensionmiddleware // import "go.opentelemetry.io/collector/extension/extensionmiddleware" + +import ( + "net/http" + + "google.golang.org/grpc" +) + +// HTTPServer defines the interface for HTTP server middleware extensions. +type HTTPServer interface { + // GetHTTPHandler wraps the provided base http.Handler. + GetHTTPHandler(base http.Handler) (http.Handler, error) +} + +// GRPCServer defines the interface for gRPC server middleware extensions. +type GRPCServer interface { + // GetGRPCServerOptions returns options for a gRPC server. + GetGRPCServerOptions() ([]grpc.ServerOption, error) +} From 60cbdfc00625d2d4145f5b445f95f377ab24b250 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:47:31 -0700 Subject: [PATCH 2/5] middleware: config struct --- config/configmiddleware/Makefile | 1 + config/configmiddleware/README.md | 37 +++ config/configmiddleware/configmiddleware.go | 92 ++++++ .../configmiddleware/configmiddleware_test.go | 277 ++++++++++++++++++ config/configmiddleware/go.mod | 50 ++++ config/configmiddleware/go.sum | 95 ++++++ 6 files changed, 552 insertions(+) create mode 100644 config/configmiddleware/Makefile create mode 100644 config/configmiddleware/README.md create mode 100644 config/configmiddleware/configmiddleware.go create mode 100644 config/configmiddleware/configmiddleware_test.go create mode 100644 config/configmiddleware/go.mod create mode 100644 config/configmiddleware/go.sum diff --git a/config/configmiddleware/Makefile b/config/configmiddleware/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/config/configmiddleware/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/config/configmiddleware/README.md b/config/configmiddleware/README.md new file mode 100644 index 000000000000..32f76d327cbf --- /dev/null +++ b/config/configmiddleware/README.md @@ -0,0 +1,37 @@ +# OpenTelemetry Collector Middleware Configuration + +This package implements a configuration struct for referring to +[middleware extensions](../../extension/extensionmiddleware/README.md). + +## Overview + +The `configmiddleware` package defines a `Middleware` type that +allows components to configure middleware extensions, typically as +an ordered list. +This support is built in for push-based receivers configured through +`confighttp` and `configgrpc`, as for example in the OTLP receiver: + +```yaml +receivers: + otlp: + protocols: + http: + middlewares: + - middleware: limitermiddleware +``` + +## Methods + +The package provides four key methods to retrieve appropriate middleware handlers: + +1. **GetHTTPClientRoundTripper**: Obtains a function to wrap an HTTP client with a middleware extension via a `http.RoundTripper`. + +2. **GetHTTPServerHandler**: Obtains a function to wrap an HTTP server with a middleware extension via a `http.Handler`. + +3. **GetGRPCClientOptions**: Obtains a `[]grpc.DialOption` that configure a middleware extension for gRPC clients. + +4. **GetGRPCServerOptions**: Obtains a `[]grpc.ServerOption` that configure a middleware extension for gRPC servers. + +These functions are typically called during Start() by a component, +passing the `component.Host` extensions. +An error is returned if the named extension cannot be found. diff --git a/config/configmiddleware/configmiddleware.go b/config/configmiddleware/configmiddleware.go new file mode 100644 index 000000000000..1b3e990b011b --- /dev/null +++ b/config/configmiddleware/configmiddleware.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package configmiddleware implements a configuration struct to +// name middleware extensions. +package configmiddleware // import "go.opentelemetry.io/collector/config/configmiddleware" + +import ( + "context" + "errors" + "fmt" + "net/http" + + "google.golang.org/grpc" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension/extensionmiddleware" +) + +var ( + errMiddlewareNotFound = errors.New("middleware not found") + errNotHTTPServer = errors.New("requested extension is not an HTTP server middleware") + errNotGRPCServer = errors.New("requested extension is not a gRPC server middleware") + errNotHTTPClient = errors.New("requested extension is not an HTTP client middleware") + errNotGRPCClient = errors.New("requested extension is not a gRPC client middleware") +) + +// Middleware defines the extension ID for a middleware component. +type Middleware struct { + // MiddlewareID specifies the name of the extension to use. + MiddlewareID component.ID `mapstructure:"middleware,omitempty"` +} + +// GetHTTPClientRoundTripper attempts to select the appropriate +// extensionmiddleware.HTTPClient from the map of extensions, and +// returns the HTTP client wrapper function. If a middleware is not +// found, an error is returned. This should only be used by HTTP +// clients. +func (m Middleware) GetHTTPClientRoundTripper(_ context.Context, extensions map[component.ID]component.Component) (func(http.RoundTripper) (http.RoundTripper, error), error) { + if ext, found := extensions[m.MiddlewareID]; found { + if client, ok := ext.(extensionmiddleware.HTTPClient); ok { + return client.GetHTTPRoundTripper, nil + } + return nil, errNotHTTPClient + } + return nil, fmt.Errorf("failed to resolve middleware %q: %w", m.MiddlewareID, errMiddlewareNotFound) +} + +// GetHTTPServerHandler attempts to select the appropriate +// extensionmiddleware.HTTPServer from the map of extensions, and +// returns the http.Handler wrapper function. If a middleware is not +// found, an error is returned. This should only be used by HTTP +// servers. +func (m Middleware) GetHTTPServerHandler(_ context.Context, extensions map[component.ID]component.Component) (func(http.Handler) (http.Handler, error), error) { + if ext, found := extensions[m.MiddlewareID]; found { + if server, ok := ext.(extensionmiddleware.HTTPServer); ok { + return server.GetHTTPHandler, nil + } + return nil, errNotHTTPServer + } + + return nil, fmt.Errorf("failed to resolve middleware %q: %w", m.MiddlewareID, errMiddlewareNotFound) +} + +// GetGRPCClientOptions attempts to select the appropriate +// extensionmiddleware.GRPCClient from the map of extensions, and +// returns the gRPC dial options. If a middleware is not found, an +// error is returned. This should only be used by gRPC clients. +func (m Middleware) GetGRPCClientOptions(_ context.Context, extensions map[component.ID]component.Component) ([]grpc.DialOption, error) { + if ext, found := extensions[m.MiddlewareID]; found { + if client, ok := ext.(extensionmiddleware.GRPCClient); ok { + return client.GetGRPCClientOptions() + } + return nil, errNotGRPCClient + } + return nil, fmt.Errorf("failed to resolve middleware %q: %w", m.MiddlewareID, errMiddlewareNotFound) +} + +// GetGRPCServerOptions attempts to select the appropriate +// extensionmiddleware.GRPCServer from the map of extensions, and +// returns the gRPC server options. If a middleware is not found, an +// error is returned. This should only be used by gRPC servers. +func (m Middleware) GetGRPCServerOptions(_ context.Context, extensions map[component.ID]component.Component) ([]grpc.ServerOption, error) { + if ext, found := extensions[m.MiddlewareID]; found { + if server, ok := ext.(extensionmiddleware.GRPCServer); ok { + return server.GetGRPCServerOptions() + } + return nil, errNotGRPCServer + } + + return nil, fmt.Errorf("failed to resolve middleware %q: %w", m.MiddlewareID, errMiddlewareNotFound) +} diff --git a/config/configmiddleware/configmiddleware_test.go b/config/configmiddleware/configmiddleware_test.go new file mode 100644 index 000000000000..aecd7a525457 --- /dev/null +++ b/config/configmiddleware/configmiddleware_test.go @@ -0,0 +1,277 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package configmiddleware + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + + "go.opentelemetry.io/collector/component" +) + +var testID = component.MustNewID("test") + +// mockComponent is a base mock that implements component.Component +type mockComponent struct{} + +func (m mockComponent) Start(_ context.Context, _ component.Host) error { + return nil +} + +func (m mockComponent) Shutdown(_ context.Context) error { + return nil +} + +// mockHTTPServerMiddleware implements extensionmiddleware.HTTPServer +type mockHTTPServerMiddleware struct { + mockComponent +} + +func (m mockHTTPServerMiddleware) GetHTTPHandler(next http.Handler) (http.Handler, error) { + return next, nil +} + +// mockHTTPClientMiddleware implements extensionmiddleware.HTTPClient +type mockHTTPClientMiddleware struct { + mockComponent +} + +func (m mockHTTPClientMiddleware) GetHTTPRoundTripper(next http.RoundTripper) (http.RoundTripper, error) { + return next, nil +} + +// mockGRPCServerMiddleware implements extensionmiddleware.GRPCServer +type mockGRPCServerMiddleware struct { + mockComponent +} + +func (m mockGRPCServerMiddleware) GetGRPCServerOptions() ([]grpc.ServerOption, error) { + return []grpc.ServerOption{}, nil +} + +// mockGRPCClientMiddleware implements extensionmiddleware.GRPCClient +type mockGRPCClientMiddleware struct { + mockComponent +} + +func (m mockGRPCClientMiddleware) GetGRPCClientOptions() ([]grpc.DialOption, error) { + return []grpc.DialOption{}, nil +} + +func TestMiddleware_GetHTTPServerHandler(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + middleware Middleware + extensions map[component.ID]component.Component + wantErr error + }{ + { + name: "found_and_valid", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockHTTPServerMiddleware{}, + }, + wantErr: nil, + }, + { + name: "middleware_not_found", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{}, + wantErr: errMiddlewareNotFound, + }, + { + name: "middleware_wrong_type", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockComponent{}, + }, + wantErr: errNotHTTPServer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value, err := tt.middleware.GetHTTPServerHandler(ctx, tt.extensions) + + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + assert.NotNil(t, value) + } + }) + } +} + +func TestMiddleware_GetHTTPClientRoundTripper(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + middleware Middleware + extensions map[component.ID]component.Component + wantErr error + }{ + { + name: "found_and_valid", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockHTTPClientMiddleware{}, + }, + wantErr: nil, + }, + { + name: "middleware_not_found", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{}, + wantErr: errMiddlewareNotFound, + }, + { + name: "middleware_wrong_type", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockComponent{}, + }, + wantErr: errNotHTTPClient, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value, err := tt.middleware.GetHTTPClientRoundTripper(ctx, tt.extensions) + + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + assert.NotNil(t, value) + } + }) + } +} + +func TestMiddleware_GetGRPCServerOptions(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + middleware Middleware + extensions map[component.ID]component.Component + wantErr error + }{ + { + name: "found_and_valid", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockGRPCServerMiddleware{}, + }, + wantErr: nil, + }, + { + name: "middleware_not_found", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{}, + wantErr: errMiddlewareNotFound, + }, + { + name: "middleware_wrong_type", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockComponent{}, + }, + wantErr: errNotGRPCServer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value, err := tt.middleware.GetGRPCServerOptions(ctx, tt.extensions) + + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + assert.NotNil(t, value) + } + }) + } +} + +func TestMiddleware_GetGRPCClientOptions(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + middleware Middleware + extensions map[component.ID]component.Component + wantErr error + }{ + { + name: "found_and_valid", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockGRPCClientMiddleware{}, + }, + wantErr: nil, + }, + { + name: "middleware_not_found", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{}, + wantErr: errMiddlewareNotFound, + }, + { + name: "middleware_wrong_type", + middleware: Middleware{ + MiddlewareID: testID, + }, + extensions: map[component.ID]component.Component{ + testID: mockComponent{}, + }, + wantErr: errNotGRPCClient, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value, err := tt.middleware.GetGRPCClientOptions(ctx, tt.extensions) + + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + assert.NotNil(t, value) + } + }) + } +} diff --git a/config/configmiddleware/go.mod b/config/configmiddleware/go.mod new file mode 100644 index 000000000000..4e004273f191 --- /dev/null +++ b/config/configmiddleware/go.mod @@ -0,0 +1,50 @@ +module go.opentelemetry.io/collector/config/configmiddleware + +go 1.23.0 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component v1.28.1 + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 + google.golang.org/grpc v1.71.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/featuregate v1.30.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect + go.opentelemetry.io/collector/pdata v1.30.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/log v0.11.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector/component => ../../component + +replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/pdata => ../../pdata + +replace go.opentelemetry.io/collector/pipeline => ../../pipeline + +replace go.opentelemetry.io/collector/featuregate => ../../featuregate diff --git a/config/configmiddleware/go.sum b/config/configmiddleware/go.sum new file mode 100644 index 000000000000..7cbc4e91eef7 --- /dev/null +++ b/config/configmiddleware/go.sum @@ -0,0 +1,95 @@ +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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 h1:ojdSRDvjrnm30beHOmwsSvLpoRF40MlwNCA+Oo93kXU= +go.opentelemetry.io/contrib/bridges/otelzap v0.10.0/go.mod h1:oTTm4g7NEtHSV2i/0FeVdPaPgUIZPfQkFbq0vbzqnv0= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y= +go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 88ace4f11822ea28f4ec5e8e39a720d530c6a85d Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:47:41 -0700 Subject: [PATCH 3/5] middleware: http support --- config/confighttp/README.md | 2 + config/confighttp/client_middleware_test.go | 159 ++++++++++++++++++++ config/confighttp/confighttp.go | 45 +++++- config/confighttp/go.mod | 6 + config/confighttp/server_middleware_test.go | 138 +++++++++++++++++ config/confighttp/xconfighttp/go.mod | 6 + 6 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 config/confighttp/client_middleware_test.go create mode 100644 config/confighttp/server_middleware_test.go diff --git a/config/confighttp/README.md b/config/confighttp/README.md index 1f1cb1bba27e..1ee56939814b 100644 --- a/config/confighttp/README.md +++ b/config/confighttp/README.md @@ -58,6 +58,7 @@ README](../configtls/README.md). - [`http2_ping_timeout`](https://pkg.go.dev/golang.org/x/net/http2#Transport) - [`cookies`](https://pkg.go.dev/net/http#CookieJar) - [`enabled`] if enabled, the client will store cookies from server responses and reuse them in subsequent requests. +- [`middlewares`](../configmiddleware/README.md) Example: @@ -107,6 +108,7 @@ will not be enabled. - [`tls`](../configtls/README.md) - [`auth`](../configauth/README.md) - `request_params`: a list of query parameter names to add to the auth context, along with the HTTP headers +- [`middlewares`](../configmiddleware/README.md) You can enable [`attribute processor`][attribute-processor] to append any http header to span's attribute using custom key. You also need to enable the "include_metadata" diff --git a/config/confighttp/client_middleware_test.go b/config/confighttp/client_middleware_test.go new file mode 100644 index 000000000000..a5424897d7e0 --- /dev/null +++ b/config/confighttp/client_middleware_test.go @@ -0,0 +1,159 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package confighttp + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configmiddleware" + "go.opentelemetry.io/collector/extension/extensionmiddleware" +) + +// testClientMiddleware is a test middleware that appends a string to the response body +type testClientMiddleware struct { + name string +} + +func (*testClientMiddleware) Start(_ context.Context, _ component.Host) error { + return nil +} + +func (*testClientMiddleware) Shutdown(_ context.Context) error { + return nil +} + +var _ extensionmiddleware.HTTPClient = &testClientMiddleware{} + +// GetHTTPRoundTripper creates a round tripper that appends text to the response body +func (m *testClientMiddleware) GetHTTPRoundTripper(transport http.RoundTripper) (http.RoundTripper, error) { + return &bodyAppenderRoundTripper{ + name: m.name, + transport: transport, + }, nil +} + +// bodyAppenderRoundTripper is a round tripper that appends text to the response body +type bodyAppenderRoundTripper struct { + name string + transport http.RoundTripper +} + +// RoundTrip appends text to the response body +func (rt *bodyAppenderRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + resp, err := rt.transport.RoundTrip(req) + if err != nil { + return resp, err + } + + // Read the original body + body, err := io.ReadAll(resp.Body) + if err != nil { + return resp, err + } + _ = resp.Body.Close() + + // Create a new body with the appended text + newBody := string(body) + "\r\noutput by " + rt.name + + // Replace the response body + resp.Body = io.NopCloser(strings.NewReader(newBody)) + resp.ContentLength = int64(len(newBody)) + + return resp, nil +} + +func newTestClientMiddleware(name string) component.Component { + return &testClientMiddleware{ + name: name, + } +} + +func newTestClientConfig(name string) configmiddleware.Middleware { + return configmiddleware.Middleware{ + MiddlewareID: component.MustNewID(name), + } +} + +func TestClientMiddlewares(t *testing.T) { + // Create a test server that returns "OK" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + })) + defer server.Close() + + // Register two test extensions + host := &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("test1"): newTestClientMiddleware("test1"), + component.MustNewID("test2"): newTestClientMiddleware("test2"), + }, + } + + // Test with different middleware configurations + testCases := []struct { + name string + middlewares []configmiddleware.Middleware + expectedOutput string + }{ + { + name: "no_middlewares", + middlewares: nil, + expectedOutput: "OK", + }, + { + name: "single_middleware", + middlewares: []configmiddleware.Middleware{ + newTestClientConfig("test1"), + }, + expectedOutput: "OK\r\noutput by test1", + }, + { + name: "multiple_middlewares", + middlewares: []configmiddleware.Middleware{ + newTestClientConfig("test1"), + newTestClientConfig("test2"), + }, + expectedOutput: "OK\r\noutput by test2\r\noutput by test1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create HTTP client config with the test middlewares + clientConfig := ClientConfig{ + Endpoint: server.URL, + Middlewares: tc.middlewares, + } + + // Create the client + client, err := clientConfig.ToClient(context.Background(), host, componenttest.NewNopTelemetrySettings()) + require.NoError(t, err) + + // Create a request to the test server + req, err := http.NewRequest(http.MethodGet, server.URL, nil) + require.NoError(t, err) + + // Send the request + resp, err := client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + // Check the response + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, tc.expectedOutput, string(body)) + }) + } +} diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 43193c957ee0..1d8f8b68c48d 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/collector/config/configauth" "go.opentelemetry.io/collector/config/configcompression" "go.opentelemetry.io/collector/config/confighttp/internal" + "go.opentelemetry.io/collector/config/configmiddleware" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" "go.opentelemetry.io/collector/extension/extensionauth" @@ -111,6 +112,11 @@ type ClientConfig struct { HTTP2PingTimeout time.Duration `mapstructure:"http2_ping_timeout,omitempty"` // Cookies configures the cookie management of the HTTP client. Cookies *CookiesConfig `mapstructure:"cookies,omitempty"` + + // Middlewares are used to add custom functionality to the HTTP client. + // Middleware handlers are applied in the order they appear in this list, + // with the first middleware becoming the outermost handler. + Middlewares []configmiddleware.Middleware `mapstructure:"middleware,omitempty"` } // CookiesConfig defines the configuration of the HTTP client regarding cookies served by the server. @@ -194,6 +200,22 @@ func (hcs *ClientConfig) ToClient(ctx context.Context, host component.Host, sett clientTransport := (http.RoundTripper)(transport) + // Apply middlewares in reverse order so they are applied in + // order. The first middleware runs after authentication. + for i := len(hcs.Middlewares) - 1; i >= 0; i-- { + var wrapper func(http.RoundTripper) (http.RoundTripper, error) + wrapper, err = hcs.Middlewares[i].GetHTTPClientRoundTripper(ctx, host.GetExtensions()) + // If we failed to get the middleware + if err != nil { + return nil, err + } + clientTransport, err = wrapper(clientTransport) + // If we failed to construct a wrapper + if err != nil { + return nil, err + } + } + // The Auth RoundTripper should always be the innermost to ensure that // request signing-based auth mechanisms operate after compression // and header middleware modifies the request @@ -338,6 +360,11 @@ type ServerConfig struct { // is zero, the value of ReadTimeout is used. If both are // zero, there is no timeout. IdleTimeout time.Duration `mapstructure:"idle_timeout"` + + // Middlewares are used to add custom functionality to the HTTP server. + // Middleware handlers are applied in the order they appear in this list, + // with the first middleware becoming the outermost handler. + Middlewares []configmiddleware.Middleware `mapstructure:"middleware,omitempty"` } // NewDefaultServerConfig returns ServerConfig type object with default values. @@ -411,7 +438,7 @@ func WithDecoder(key string, dec func(body io.ReadCloser) (io.ReadCloser, error) } // ToServer creates an http.Server from settings object. -func (hss *ServerConfig) ToServer(_ context.Context, host component.Host, settings component.TelemetrySettings, handler http.Handler, opts ...ToServerOption) (*http.Server, error) { +func (hss *ServerConfig) ToServer(ctx context.Context, host component.Host, settings component.TelemetrySettings, handler http.Handler, opts ...ToServerOption) (*http.Server, error) { serverOpts := &toServerOptions{} serverOpts.Apply(opts...) @@ -423,6 +450,22 @@ func (hss *ServerConfig) ToServer(_ context.Context, host component.Host, settin hss.CompressionAlgorithms = defaultCompressionAlgorithms } + // Apply middlewares in reverse order so they are applied in + // order. The first middleware runs after decompression, + // below, preceded by Auth, CORS, etc. + for i := len(hss.Middlewares) - 1; i >= 0; i-- { + wrapper, err := hss.Middlewares[i].GetHTTPServerHandler(ctx, host.GetExtensions()) + // If we failed to get the middleware + if err != nil { + return nil, err + } + handler, err = wrapper(handler) + // If we failed to construct a wrapper + if err != nil { + return nil, err + } + } + handler = httpContentDecompressor( handler, hss.MaxRequestBodySize, diff --git a/config/confighttp/go.mod b/config/confighttp/go.mod index 2b596b18edfb..c9f28edf0276 100644 --- a/config/confighttp/go.mod +++ b/config/confighttp/go.mod @@ -13,11 +13,13 @@ require ( go.opentelemetry.io/collector/component/componenttest v0.124.0 go.opentelemetry.io/collector/config/configauth v0.124.0 go.opentelemetry.io/collector/config/configcompression v1.30.0 + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 go.opentelemetry.io/collector/config/configopaque v1.30.0 go.opentelemetry.io/collector/config/configtls v1.30.0 go.opentelemetry.io/collector/extension v1.30.0 go.opentelemetry.io/collector/extension/extensionauth v1.30.0 go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest v0.124.0 + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 go.opentelemetry.io/otel v1.35.0 go.uber.org/goleak v1.3.0 @@ -58,6 +60,8 @@ replace go.opentelemetry.io/collector/config/configauth => ../configauth replace go.opentelemetry.io/collector/config/configcompression => ../configcompression +replace go.opentelemetry.io/collector/config/configmiddleware => ../configmiddleware + replace go.opentelemetry.io/collector/config/configopaque => ../configopaque replace go.opentelemetry.io/collector/config/configtls => ../configtls @@ -66,6 +70,8 @@ replace go.opentelemetry.io/collector/extension => ../../extension replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + replace go.opentelemetry.io/collector/pdata => ../../pdata replace go.opentelemetry.io/collector/component => ../../component diff --git a/config/confighttp/server_middleware_test.go b/config/confighttp/server_middleware_test.go new file mode 100644 index 000000000000..bc9c2ebec7ac --- /dev/null +++ b/config/confighttp/server_middleware_test.go @@ -0,0 +1,138 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package confighttp + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configmiddleware" +) + +// testServerMiddleware is a test implementation of configmiddleware.Middleware +type testServerMiddleware struct { + name string +} + +func (*testServerMiddleware) Start(_ context.Context, _ component.Host) error { + return nil +} + +func (*testServerMiddleware) Shutdown(_ context.Context) error { + return nil +} + +func (tm *testServerMiddleware) GetHTTPHandler(handler http.Handler) (http.Handler, error) { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Append middleware name to the URL path + r.URL.Path += tm.name + "/" + + // Call the next handler in the chain + handler.ServeHTTP(w, r) + + // Add middleware name to the response + _, _ = w.Write([]byte("\r\nserved by " + tm.name)) + }), nil +} + +func newTestServerMiddleware(name string) component.Component { + return &testServerMiddleware{ + name: name, + } +} + +func newTestServerConfig(name string) configmiddleware.Middleware { + return configmiddleware.Middleware{ + MiddlewareID: component.MustNewID(name), + } +} + +func TestServerMiddleware(t *testing.T) { + // Register two test extensions + host := &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("test1"): newTestServerMiddleware("test1"), + component.MustNewID("test2"): newTestServerMiddleware("test2"), + }, + } + + // Test with different middleware configurations + testCases := []struct { + name string + middlewares []configmiddleware.Middleware + expectedOutput string + }{ + { + name: "no_middlewares", + middlewares: nil, + expectedOutput: "OK{/}", + }, + { + name: "single_middleware", + middlewares: []configmiddleware.Middleware{ + newTestServerConfig("test1"), + }, + expectedOutput: "OK{/test1/}\r\nserved by test1", + }, + { + name: "multiple_middlewares", + middlewares: []configmiddleware.Middleware{ + newTestServerConfig("test1"), + newTestServerConfig("test2"), + }, + expectedOutput: "OK{/test1/test2/}\r\nserved by test2\r\nserved by test1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create server config with the test middlewares + cfg := ServerConfig{ + Endpoint: "localhost:0", + Middlewares: tc.middlewares, + } + + // Create a test handler that responds with the request path + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("OK{" + r.URL.Path + "}")) + }) + + // Create the server + srv, err := cfg.ToServer( + context.Background(), + host, + componenttest.NewNopTelemetrySettings(), + handler, + ) + require.NoError(t, err) + + // Create a test request + req := httptest.NewRequest(http.MethodGet, "/", nil) + + // Create a response recorder + rec := httptest.NewRecorder() + + // Serve the request + srv.Handler.ServeHTTP(rec, req) + + // Get the response + resp := rec.Result() + defer resp.Body.Close() + + // Check the response + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, tc.expectedOutput, string(body)) + }) + } +} diff --git a/config/confighttp/xconfighttp/go.mod b/config/confighttp/xconfighttp/go.mod index 53545c9d5383..12da6a075184 100644 --- a/config/confighttp/xconfighttp/go.mod +++ b/config/confighttp/xconfighttp/go.mod @@ -30,9 +30,11 @@ require ( go.opentelemetry.io/collector/component v1.30.0 // indirect go.opentelemetry.io/collector/config/configauth v0.124.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/config/configopaque v1.30.0 // indirect go.opentelemetry.io/collector/config/configtls v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect go.opentelemetry.io/collector/pdata v1.30.0 // indirect @@ -83,3 +85,7 @@ replace go.opentelemetry.io/collector/pipeline => ../../../pipeline replace go.opentelemetry.io/collector/internal/telemetry => ../../../internal/telemetry replace go.opentelemetry.io/collector/featuregate => ../../../featuregate + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../configmiddleware From 4bf2b6a7cf6aaafd29126d5ec63b19ba4b00da1b Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:47:49 -0700 Subject: [PATCH 4/5] middleware: grpc support --- config/configgrpc/README.md | 2 + config/configgrpc/client_middleware_test.go | 137 ++++++++++++++++++++ config/configgrpc/configgrpc.go | 30 ++++- config/configgrpc/configgrpc_test.go | 19 ++- config/configgrpc/go.mod | 6 + config/configgrpc/server_middleware_test.go | 110 ++++++++++++++++ 6 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 config/configgrpc/client_middleware_test.go create mode 100644 config/configgrpc/server_middleware_test.go diff --git a/config/configgrpc/README.md b/config/configgrpc/README.md index e0db0421e7ef..1fb4037b274d 100644 --- a/config/configgrpc/README.md +++ b/config/configgrpc/README.md @@ -27,6 +27,7 @@ README](../configtls/README.md). - [`read_buffer_size`](https://godoc.org/google.golang.org/grpc#ReadBufferSize) - [`write_buffer_size`](https://godoc.org/google.golang.org/grpc#WriteBufferSize) - [`auth`](../configauth/README.md) +- [`middlewares`](../configmiddleware/README.md) Please note that [`per_rpc_auth`](https://pkg.go.dev/google.golang.org/grpc#PerRPCCredentials) which allows the credentials to send for every RPC is now moved to become an [extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/extension/bearertokenauthextension). Note that this feature isn't about sending the headers only during the initial connection as an `authorization` header under the `headers` would do: this is sent for every RPC performed during an established connection. @@ -111,3 +112,4 @@ see [confignet README](../confignet/README.md). - [`tls`](../configtls/README.md) - [`write_buffer_size`](https://godoc.org/google.golang.org/grpc#WriteBufferSize) - [`auth`](../configauth/README.md) +- [`middlewares`](../configmiddleware/README.md) diff --git a/config/configgrpc/client_middleware_test.go b/config/configgrpc/client_middleware_test.go new file mode 100644 index 000000000000..8e4970d2881a --- /dev/null +++ b/config/configgrpc/client_middleware_test.go @@ -0,0 +1,137 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package configgrpc + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configmiddleware" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/extension/extensionmiddleware" +) + +// TestClientMiddlewareOrdering verifies that client middleware +// interceptors are called in the right order. +func TestClientMiddlewareOrdering(t *testing.T) { + // Create a middleware tracking header that will be modified by our middleware interceptors + const middlewareTrackingHeader = "middleware-sequence" + + // Create middleware extensions that will modify the metadata to track their execution order + mockMiddleware1 := &mockClientMiddleware{id: "middleware-1"} + mockMiddleware2 := &mockClientMiddleware{id: "middleware-2"} + + mockExt := map[component.ID]component.Component{ + component.MustNewID("middleware1"): mockMiddleware1, + component.MustNewID("middleware2"): mockMiddleware2, + } + + // Start a gRPC server that will record the incoming metadata + server := &grpcTraceServer{} + srv, addr := server.startTestServer(t, ServerConfig{ + NetAddr: confignet.AddrConfig{ + Endpoint: "localhost:0", + Transport: confignet.TransportTypeTCP, + }, + }) + defer srv.Stop() + + // Create client config with middleware extensions + clientConfig := ClientConfig{ + Endpoint: addr, + TLSSetting: configtls.ClientConfig{ + Insecure: true, + }, + Middlewares: []configmiddleware.Middleware{ + { + MiddlewareID: component.MustNewID("middleware1"), + }, + { + MiddlewareID: component.MustNewID("middleware2"), + }, + }, + } + + // Create a test host with our mock extensions + host := &mockHost{ext: mockExt} + + // Send a request using the client with middleware + resp, err := sendTestRequestWithHost(t, clientConfig, host) + require.NoError(t, err) + assert.NotNil(t, resp) + + // Verify that the middleware order was respected as recorded in the metadata + ictx, ok := metadata.FromIncomingContext(server.recordedContext) + require.True(t, ok, "middleware tracking header not found in metadata") + md := ictx[middlewareTrackingHeader] + require.Len(t, md, 1, "expected exactly one middleware tracking header value") + + // The sequence should be "middleware-1,middleware-2" as that's the order they were registered + expectedSequence := "middleware-1,middleware-2" + assert.Equal(t, expectedSequence, md[0]) +} + +// mockClientMiddleware is a mock implementation of a middleware extension +type mockClientMiddleware struct { + id string +} + +var ( + _ component.Component = &mockClientMiddleware{} + _ extensionmiddleware.GRPCClient = &mockClientMiddleware{} +) + +// Start implements component.Component +func (m *mockClientMiddleware) Start(context.Context, component.Host) error { + return nil +} + +// Shutdown implements component.Component +func (m *mockClientMiddleware) Shutdown(context.Context) error { + return nil +} + +// UnaryClientInterceptor intercepts unary calls and adds middleware ID to the tracking header +func (m *mockClientMiddleware) GetGRPCClientOptions() ([]grpc.DialOption, error) { + return []grpc.DialOption{grpc.WithChainUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + // Get existing metadata or create new metadata + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + md = metadata.New(nil) + } else { + // Clone the metadata to avoid modifying the real metadata map + md = md.Copy() + } + + // Check if there's already a middleware sequence header + sequence := "" + if values := md.Get("middleware-sequence"); len(values) > 0 { + sequence = values[0] + } + + // Append this middleware's ID to the sequence + if sequence == "" { + sequence = m.id + } else { + sequence = fmt.Sprintf("%s,%s", sequence, m.id) + } + + // Set the updated sequence + md.Set("middleware-sequence", sequence) + + // Create a new context with the updated metadata + newCtx := metadata.NewOutgoingContext(ctx, md) + + // Continue the call with our updated context + return invoker(newCtx, method, req, reply, cc, opts...) + })}, nil +} diff --git a/config/configgrpc/configgrpc.go b/config/configgrpc/configgrpc.go index 27714a06b97b..77ee82094972 100644 --- a/config/configgrpc/configgrpc.go +++ b/config/configgrpc/configgrpc.go @@ -31,6 +31,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configauth" "go.opentelemetry.io/collector/config/configcompression" + "go.opentelemetry.io/collector/config/configmiddleware" "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" @@ -103,6 +104,9 @@ type ClientConfig struct { // Auth configuration for outgoing RPCs. Auth *configauth.Authentication `mapstructure:"auth,omitempty"` + + // Middlewares for the gRPC client. + Middlewares []configmiddleware.Middleware `mapstructure:"middlewares,omitempty"` } // NewDefaultClientConfig returns a new instance of ClientConfig with default values. @@ -189,6 +193,9 @@ type ServerConfig struct { // Include propagates the incoming connection's metadata to downstream consumers. IncludeMetadata bool `mapstructure:"include_metadata,omitempty"` + + // Middlewares for the gRPC server. + Middlewares []configmiddleware.Middleware `mapstructure:"middlewares,omitempty"` } // NewDefaultServerConfig returns a new instance of ServerConfig with default values. @@ -362,6 +369,15 @@ func (gcs *ClientConfig) getGrpcDialOptions( ) } + // Apply middleware options. Note: OpenTelemetry could be registered as an extension. + for _, middleware := range gcs.Middlewares { + middlewareOptions, err := middleware.GetGRPCClientOptions(ctx, host.GetExtensions()) + if err != nil { + return nil, fmt.Errorf("failed to get gRPC client options from middleware: %w", err) + } + opts = append(opts, middlewareOptions...) + } + for _, opt := range extraOpts { if wrapper, ok := opt.(grpcDialOptionWrapper); ok { opts = append(opts, wrapper.opt) @@ -404,12 +420,12 @@ func (grpcServerOptionWrapper) isToServerOption() {} // ToServer returns a [grpc.Server] for the configuration. func (gss *ServerConfig) ToServer( - _ context.Context, + ctx context.Context, host component.Host, settings component.TelemetrySettings, extraOpts ...ToServerOption, ) (*grpc.Server, error) { - grpcOpts, err := gss.getGrpcServerOptions(host, settings, extraOpts) + grpcOpts, err := gss.getGrpcServerOptions(ctx, host, settings, extraOpts) if err != nil { return nil, err } @@ -417,6 +433,7 @@ func (gss *ServerConfig) ToServer( } func (gss *ServerConfig) getGrpcServerOptions( + ctx context.Context, host component.Host, settings component.TelemetrySettings, extraOpts []ToServerOption, @@ -505,6 +522,15 @@ func (gss *ServerConfig) getGrpcServerOptions( opts = append(opts, grpc.StatsHandler(otelgrpc.NewServerHandler(otelOpts...)), grpc.ChainUnaryInterceptor(uInterceptors...), grpc.ChainStreamInterceptor(sInterceptors...)) + // Apply middleware options. Note: OpenTelemetry could be registered as an extension. + for _, middleware := range gss.Middlewares { + middlewareOptions, err := middleware.GetGRPCServerOptions(ctx, host.GetExtensions()) + if err != nil { + return nil, fmt.Errorf("failed to get gRPC server options from middleware: %w", err) + } + opts = append(opts, middlewareOptions...) + } + for _, opt := range extraOpts { if wrapper, ok := opt.(grpcServerOptionWrapper); ok { opts = append(opts, wrapper.opt) diff --git a/config/configgrpc/configgrpc_test.go b/config/configgrpc/configgrpc_test.go index 3c46d88f96ff..89a37aafa265 100644 --- a/config/configgrpc/configgrpc_test.go +++ b/config/configgrpc/configgrpc_test.go @@ -299,7 +299,7 @@ func TestDefaultGrpcServerSettings(t *testing.T) { Endpoint: "0.0.0.0:1234", }, } - opts, err := gss.getGrpcServerOptions(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) + opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) require.NoError(t, err) assert.Len(t, opts, 3) } @@ -312,6 +312,7 @@ func TestGrpcServerExtraOption(t *testing.T) { } extraOpt := grpc.ConnectionTimeout(1_000_000_000) opts, err := gss.getGrpcServerOptions( + context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{WithGrpcServerOption(extraOpt)}, @@ -401,7 +402,7 @@ func TestAllGrpcServerSettingsExceptAuth(t *testing.T) { }, }, } - opts, err := gss.getGrpcServerOptions(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) + opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) require.NoError(t, err) assert.Len(t, opts, 10) } @@ -1144,9 +1145,13 @@ func (gts *grpcTraceServer) Export(ctx context.Context, _ ptraceotlp.ExportReque } func (gts *grpcTraceServer) startTestServer(t *testing.T, gss ServerConfig) (*grpc.Server, string) { + return gts.startTestServerWithHost(t, gss, componenttest.NewNopHost()) +} + +func (gts *grpcTraceServer) startTestServerWithHost(t *testing.T, gss ServerConfig, host component.Host, opts ...ToServerOption) (*grpc.Server, string) { listener, err := gss.NetAddr.Listen(context.Background()) require.NoError(t, err) - server, err := gss.ToServer(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + server, err := gss.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), opts...) require.NoError(t, err) ptraceotlp.RegisterGRPCServer(server, gts) go func() { @@ -1155,8 +1160,14 @@ func (gts *grpcTraceServer) startTestServer(t *testing.T, gss ServerConfig) (*gr return server, listener.Addr().String() } +// sendTestRequest issues a ptraceotlp export request and captures metadata. func sendTestRequest(t *testing.T, gcs ClientConfig) (ptraceotlp.ExportResponse, error) { - grpcClientConn, errClient := gcs.ToClientConn(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + return sendTestRequestWithHost(t, gcs, componenttest.NewNopHost()) +} + +// sendTestRequestWithHost is similar to sendTestRequest but allows specifying the host +func sendTestRequestWithHost(t *testing.T, gcs ClientConfig, host component.Host) (ptraceotlp.ExportResponse, error) { + grpcClientConn, errClient := gcs.ToClientConn(context.Background(), host, componenttest.NewNopTelemetrySettings()) require.NoError(t, errClient) defer func() { assert.NoError(t, grpcClientConn.Close()) }() c := ptraceotlp.NewGRPCClient(grpcClientConn) diff --git a/config/configgrpc/go.mod b/config/configgrpc/go.mod index c999077da2da..6cb4f20b8ba4 100644 --- a/config/configgrpc/go.mod +++ b/config/configgrpc/go.mod @@ -10,12 +10,14 @@ require ( go.opentelemetry.io/collector/component/componenttest v0.124.0 go.opentelemetry.io/collector/config/configauth v0.124.0 go.opentelemetry.io/collector/config/configcompression v1.30.0 + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 go.opentelemetry.io/collector/config/confignet v1.30.0 go.opentelemetry.io/collector/config/configopaque v1.30.0 go.opentelemetry.io/collector/config/configtls v1.30.0 go.opentelemetry.io/collector/extension v1.30.0 go.opentelemetry.io/collector/extension/extensionauth v1.30.0 go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest v0.124.0 + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 go.opentelemetry.io/collector/pdata v1.30.0 go.opentelemetry.io/collector/pdata/testdata v0.124.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 @@ -74,6 +76,10 @@ replace go.opentelemetry.io/collector/extension => ../../extension replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth +replace go.opentelemetry.io/collector/config/configmiddleware => ../configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + replace go.opentelemetry.io/collector/pdata => ../../pdata replace go.opentelemetry.io/collector/pdata/testdata => ../../pdata/testdata diff --git a/config/configgrpc/server_middleware_test.go b/config/configgrpc/server_middleware_test.go new file mode 100644 index 000000000000..f8fefffb2aa1 --- /dev/null +++ b/config/configgrpc/server_middleware_test.go @@ -0,0 +1,110 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package configgrpc + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configmiddleware" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/config/configtls" +) + +// contextKey is a private type for keys defined in this test. +type contextKey int + +// Key for the slice of middleware names in the context. +const middlewareCallsKey contextKey = 0 + +// getMiddlewareCalls retrieves the middleware calls from context or returns an empty slice. +func getMiddlewareCalls(ctx context.Context) []string { + calls, ok := ctx.Value(middlewareCallsKey).([]string) + if !ok { + return []string{} + } + return calls +} + +// testServerMiddleware is a test implementation of configmiddleware.Middleware +type testServerMiddleware struct { + name string +} + +func (*testServerMiddleware) Start(_ context.Context, _ component.Host) error { + return nil +} + +func (*testServerMiddleware) Shutdown(_ context.Context) error { + return nil +} + +func (tm *testServerMiddleware) call(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + ctx = context.WithValue(ctx, middlewareCallsKey, append(getMiddlewareCalls(ctx), tm.name)) + return handler(ctx, req) +} + +func (tm *testServerMiddleware) GetGRPCServerOptions() ([]grpc.ServerOption, error) { + return []grpc.ServerOption{grpc.ChainUnaryInterceptor(tm.call)}, nil +} + +func newTestServerMiddleware(name string) component.Component { + return &testServerMiddleware{ + name: name, + } +} + +func newTestServerConfig(name string) configmiddleware.Middleware { + return configmiddleware.Middleware{ + MiddlewareID: component.MustNewID(name), + } +} + +func TestGrpcServerUnaryInterceptor(t *testing.T) { + // Register two test extensions + host := &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("test1"): newTestServerMiddleware("test1"), + component.MustNewID("test2"): newTestServerMiddleware("test2"), + }, + } + + // Setup the server with both middleware options + server := &grpcTraceServer{} + var addr string + + // Create the server with middleware interceptors + { + var srv *grpc.Server + srv, addr = server.startTestServerWithHost(t, ServerConfig{ + NetAddr: confignet.AddrConfig{ + Endpoint: "localhost:0", + Transport: confignet.TransportTypeTCP, + }, + Middlewares: []configmiddleware.Middleware{ + newTestServerConfig("test1"), + newTestServerConfig("test2"), + }, + }, host) + defer srv.Stop() + } + + // Send a request to trigger the interceptors + resp, errResp := sendTestRequest(t, ClientConfig{ + Endpoint: addr, + TLSSetting: configtls.ClientConfig{ + Insecure: true, + }, + }) + require.NoError(t, errResp) + require.NotNil(t, resp) + + // Verify interceptors were called in the correct order + assert.Equal(t, []string{"test1", "test2"}, getMiddlewareCalls(server.recordedContext)) +} From 00ce5c55c52641152c92a99a25f945efc0dfddd1 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:48:22 -0700 Subject: [PATCH 5/5] middleware: all remaining diffs --- .chloggen/middleware-all.yaml | 25 +++++++++++++++++++++++++ cmd/otelcorecol/builder-config.yaml | 2 ++ cmd/otelcorecol/go.mod | 8 +++++++- exporter/otlpexporter/go.mod | 6 ++++++ exporter/otlphttpexporter/go.mod | 6 ++++++ extension/zpagesextension/go.mod | 6 ++++++ internal/e2e/go.mod | 6 ++++++ otelcol/go.mod | 4 ++++ otelcol/otelcoltest/go.mod | 4 ++++ receiver/otlpreceiver/go.mod | 6 ++++++ service/go.mod | 6 ++++++ service/hostcapabilities/go.mod | 4 ++++ versions.yaml | 2 ++ 13 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 .chloggen/middleware-all.yaml diff --git a/.chloggen/middleware-all.yaml b/.chloggen/middleware-all.yaml new file mode 100644 index 000000000000..d22d29c75e5c --- /dev/null +++ b/.chloggen/middleware-all.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: configmiddleware + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add configmiddleware, extensionmiddleware, and add support in gRPC and HTTP. + +# One or more tracking issues or pull requests related to the change +issues: [12603, 9591] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index 47585926e726..27a4f5910ca5 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -46,6 +46,7 @@ replaces: - go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression - go.opentelemetry.io/collector/config/configgrpc => ../../config/configgrpc - go.opentelemetry.io/collector/config/confighttp => ../../config/confighttp + - go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware - go.opentelemetry.io/collector/config/confignet => ../../config/confignet - go.opentelemetry.io/collector/config/configopaque => ../../config/configopaque - go.opentelemetry.io/collector/config/configretry => ../../config/configretry @@ -78,6 +79,7 @@ replaces: - go.opentelemetry.io/collector/extension => ../../extension - go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth - go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest + - go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware - go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities - go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest - go.opentelemetry.io/collector/extension/memorylimiterextension => ../../extension/memorylimiterextension diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 5a683ce5fcca..7ad593ca7ca1 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -4,7 +4,7 @@ module go.opentelemetry.io/collector/cmd/otelcorecol go 1.23.0 -toolchain go1.23.8 +toolchain go1.24.1 require ( go.opentelemetry.io/collector/component v1.30.0 @@ -89,6 +89,7 @@ require ( go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect go.opentelemetry.io/collector/config/configgrpc v0.124.0 // indirect go.opentelemetry.io/collector/config/confighttp v0.124.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/config/confignet v1.30.0 // indirect go.opentelemetry.io/collector/config/configopaque v1.30.0 // indirect go.opentelemetry.io/collector/config/configretry v1.30.0 // indirect @@ -107,6 +108,7 @@ require ( go.opentelemetry.io/collector/exporter/xexporter v0.124.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensioncapabilities v0.124.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/extension/extensiontest v0.124.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.124.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect @@ -185,6 +187,8 @@ replace go.opentelemetry.io/collector/config/configgrpc => ../../config/configgr replace go.opentelemetry.io/collector/config/confighttp => ../../config/confighttp +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + replace go.opentelemetry.io/collector/config/confignet => ../../config/confignet replace go.opentelemetry.io/collector/config/configopaque => ../../config/configopaque @@ -249,6 +253,8 @@ replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + replace go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest diff --git a/exporter/otlpexporter/go.mod b/exporter/otlpexporter/go.mod index e1844099e63c..8bbf287a56be 100644 --- a/exporter/otlpexporter/go.mod +++ b/exporter/otlpexporter/go.mod @@ -55,12 +55,14 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/config/confignet v1.30.0 // indirect go.opentelemetry.io/collector/consumer/consumererror/xconsumererror v0.124.0 // indirect go.opentelemetry.io/collector/consumer/consumertest v0.124.0 // indirect go.opentelemetry.io/collector/consumer/xconsumer v0.124.0 // indirect go.opentelemetry.io/collector/extension v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/extension/xextension v0.124.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect @@ -162,3 +164,7 @@ replace go.opentelemetry.io/collector/extension/xextension => ../../extension/xe replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware diff --git a/exporter/otlphttpexporter/go.mod b/exporter/otlphttpexporter/go.mod index ad1f4038f152..bc7fda02a106 100644 --- a/exporter/otlphttpexporter/go.mod +++ b/exporter/otlphttpexporter/go.mod @@ -56,11 +56,13 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.30.0 // indirect go.opentelemetry.io/collector/config/configauth v0.124.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/consumer/consumererror/xconsumererror v0.124.0 // indirect go.opentelemetry.io/collector/consumer/consumertest v0.124.0 // indirect go.opentelemetry.io/collector/consumer/xconsumer v0.124.0 // indirect go.opentelemetry.io/collector/extension v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/extension/xextension v0.124.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect @@ -160,3 +162,7 @@ replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/extension/xextension => ../../extension/xextension replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware diff --git a/extension/zpagesextension/go.mod b/extension/zpagesextension/go.mod index f46acacdb4f8..732df443c74a 100644 --- a/extension/zpagesextension/go.mod +++ b/extension/zpagesextension/go.mod @@ -43,9 +43,11 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.30.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/config/configopaque v1.30.0 // indirect go.opentelemetry.io/collector/config/configtls v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect go.opentelemetry.io/collector/pdata v1.30.0 // indirect @@ -110,3 +112,7 @@ replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../extensionmiddleware diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index f0dc40361242..8bf27c2686eb 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -83,6 +83,7 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.30.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/connector/xconnector v0.124.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.124.0 // indirect go.opentelemetry.io/collector/consumer/consumererror/xconsumererror v0.124.0 // indirect @@ -91,6 +92,7 @@ require ( go.opentelemetry.io/collector/exporter/xexporter v0.124.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect go.opentelemetry.io/collector/extension/extensioncapabilities v0.124.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/extension/extensiontest v0.124.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.124.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect @@ -260,3 +262,7 @@ replace go.opentelemetry.io/collector/confmap/provider/yamlprovider => ../../con replace go.opentelemetry.io/collector/confmap/provider/fileprovider => ../../confmap/provider/fileprovider replace go.opentelemetry.io/collector/service/hostcapabilities => ../../service/hostcapabilities + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware diff --git a/otelcol/go.mod b/otelcol/go.mod index 16c7add2d858..df45fa674e66 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -219,3 +219,7 @@ replace go.opentelemetry.io/collector/confmap/provider/fileprovider => ../confma replace go.opentelemetry.io/collector/confmap/provider/yamlprovider => ../confmap/provider/yamlprovider replace go.opentelemetry.io/collector/internal/telemetry => ../internal/telemetry + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../config/configmiddleware diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index c05652012261..6e6ce2edfc3d 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -228,3 +228,7 @@ replace go.opentelemetry.io/collector/processor/processortest => ../../processor replace go.opentelemetry.io/collector/pdata/testdata => ../../pdata/testdata replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware diff --git a/receiver/otlpreceiver/go.mod b/receiver/otlpreceiver/go.mod index 8eecdb4de48b..2d45966334b7 100644 --- a/receiver/otlpreceiver/go.mod +++ b/receiver/otlpreceiver/go.mod @@ -66,7 +66,9 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.30.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/pipeline v0.124.0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect @@ -150,3 +152,7 @@ retract ( replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware diff --git a/service/go.mod b/service/go.mod index 884754105954..c97ce6be90cb 100644 --- a/service/go.mod +++ b/service/go.mod @@ -104,10 +104,12 @@ require ( go.opentelemetry.io/collector/client v1.30.0 // indirect go.opentelemetry.io/collector/config/configauth v0.124.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.30.0 // indirect + go.opentelemetry.io/collector/config/configmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/collector/config/configopaque v1.30.0 // indirect go.opentelemetry.io/collector/config/configtls v1.30.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.124.0 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect + go.opentelemetry.io/collector/extension/extensionmiddleware v0.0.0-00010101000000-000000000000 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/contrib/zpages v0.60.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 // indirect @@ -233,3 +235,7 @@ replace go.opentelemetry.io/collector/otelcol => ../otelcol replace go.opentelemetry.io/collector/confmap/provider/yamlprovider => ../confmap/provider/yamlprovider replace go.opentelemetry.io/collector/confmap/provider/fileprovider => ../confmap/provider/fileprovider + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../config/configmiddleware diff --git a/service/hostcapabilities/go.mod b/service/hostcapabilities/go.mod index 02833db0656b..5bcf1a541002 100644 --- a/service/hostcapabilities/go.mod +++ b/service/hostcapabilities/go.mod @@ -89,3 +89,7 @@ replace go.opentelemetry.io/collector/otelcol => ../../otelcol replace go.opentelemetry.io/collector/confmap/provider/fileprovider => ../../confmap/provider/fileprovider replace go.opentelemetry.io/collector/confmap/provider/yamlprovider => ../../confmap/provider/yamlprovider + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware diff --git a/versions.yaml b/versions.yaml index 44705dc3ec0b..8a09c94f3c87 100644 --- a/versions.yaml +++ b/versions.yaml @@ -42,6 +42,7 @@ module-sets: - go.opentelemetry.io/collector/config/configgrpc - go.opentelemetry.io/collector/config/confighttp - go.opentelemetry.io/collector/config/confighttp/xconfighttp + - go.opentelemetry.io/collector/config/configmiddleware - go.opentelemetry.io/collector/config/configtelemetry - go.opentelemetry.io/collector/connector - go.opentelemetry.io/collector/connector/connectortest @@ -61,6 +62,7 @@ module-sets: - go.opentelemetry.io/collector/exporter/xexporter - go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest - go.opentelemetry.io/collector/extension/extensioncapabilities + - go.opentelemetry.io/collector/extension/extensionmiddleware - go.opentelemetry.io/collector/extension/extensiontest - go.opentelemetry.io/collector/extension/zpagesextension - go.opentelemetry.io/collector/extension/memorylimiterextension