From 30f4bc11379fad311537db216f7b7171a89e00fe Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 17:59:36 -0700 Subject: [PATCH 01/13] Middleware: HTTP support (part 3/4) --- .chloggen/middleware-http.yaml | 25 +++ 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 + 7 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 .chloggen/middleware-http.yaml create mode 100644 config/confighttp/client_middleware_test.go create mode 100644 config/confighttp/server_middleware_test.go diff --git a/.chloggen/middleware-http.yaml b/.chloggen/middleware-http.yaml new file mode 100644 index 00000000000..a16d7b41323 --- /dev/null +++ b/.chloggen/middleware-http.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 HTTP middleware support. + +# 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/config/confighttp/README.md b/config/confighttp/README.md index 1f1cb1bba27..1ee56939814 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 00000000000..a5424897d7e --- /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 43193c957ee..1d8f8b68c48 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 2b596b18edf..c9f28edf027 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 00000000000..bc9c2ebec7a --- /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 53545c9d538..12da6a07518 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 fd6c3735f95ca6c5b7bb15941eafa09370698f4d Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Mon, 14 Apr 2025 18:03:12 -0700 Subject: [PATCH 02/13] typo --- .chloggen/middleware-http.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/middleware-http.yaml b/.chloggen/middleware-http.yaml index a16d7b41323..1ba58fa04a0 100644 --- a/.chloggen/middleware-http.yaml +++ b/.chloggen/middleware-http.yaml @@ -4,7 +4,7 @@ change_type: enhancement # The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) -component: configmiddleware +component: confighttp # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). note: Add HTTP middleware support. From d3a42810fd02757716cb45ca7b06d061f4f1c5e7 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 09:33:22 -0700 Subject: [PATCH 03/13] tidy up the tests --- cmd/otelcorecol/go.mod | 8 ++ config/confighttp/client_middleware_test.go | 92 ++++++++----------- config/confighttp/confighttp.go | 4 +- config/confighttp/go.mod | 5 +- config/confighttp/server_middleware_test.go | 55 +++++------ config/confighttp/xconfighttp/go.mod | 4 +- exporter/otlphttpexporter/go.mod | 8 ++ extension/extensionmiddleware/client_test.go | 23 ----- .../extensionmiddlewaretest/nop.go | 9 ++ extension/zpagesextension/go.mod | 8 ++ internal/e2e/go.mod | 8 ++ otelcol/go.mod | 7 ++ otelcol/otelcoltest/go.mod | 7 ++ receiver/otlpreceiver/go.mod | 8 ++ service/go.mod | 8 ++ service/hostcapabilities/go.mod | 6 ++ 16 files changed, 147 insertions(+), 113 deletions(-) diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 05db670b0aa..444289030e8 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -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 v1.30.0 // 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 @@ -313,3 +315,9 @@ replace go.opentelemetry.io/collector/semconv => ../../semconv replace go.opentelemetry.io/collector/service => ../../service replace go.opentelemetry.io/collector/service/hostcapabilities => ../../service/hostcapabilities + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/config/confighttp/client_middleware_test.go b/config/confighttp/client_middleware_test.go index a5424897d7e..6e4f315fc4c 100644 --- a/config/confighttp/client_middleware_test.go +++ b/config/confighttp/client_middleware_test.go @@ -17,71 +17,51 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configmiddleware" + "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/extension/extensionmiddleware" + "go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest" ) // 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 + extension.Extension + extensionmiddleware.GetHTTPRoundTripperFunc } func newTestClientMiddleware(name string) component.Component { return &testClientMiddleware{ - name: name, + Extension: extensionmiddlewaretest.NewNop(), + GetHTTPRoundTripperFunc: func(transport http.RoundTripper) (http.RoundTripper, error) { + return extensionmiddlewaretest.HTTPClientFunc( + func(req *http.Request) (*http.Response, error) { + resp, err := 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 " + name + + // Replace the response body + resp.Body = io.NopCloser(strings.NewReader(newBody)) + resp.ContentLength = int64(len(newBody)) + + return resp, nil + }), nil + }, } } -func newTestClientConfig(name string) configmiddleware.Middleware { - return configmiddleware.Middleware{ - MiddlewareID: component.MustNewID(name), +func newTestClientConfig(name string) configmiddleware.Config { + return configmiddleware.Config{ + ID: component.MustNewID(name), } } @@ -104,7 +84,7 @@ func TestClientMiddlewares(t *testing.T) { // Test with different middleware configurations testCases := []struct { name string - middlewares []configmiddleware.Middleware + middlewares []configmiddleware.Config expectedOutput string }{ { @@ -114,14 +94,14 @@ func TestClientMiddlewares(t *testing.T) { }, { name: "single_middleware", - middlewares: []configmiddleware.Middleware{ + middlewares: []configmiddleware.Config{ newTestClientConfig("test1"), }, expectedOutput: "OK\r\noutput by test1", }, { name: "multiple_middlewares", - middlewares: []configmiddleware.Middleware{ + middlewares: []configmiddleware.Config{ newTestClientConfig("test1"), newTestClientConfig("test2"), }, diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 96faedf65d2..360d4c773fc 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -116,7 +116,7 @@ type ClientConfig struct { // 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"` + Middlewares []configmiddleware.Config `mapstructure:"middleware,omitempty"` } // CookiesConfig defines the configuration of the HTTP client regarding cookies served by the server. @@ -365,7 +365,7 @@ type ServerConfig struct { // 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"` + Middlewares []configmiddleware.Config `mapstructure:"middleware,omitempty"` } // NewDefaultServerConfig returns ServerConfig type object with default values. diff --git a/config/confighttp/go.mod b/config/confighttp/go.mod index c9f28edf027..1c738c63353 100644 --- a/config/confighttp/go.mod +++ b/config/confighttp/go.mod @@ -19,7 +19,8 @@ require ( 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/extension/extensionmiddleware v1.30.0 + go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest 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 @@ -89,3 +90,5 @@ replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telem replace go.opentelemetry.io/collector/pipeline => ../../pipeline replace go.opentelemetry.io/collector/featuregate => ../../featuregate + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/config/confighttp/server_middleware_test.go b/config/confighttp/server_middleware_test.go index bc9c2ebec7a..e87506d5e55 100644 --- a/config/confighttp/server_middleware_test.go +++ b/config/confighttp/server_middleware_test.go @@ -16,43 +16,38 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configmiddleware" + "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/extension/extensionmiddleware" + "go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest" ) -// testServerMiddleware is a test implementation of configmiddleware.Middleware +// testServerMiddleware is a test implementation of configmiddleware.Config 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 + extension.Extension + extensionmiddleware.GetHTTPHandlerFunc } func newTestServerMiddleware(name string) component.Component { return &testServerMiddleware{ - name: name, + Extension: extensionmiddlewaretest.NewNop(), + GetHTTPHandlerFunc: func(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 += name + "/" + + // Call the next handler in the chain + handler.ServeHTTP(w, r) + + // Add middleware name to the response + _, _ = w.Write([]byte("\r\nserved by " + name)) + }), nil + }, } } -func newTestServerConfig(name string) configmiddleware.Middleware { - return configmiddleware.Middleware{ - MiddlewareID: component.MustNewID(name), +func newTestServerConfig(name string) configmiddleware.Config { + return configmiddleware.Config{ + ID: component.MustNewID(name), } } @@ -68,7 +63,7 @@ func TestServerMiddleware(t *testing.T) { // Test with different middleware configurations testCases := []struct { name string - middlewares []configmiddleware.Middleware + middlewares []configmiddleware.Config expectedOutput string }{ { @@ -78,14 +73,14 @@ func TestServerMiddleware(t *testing.T) { }, { name: "single_middleware", - middlewares: []configmiddleware.Middleware{ + middlewares: []configmiddleware.Config{ newTestServerConfig("test1"), }, expectedOutput: "OK{/test1/}\r\nserved by test1", }, { name: "multiple_middlewares", - middlewares: []configmiddleware.Middleware{ + middlewares: []configmiddleware.Config{ newTestServerConfig("test1"), newTestServerConfig("test2"), }, diff --git a/config/confighttp/xconfighttp/go.mod b/config/confighttp/xconfighttp/go.mod index 12da6a07518..242306f266d 100644 --- a/config/confighttp/xconfighttp/go.mod +++ b/config/confighttp/xconfighttp/go.mod @@ -34,7 +34,7 @@ require ( 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/extension/extensionmiddleware v1.30.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 @@ -89,3 +89,5 @@ replace go.opentelemetry.io/collector/featuregate => ../../../featuregate replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../../extension/extensionmiddleware replace go.opentelemetry.io/collector/config/configmiddleware => ../../configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/exporter/otlphttpexporter/go.mod b/exporter/otlphttpexporter/go.mod index 779acf340a0..8a6fff2129f 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 v1.30.0 // 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 @@ -161,3 +163,9 @@ 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/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/extension/extensionmiddleware/client_test.go b/extension/extensionmiddleware/client_test.go index c525ffa3b76..8d4eadcade2 100644 --- a/extension/extensionmiddleware/client_test.go +++ b/extension/extensionmiddleware/client_test.go @@ -32,19 +32,6 @@ func TestGetHTTPRoundTripperFunc(t *testing.T) { require.Equal(t, baseRT, rt) }) - t.Run("wrapping function", func(t *testing.T) { - // Create a custom round tripper wrapper for testing - customRT := &testRoundTripper{base: baseRT} - - wrapperFunc := GetHTTPRoundTripperFunc(func(base http.RoundTripper) (http.RoundTripper, error) { - return &testRoundTripper{base: base}, nil - }) - - rt, err := wrapperFunc.GetHTTPRoundTripper(baseRT) - require.NoError(t, err) - require.IsType(t, customRT, rt, "wrapper function should return the custom round tripper") - }) - t.Run("error function", func(t *testing.T) { expectedErr := errors.New("round tripper error") errorFunc := GetHTTPRoundTripperFunc(func(_ http.RoundTripper) (http.RoundTripper, error) { @@ -58,16 +45,6 @@ func TestGetHTTPRoundTripperFunc(t *testing.T) { }) } -// testRoundTripper is a simple round tripper implementation for testing -type testRoundTripper struct { - base http.RoundTripper -} - -func (t *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - // Just delegate to the base round tripper - return t.base.RoundTrip(req) -} - func TestGetGRPCClientOptionsFunc(t *testing.T) { t.Run("nil function", func(t *testing.T) { var nilFunc GetGRPCClientOptionsFunc diff --git a/extension/extensionmiddleware/extensionmiddlewaretest/nop.go b/extension/extensionmiddleware/extensionmiddlewaretest/nop.go index 7d8415577cf..6a928331582 100644 --- a/extension/extensionmiddleware/extensionmiddlewaretest/nop.go +++ b/extension/extensionmiddleware/extensionmiddlewaretest/nop.go @@ -4,6 +4,8 @@ package extensionmiddlewaretest // import "go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest" import ( + "net/http" + "go.opentelemetry.io/collector/extension" ) @@ -14,3 +16,10 @@ import ( func NewNop() extension.Extension { return &baseExtension{} } + +// HTTPClientFunc implements an HTTP client middleware function. +type HTTPClientFunc func(*http.Request) (*http.Response, error) + +func (f HTTPClientFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} diff --git a/extension/zpagesextension/go.mod b/extension/zpagesextension/go.mod index 9ee9b204157..8858109b5c4 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 v1.30.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 @@ -111,3 +113,9 @@ 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/extension/extensionmiddleware => ../extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../extensionmiddleware/extensionmiddlewaretest diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index a2aa3087a0d..006d2ff67b9 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 v1.30.0 // 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 @@ -261,3 +263,9 @@ 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/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware diff --git a/otelcol/go.mod b/otelcol/go.mod index 1815d666143..7aefd6ad37e 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -74,6 +74,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/component/componenttest v0.124.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 v1.30.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.124.0 // indirect @@ -220,3 +221,9 @@ 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/config/configmiddleware => ../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index f8a4d687273..b1c5bf071a9 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -74,6 +74,7 @@ require ( go.opentelemetry.io/collector/exporter/xexporter v0.124.0 // indirect go.opentelemetry.io/collector/extension 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/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/fanoutconsumer v0.124.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect @@ -229,3 +230,9 @@ 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/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware diff --git a/receiver/otlpreceiver/go.mod b/receiver/otlpreceiver/go.mod index 1cb923fe48e..f4538d79c44 100644 --- a/receiver/otlpreceiver/go.mod +++ b/receiver/otlpreceiver/go.mod @@ -65,7 +65,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 v1.30.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/pipeline v0.124.0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect @@ -151,3 +153,9 @@ 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 + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/service/go.mod b/service/go.mod index 6ab66b2ee64..65549bc6f3d 100644 --- a/service/go.mod +++ b/service/go.mod @@ -103,10 +103,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 v1.30.0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/contrib/zpages v0.60.0 // indirect @@ -234,3 +236,9 @@ 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 + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/service/hostcapabilities/go.mod b/service/hostcapabilities/go.mod index 02833db0656..9e33baab67e 100644 --- a/service/hostcapabilities/go.mod +++ b/service/hostcapabilities/go.mod @@ -89,3 +89,9 @@ 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/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest + +replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware From 5fad04b7db6548694c124108570805be2deffb36 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 09:38:34 -0700 Subject: [PATCH 04/13] small test --- .../extensionmiddlewaretest/nop_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go index 4b136369f75..bebd0355a9a 100644 --- a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go +++ b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go @@ -4,6 +4,7 @@ package extensionmiddlewaretest import ( + "net/http" "testing" "github.com/stretchr/testify/require" @@ -42,3 +43,18 @@ func TestNopServer(t *testing.T) { require.NoError(t, err) require.Nil(t, grpcOpts) } + +func TestHTTPClientFunc(t *testing.T) { + called := false + req := &http.Request{} + resp := &http.Response{} + + f := HTTPClientFunc(func(r *http.Request) (*http.Response, error) { + called = true + return resp, nil + }) + + result, _ := f.RoundTrip(req) + require.True(t, called) + require.Equal(t, resp, result) +} From 16a4d92afa719a859763ffda29977028e6077404 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:08:56 -0700 Subject: [PATCH 05/13] update builder-config.yaml --- cmd/otelcorecol/builder-config.yaml | 3 +++ cmd/otelcorecol/go.mod | 14 +++++++------- .../extensionmiddlewaretest/nop_test.go | 1 + otelcol/go.mod | 1 - otelcol/otelcoltest/go.mod | 1 - 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index 47585926e72..a0dd3029a1f 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 @@ -79,6 +80,8 @@ replaces: - go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth - go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest - go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities + - go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + - go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest - go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest - go.opentelemetry.io/collector/extension/memorylimiterextension => ../../extension/memorylimiterextension - go.opentelemetry.io/collector/extension/xextension => ../../extension/xextension diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 444289030e8..bd98ec7fea2 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 @@ -188,6 +188,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 @@ -254,6 +256,10 @@ replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities +replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware + +replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest + replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest replace go.opentelemetry.io/collector/extension/memorylimiterextension => ../../extension/memorylimiterextension @@ -315,9 +321,3 @@ replace go.opentelemetry.io/collector/semconv => ../../semconv replace go.opentelemetry.io/collector/service => ../../service replace go.opentelemetry.io/collector/service/hostcapabilities => ../../service/hostcapabilities - -replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../extension/extensionmiddleware - -replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware - -replace go.opentelemetry.io/collector/extension/extensionmiddleware/extensionmiddlewaretest => ../../extension/extensionmiddleware/extensionmiddlewaretest diff --git a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go index bebd0355a9a..fe5d12408d8 100644 --- a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go +++ b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go @@ -50,6 +50,7 @@ func TestHTTPClientFunc(t *testing.T) { resp := &http.Response{} f := HTTPClientFunc(func(r *http.Request) (*http.Response, error) { + require.Equal(t, r, resp) called = true return resp, nil }) diff --git a/otelcol/go.mod b/otelcol/go.mod index 7aefd6ad37e..d3aafc87a7d 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -74,7 +74,6 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/component/componenttest v0.124.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 v1.30.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.124.0 // indirect diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index b1c5bf071a9..24c4b1ab08f 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -74,7 +74,6 @@ require ( go.opentelemetry.io/collector/exporter/xexporter v0.124.0 // indirect go.opentelemetry.io/collector/extension 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/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/fanoutconsumer v0.124.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.124.0 // indirect From a8141565154312e54b48262fddadc6d7cf6d6124 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:11:11 -0700 Subject: [PATCH 06/13] more comment nop --- extension/extensionmiddleware/extensionmiddlewaretest/nop.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extension/extensionmiddleware/extensionmiddlewaretest/nop.go b/extension/extensionmiddleware/extensionmiddlewaretest/nop.go index 6a928331582..b15addccf2d 100644 --- a/extension/extensionmiddleware/extensionmiddlewaretest/nop.go +++ b/extension/extensionmiddleware/extensionmiddlewaretest/nop.go @@ -17,7 +17,9 @@ func NewNop() extension.Extension { return &baseExtension{} } -// HTTPClientFunc implements an HTTP client middleware function. +// HTTPClientFunc implements an HTTP client middleware function. This +// is the equivalent of net/http.HandlerFunc for creating a +// net/http.RoundTripper from a function. type HTTPClientFunc func(*http.Request) (*http.Response, error) func (f HTTPClientFunc) RoundTrip(req *http.Request) (*http.Response, error) { From bf02835f5cc9c17902e805de11d756bb8ea8d24c Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:16:42 -0700 Subject: [PATCH 07/13] lint --- .chloggen/middleware-http.yaml | 2 +- CHANGELOG.md | 2 +- docs/rfcs/env-vars.md | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.chloggen/middleware-http.yaml b/.chloggen/middleware-http.yaml index 1ba58fa04a0..2b916615cd8 100644 --- a/.chloggen/middleware-http.yaml +++ b/.chloggen/middleware-http.yaml @@ -10,7 +10,7 @@ component: confighttp note: Add HTTP middleware support. # One or more tracking issues or pull requests related to the change -issues: [12603, 9591] +issues: [12603, 9591, 7441] # (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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bbc41e44ba..adfb77288b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3464,7 +3464,7 @@ This release is marked as "bad" since the metrics pipelines will produce bad dat - Rename ForEach (in pdata) with Range to be consistent with sync.Map (#2931) - Rename `componenthelper.Start` to `componenthelper.StartFunc` (#2880) - Rename `componenthelper.Stop` to `componenthelper.StopFunc` (#2880) -- Remove `exporterheleper.WithCustomUnmarshaler`, `processorhelper.WithCustomUnmarshaler`, `receiverhelper.WithCustomUnmarshaler`, `extensionheleper.WithCustomUnmarshaler`, implement `config.CustomUnmarshaler` interface instead (#2867) +- Remove `exporterhelper.WithCustomUnmarshaler`, `processorhelper.WithCustomUnmarshaler`, `receiverhelper.WithCustomUnmarshaler`, `extensionhelper.WithCustomUnmarshaler`, implement `config.CustomUnmarshaler` interface instead (#2867) - Remove `component.CustomUnmarshaler` implement `config.CustomUnmarshaler` interface instead (#2867) - Remove `testutil.HostPortFromAddr`, users can write their own parsing helper (#2919) - Remove `configparser.DecodeTypeAndName`, use `config.IDFromString` (#2869) diff --git a/docs/rfcs/env-vars.md b/docs/rfcs/env-vars.md index afff3dd2c00..9c6b6e9224f 100644 --- a/docs/rfcs/env-vars.md +++ b/docs/rfcs/env-vars.md @@ -104,9 +104,8 @@ environment variable whose name is stored in the URL The environment variable value is parsed by the yaml.v3 parser to an any-typed variable. The yaml.v3 parser mostly follows the YAML v1.2 specification with [*some exceptions*](https://github.com/go-yaml/yaml#compatibility). -You can see -how it works for some edge cases in this example: -[`go.dev/play/p/3vNLznwSZQe`](https://go.dev/play/p/3vNLznwSZQe). +You can see how it works for some edge cases in +[this go.dev/play example](https://go.dev/play/p/3vNLznwSZQe). ### Issues of current behavior From 981de8b9d2163db91c50677d4699a62d5317659c Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:26:35 -0700 Subject: [PATCH 08/13] revert toolchain --- cmd/otelcorecol/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index bd98ec7fea2..75241b1b7e1 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.24.1 +toolchain go1.23.8 require ( go.opentelemetry.io/collector/component v1.30.0 From 17573e2cadbb539c87de0134a5f611ea450e911d Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:32:13 -0700 Subject: [PATCH 09/13] replaces for builder command --- cmd/builder/internal/builder/main_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/builder/internal/builder/main_test.go b/cmd/builder/internal/builder/main_test.go index 0d49d538b6f..0d6cf0d528e 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -47,6 +47,7 @@ var replaceModules = []string{ "/config/configcompression", "/config/configgrpc", "/config/confighttp", + "/config/configmiddleware", "/config/confignet", "/config/configopaque", "/config/configretry", @@ -79,6 +80,8 @@ var replaceModules = []string{ "/extension/extensionauth", "/extension/extensionauth/extensionauthtest", "/extension/extensioncapabilities", + "/extension/extensionmiddleware", + "/extension/extensionmiddleware/extensionmiddlewaretest", "/extension/extensiontest", "/extension/zpagesextension", "/extension/xextension", From ece76951a7e5ff5abda7fcb986874a36f1a116d1 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:40:01 -0700 Subject: [PATCH 10/13] test fix --- .../extensionmiddleware/extensionmiddlewaretest/nop_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go index fe5d12408d8..8c92eca20fe 100644 --- a/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go +++ b/extension/extensionmiddleware/extensionmiddlewaretest/nop_test.go @@ -50,7 +50,7 @@ func TestHTTPClientFunc(t *testing.T) { resp := &http.Response{} f := HTTPClientFunc(func(r *http.Request) (*http.Response, error) { - require.Equal(t, r, resp) + require.Equal(t, r, req) called = true return resp, nil }) From e88240187ec66ce078841ddd3fac3118765f89af Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 10:45:32 -0700 Subject: [PATCH 11/13] comments --- config/confighttp/confighttp.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 360d4c773fc..a7c069048d6 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -114,7 +114,7 @@ type ClientConfig struct { 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, + // Middleware handlers are called in the order they appear in this list, // with the first middleware becoming the outermost handler. Middlewares []configmiddleware.Config `mapstructure:"middleware,omitempty"` } @@ -201,8 +201,8 @@ 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. + // Apply middlewares in reverse order so they execute in + // forward 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()) @@ -363,7 +363,7 @@ type ServerConfig struct { 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, + // Middleware handlers are called in the order they appear in this list, // with the first middleware becoming the outermost handler. Middlewares []configmiddleware.Config `mapstructure:"middleware,omitempty"` } @@ -453,9 +453,9 @@ func (hss *ServerConfig) ToServer(ctx context.Context, host component.Host, sett 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. + // Apply middlewares in reverse order so they execute in + // forward 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 From 472a2e962888765ba52f28ec7ca32fe6a08d3b1a Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 11:25:31 -0700 Subject: [PATCH 12/13] improve coverage --- config/confighttp/client_middleware_test.go | 116 +++++++++++++++++++ config/confighttp/server_middleware_test.go | 118 ++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/config/confighttp/client_middleware_test.go b/config/confighttp/client_middleware_test.go index 6e4f315fc4c..c9427d9fd02 100644 --- a/config/confighttp/client_middleware_test.go +++ b/config/confighttp/client_middleware_test.go @@ -5,6 +5,7 @@ package confighttp import ( "context" + "errors" "io" "net/http" "net/http/httptest" @@ -137,3 +138,118 @@ func TestClientMiddlewares(t *testing.T) { }) } } + +func TestClientMiddlewareErrors(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() + + // Test cases for HTTP client middleware errors + httpTests := []struct { + name string + host component.Host + config ClientConfig + errText string + }{ + { + name: "extension_not_found", + host: &mockHost{ + ext: map[component.ID]component.Component{}, + }, + config: ClientConfig{ + Endpoint: server.URL, + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("nonexistent"), + }, + }, + }, + errText: "failed to resolve middleware \"nonexistent\": middleware not found", + }, + { + name: "get_round_tripper_fails", + host: &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), + }, + }, + config: ClientConfig{ + Endpoint: server.URL, + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("errormw"), + }, + }, + }, + errText: "http middleware error", + }, + } + + for _, tc := range httpTests { + t.Run(tc.name, func(t *testing.T) { + // Trying to create the client should fail + _, err := tc.config.ToClient(context.Background(), tc.host, componenttest.NewNopTelemetrySettings()) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errText) + }) + } +} + +// Test failures for gRPC client middlewares by creating a mock implementation +// that can fail in similar ways to HTTP clients +func TestGRPCClientMiddlewareErrors(t *testing.T) { + // Test cases for gRPC client middleware errors + grpcTests := []struct { + name string + host component.Host + config ClientConfig + errText string + }{ + { + name: "grpc_extension_not_found", + host: &mockHost{ + ext: map[component.ID]component.Component{}, + }, + config: ClientConfig{ + Endpoint: "localhost:1234", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("nonexistent"), + }, + }, + }, + errText: "failed to resolve middleware \"nonexistent\": middleware not found", + }, + { + name: "grpc_get_client_options_fails", + host: &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), + }, + }, + config: ClientConfig{ + Endpoint: "localhost:1234", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("errormw"), + }, + }, + }, + errText: "grpc middleware error", + }, + } + + for _, tc := range grpcTests { + t.Run(tc.name, func(t *testing.T) { + // For gRPC, we need to use the configgrpc.ClientConfig structure + // We'll test the middleware failure path here using the HTTP client approach, + // as the middleware resolution logic is the same + _, err := tc.config.ToClient(context.Background(), tc.host, componenttest.NewNopTelemetrySettings()) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errText) + }) + } +} diff --git a/config/confighttp/server_middleware_test.go b/config/confighttp/server_middleware_test.go index e87506d5e55..ca383ccfe38 100644 --- a/config/confighttp/server_middleware_test.go +++ b/config/confighttp/server_middleware_test.go @@ -5,6 +5,7 @@ package confighttp import ( "context" + "errors" "io" "net/http" "net/http/httptest" @@ -131,3 +132,120 @@ func TestServerMiddleware(t *testing.T) { }) } } + +func TestServerMiddlewareErrors(t *testing.T) { + // Create a basic handler for testing + handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("OK")) + }) + + // Test cases for HTTP server middleware errors + httpTests := []struct { + name string + host component.Host + config ServerConfig + errText string + }{ + { + name: "extension_not_found", + host: &mockHost{ + ext: map[component.ID]component.Component{}, + }, + config: ServerConfig{ + Endpoint: "localhost:0", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("nonexistent"), + }, + }, + }, + errText: "failed to resolve middleware \"nonexistent\": middleware not found", + }, + { + name: "get_http_handler_fails", + host: &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), + }, + }, + config: ServerConfig{ + Endpoint: "localhost:0", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("errormw"), + }, + }, + }, + errText: "http middleware error", + }, + } + + for _, tc := range httpTests { + t.Run(tc.name, func(t *testing.T) { + // Trying to create the server should fail + _, err := tc.config.ToServer( + context.Background(), + tc.host, + componenttest.NewNopTelemetrySettings(), + handler, + ) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errText) + }) + } + + // Test cases for gRPC server middleware errors + grpcTests := []struct { + name string + host component.Host + config ServerConfig + errText string + }{ + { + name: "grpc_extension_not_found", + host: &mockHost{ + ext: map[component.ID]component.Component{}, + }, + config: ServerConfig{ + Endpoint: "localhost:0", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("nonexistent"), + }, + }, + }, + errText: "failed to resolve middleware \"nonexistent\": middleware not found", + }, + { + name: "get_grpc_handler_fails", + host: &mockHost{ + ext: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), + }, + }, + config: ServerConfig{ + Endpoint: "localhost:0", + Middlewares: []configmiddleware.Config{ + { + ID: component.MustNewID("errormw"), + }, + }, + }, + errText: "grpc middleware error", + }, + } + + for _, tc := range grpcTests { + t.Run(tc.name, func(t *testing.T) { + // Trying to create the server should fail + _, err := tc.config.ToServer( + context.Background(), + tc.host, + componenttest.NewNopTelemetrySettings(), + handler, + ) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errText) + }) + } +} From abc302dcd9d39259d0b102991b3644060571b22b Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Fri, 18 Apr 2025 11:33:21 -0700 Subject: [PATCH 13/13] Revert --- CHANGELOG.md | 2 +- docs/rfcs/env-vars.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adfb77288b1..6bbc41e44ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3464,7 +3464,7 @@ This release is marked as "bad" since the metrics pipelines will produce bad dat - Rename ForEach (in pdata) with Range to be consistent with sync.Map (#2931) - Rename `componenthelper.Start` to `componenthelper.StartFunc` (#2880) - Rename `componenthelper.Stop` to `componenthelper.StopFunc` (#2880) -- Remove `exporterhelper.WithCustomUnmarshaler`, `processorhelper.WithCustomUnmarshaler`, `receiverhelper.WithCustomUnmarshaler`, `extensionhelper.WithCustomUnmarshaler`, implement `config.CustomUnmarshaler` interface instead (#2867) +- Remove `exporterheleper.WithCustomUnmarshaler`, `processorhelper.WithCustomUnmarshaler`, `receiverhelper.WithCustomUnmarshaler`, `extensionheleper.WithCustomUnmarshaler`, implement `config.CustomUnmarshaler` interface instead (#2867) - Remove `component.CustomUnmarshaler` implement `config.CustomUnmarshaler` interface instead (#2867) - Remove `testutil.HostPortFromAddr`, users can write their own parsing helper (#2919) - Remove `configparser.DecodeTypeAndName`, use `config.IDFromString` (#2869) diff --git a/docs/rfcs/env-vars.md b/docs/rfcs/env-vars.md index 9c6b6e9224f..afff3dd2c00 100644 --- a/docs/rfcs/env-vars.md +++ b/docs/rfcs/env-vars.md @@ -104,8 +104,9 @@ environment variable whose name is stored in the URL The environment variable value is parsed by the yaml.v3 parser to an any-typed variable. The yaml.v3 parser mostly follows the YAML v1.2 specification with [*some exceptions*](https://github.com/go-yaml/yaml#compatibility). -You can see how it works for some edge cases in -[this go.dev/play example](https://go.dev/play/p/3vNLznwSZQe). +You can see +how it works for some edge cases in this example: +[`go.dev/play/p/3vNLznwSZQe`](https://go.dev/play/p/3vNLznwSZQe). ### Issues of current behavior