diff --git a/.chloggen/no-host-in-configgrpc.yaml b/.chloggen/no-host-in-configgrpc.yaml new file mode 100644 index 00000000000..40f7abf9210 --- /dev/null +++ b/.chloggen/no-host-in-configgrpc.yaml @@ -0,0 +1,38 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: pkg/config/configgrpc + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove `component.Host` parameter of ToServer/ToClientConn + +# One or more tracking issues or pull requests related to the change +issues: [13640] + +# (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: | + The `host` parameter was only used to find the middleware extensions specified + under the `middlewares` configuration key. + + The map of available extensions must now be passed explicitly with the + `With[Server/Client]Extensions` option. + If the option is not provided, no middlewares will be available for use. + + For typical use cases (user-controlled configuration inside a Collector component) + where a real `host` was passed, it should be replaced by + `With[Server/Client]Extensions(host.GetExtensions())`. + + For test or non-Collector use cases where a no-op host was passed, + the `host` parameter can simply be dropped. + +# 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: [api] diff --git a/.chloggen/no-host-in-confighttp.yaml b/.chloggen/no-host-in-confighttp.yaml new file mode 100644 index 00000000000..aa968c813a6 --- /dev/null +++ b/.chloggen/no-host-in-confighttp.yaml @@ -0,0 +1,38 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: pkg/config/confighttp + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove `component.Host` parameter of ToServer/ToClient + +# One or more tracking issues or pull requests related to the change +issues: [13640] + +# (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: | + The `host` parameter was only used to find the middleware extensions specified + under the `middlewares` configuration key. + + The map of available extensions must now be passed explicitly with the + `With[Server/Client]Extensions` option. + If the option is not provided, no middlewares will be available for use. + + For typical use cases (user-controlled configuration inside a Collector component) + where a real `host` was passed, it should be replaced by + `With[Server/Client]Extensions(host.GetExtensions())`. + + For test or non-Collector use cases where a no-op host was passed, + the `host` parameter can simply be dropped. + +# 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: [api] diff --git a/config/configgrpc/client_middleware_test.go b/config/configgrpc/client_middleware_test.go index 846dcc18581..dbf1c12b409 100644 --- a/config/configgrpc/client_middleware_test.go +++ b/config/configgrpc/client_middleware_test.go @@ -124,11 +124,8 @@ func TestClientMiddlewareOrdering(t *testing.T) { }, } - // 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) + resp, err := sendTestRequest(t, clientConfig, WithClientExtensions(mockExt)) require.NoError(t, err) assert.NotNil(t, resp) @@ -147,16 +144,14 @@ func TestClientMiddlewareOrdering(t *testing.T) { // specifically related to middleware resolution and API calls. func TestClientMiddlewareToClientErrors(t *testing.T) { tests := []struct { - name string - host component.Host - config ClientConfig - errText string + name string + extensions map[component.ID]component.Component + config ClientConfig + errText string }{ { - name: "extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "extension_not_found", + extensions: map[component.ID]component.Component{}, config: ClientConfig{ Endpoint: "localhost:1234", TLS: configtls.ClientConfig{ @@ -172,10 +167,8 @@ func TestClientMiddlewareToClientErrors(t *testing.T) { }, { name: "get_client_options_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("get options failed")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("get options failed")), }, config: ClientConfig{ Endpoint: "localhost:1234", @@ -195,7 +188,7 @@ func TestClientMiddlewareToClientErrors(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // Test creating the client with middleware errors - _, err := tc.config.ToClientConn(context.Background(), tc.host, componenttest.NewNopTelemetrySettings()) + _, err := tc.config.ToClientConn(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(tc.extensions)) require.Error(t, err) assert.Contains(t, err.Error(), tc.errText) }) diff --git a/config/configgrpc/configgrpc.go b/config/configgrpc/configgrpc.go index 3bd98b0b2ad..fde8e3d6f3d 100644 --- a/config/configgrpc/configgrpc.go +++ b/config/configgrpc/configgrpc.go @@ -268,17 +268,31 @@ func WithGrpcDialOption(opt grpc.DialOption) ToClientConnOption { } func (grpcDialOptionWrapper) isToClientConnOption() {} +type clientExtensionsOption struct { + extensions map[component.ID]component.Component +} + +// WithClientExtensions is a [ToClientConnOption] which supplies the map of extensions to search for middlewares. +// +// Typically called with the output of host.GetExtensions(). +func WithClientExtensions(extensions map[component.ID]component.Component) ToClientConnOption { + return clientExtensionsOption{extensions: extensions} +} +func (clientExtensionsOption) isToClientConnOption() {} + // ToClientConn creates a client connection to the given target. By default, it's // a non-blocking dial (the function won't wait for connections to be // established, and connecting happens in the background). To make it a blocking // dial, use the WithGrpcDialOption(grpc.WithBlock()) option. +// +// To allow the configuration to reference middleware extensions, +// use the [WithClientExtensions] option with the output of host.GetExtensions(). func (cc *ClientConfig) ToClientConn( ctx context.Context, - host component.Host, settings component.TelemetrySettings, extraOpts ...ToClientConnOption, ) (*grpc.ClientConn, error) { - grpcOpts, err := cc.getGrpcDialOptions(ctx, host, settings, extraOpts) + grpcOpts, err := cc.getGrpcDialOptions(ctx, settings, extraOpts) if err != nil { return nil, err } @@ -299,10 +313,20 @@ func (cc *ClientConfig) addHeadersIfAbsent(ctx context.Context) context.Context func (cc *ClientConfig) getGrpcDialOptions( ctx context.Context, - host component.Host, settings component.TelemetrySettings, extraOpts []ToClientConnOption, ) ([]grpc.DialOption, error) { + var extraGrpcOpts []grpc.DialOption + var extensions map[component.ID]component.Component + for _, opt := range extraOpts { + switch opt := opt.(type) { + case grpcDialOptionWrapper: + extraGrpcOpts = append(extraGrpcOpts, opt.opt) + case clientExtensionsOption: + extensions = opt.extensions + } + } + var opts []grpc.DialOption if cc.Compression.IsCompressed() { cp, err := getGRPCCompressionName(cc.Compression) @@ -343,11 +367,11 @@ func (cc *ClientConfig) getGrpcDialOptions( } if cc.Auth.HasValue() { - if host.GetExtensions() == nil { + if extensions == nil { return nil, errors.New("no extensions configuration available") } - grpcAuthenticator, cerr := cc.Auth.Get().GetGRPCClientAuthenticator(ctx, host.GetExtensions()) + grpcAuthenticator, cerr := cc.Auth.Get().GetGRPCClientAuthenticator(ctx, extensions) if cerr != nil { return nil, cerr } @@ -389,18 +413,14 @@ func (cc *ClientConfig) getGrpcDialOptions( // Apply middleware options. Note: OpenTelemetry could be registered as an extension. for _, middleware := range cc.Middlewares { - middlewareOptions, err := middleware.GetGRPCClientOptions(ctx, host.GetExtensions()) + middlewareOptions, err := middleware.GetGRPCClientOptions(ctx, extensions) 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) - } - } + opts = append(opts, extraGrpcOpts...) return opts, nil } @@ -436,14 +456,28 @@ func WithGrpcServerOption(opt grpc.ServerOption) ToServerOption { } func (grpcServerOptionWrapper) isToServerOption() {} +type serverExtensionsOption struct { + extensions map[component.ID]component.Component +} + +// WithServerExtensions is a [ToServerOption] which supplies the map of extensions to search for middlewares. +// +// Typically called with the output of host.GetExtensions(). +func WithServerExtensions(extensions map[component.ID]component.Component) ToServerOption { + return serverExtensionsOption{extensions: extensions} +} +func (serverExtensionsOption) isToServerOption() {} + // ToServer returns a [grpc.Server] for the configuration. +// +// To allow the configuration to reference middleware extensions, +// use the [WithServerExtensions] option with the output of host.GetExtensions(). func (sc *ServerConfig) ToServer( ctx context.Context, - host component.Host, settings component.TelemetrySettings, extraOpts ...ToServerOption, ) (*grpc.Server, error) { - grpcOpts, err := sc.getGrpcServerOptions(ctx, host, settings, extraOpts) + grpcOpts, err := sc.getGrpcServerOptions(ctx, settings, extraOpts) if err != nil { return nil, err } @@ -452,10 +486,20 @@ func (sc *ServerConfig) ToServer( func (sc *ServerConfig) getGrpcServerOptions( ctx context.Context, - host component.Host, settings component.TelemetrySettings, extraOpts []ToServerOption, ) ([]grpc.ServerOption, error) { + var extraGrpcOpts []grpc.ServerOption + var extensions map[component.ID]component.Component + for _, opt := range extraOpts { + switch opt := opt.(type) { + case grpcServerOptionWrapper: + extraGrpcOpts = append(extraGrpcOpts, opt.opt) + case serverExtensionsOption: + extensions = opt.extensions + } + } + var opts []grpc.ServerOption if sc.TLS.HasValue() { @@ -515,7 +559,7 @@ func (sc *ServerConfig) getGrpcServerOptions( var sInterceptors []grpc.StreamServerInterceptor if sc.Auth.HasValue() { - authenticator, err := sc.Auth.Get().GetServerAuthenticator(ctx, host.GetExtensions()) + authenticator, err := sc.Auth.Get().GetServerAuthenticator(ctx, extensions) if err != nil { return nil, err } @@ -539,18 +583,14 @@ func (sc *ServerConfig) getGrpcServerOptions( // Apply middleware options. Note: OpenTelemetry could be registered as an extension. for _, middleware := range sc.Middlewares { - middlewareOptions, err := middleware.GetGRPCServerOptions(ctx, host.GetExtensions()) + middlewareOptions, err := middleware.GetGRPCServerOptions(ctx, extensions) 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) - } - } + opts = append(opts, extraGrpcOpts...) return opts, nil } diff --git a/config/configgrpc/configgrpc_test.go b/config/configgrpc/configgrpc_test.go index 6c7185ae6f5..4d39d699610 100644 --- a/config/configgrpc/configgrpc_test.go +++ b/config/configgrpc/configgrpc_test.go @@ -122,7 +122,7 @@ func TestDefaultGrpcClientSettings(t *testing.T) { Insecure: true, }, } - opts, err := cc.getGrpcDialOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{}) + opts, err := cc.getGrpcDialOptions(context.Background(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{}) require.NoError(t, err) /* Expecting 2 DialOptions: * - WithTransportCredentials (TLS) @@ -140,7 +140,6 @@ func TestGrpcClientExtraOption(t *testing.T) { extraOpt := grpc.WithUserAgent("test-agent") opts, err := cc.getGrpcDialOptions( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{WithGrpcDialOption(extraOpt)}, ) @@ -156,9 +155,9 @@ func TestGrpcClientExtraOption(t *testing.T) { func TestAllGrpcClientSettings(t *testing.T) { tests := []struct { - settings ClientConfig - name string - host component.Host + settings ClientConfig + name string + extensions map[component.ID]component.Component }{ { name: "test all with gzip compression", @@ -183,10 +182,8 @@ func TestAllGrpcClientSettings(t *testing.T) { Authority: "pseudo-authority", Auth: configoptional.Some(configauth.Config{AuthenticatorID: testAuthID}), }, - host: &mockHost{ - ext: map[component.ID]component.Component{ - testAuthID: extensionauthtest.NewNopClient(), - }, + extensions: map[component.ID]component.Component{ + testAuthID: extensionauthtest.NewNopClient(), }, }, { @@ -212,10 +209,8 @@ func TestAllGrpcClientSettings(t *testing.T) { Authority: "pseudo-authority", Auth: configoptional.Some(configauth.Config{AuthenticatorID: testAuthID}), }, - host: &mockHost{ - ext: map[component.ID]component.Component{ - testAuthID: extensionauthtest.NewNopClient(), - }, + extensions: map[component.ID]component.Component{ + testAuthID: extensionauthtest.NewNopClient(), }, }, { @@ -241,16 +236,16 @@ func TestAllGrpcClientSettings(t *testing.T) { Authority: "pseudo-authority", Auth: configoptional.Some(configauth.Config{AuthenticatorID: testAuthID}), }, - host: &mockHost{ - ext: map[component.ID]component.Component{ - testAuthID: extensionauthtest.NewNopClient(), - }, + extensions: map[component.ID]component.Component{ + testAuthID: extensionauthtest.NewNopClient(), }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - opts, err := test.settings.getGrpcDialOptions(context.Background(), test.host, componenttest.NewNopTelemetrySettings(), []ToClientConnOption{}) + opts, err := test.settings.getGrpcDialOptions(context.Background(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{ + WithClientExtensions(test.extensions), + }) require.NoError(t, err) /* Expecting 11 DialOptions: * - WithDefaultCallOptions (Compression) @@ -304,7 +299,7 @@ func TestDefaultGrpcServerSettings(t *testing.T) { Endpoint: "0.0.0.0:1234", }, } - opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) + opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) require.NoError(t, err) assert.Len(t, opts, 3) } @@ -318,7 +313,6 @@ 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)}, ) @@ -407,7 +401,7 @@ func TestAllGrpcServerSettingsExceptAuth(t *testing.T) { }), }), } - opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) + opts, err := gss.getGrpcServerOptions(context.Background(), componenttest.NewNopTelemetrySettings(), []ToServerOption{}) require.NoError(t, err) assert.Len(t, opts, 10) } @@ -422,12 +416,9 @@ func TestGrpcServerAuthSettings(t *testing.T) { AuthenticatorID: mockID, }) - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: extensionauthtest.NewNopServer(), - }, - } - srv, err := gss.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings()) + srv, err := gss.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), WithServerExtensions(map[component.ID]component.Component{ + mockID: extensionauthtest.NewNopServer(), + })) require.NoError(t, err) assert.NotNil(t, srv) } @@ -457,9 +448,9 @@ func TestGrpcClientConfigInvalidBalancer(t *testing.T) { func TestGRPCClientSettingsError(t *testing.T) { tests := []struct { - settings ClientConfig - err string - host component.Host + settings ClientConfig + err string + extensions map[component.ID]component.Component }{ { err: "failed to load TLS config: failed to load CA CertPool File: failed to load cert /doesnt/exist:", @@ -497,7 +488,7 @@ func TestGRPCClientSettingsError(t *testing.T) { Endpoint: "localhost:1234", Auth: configoptional.Some(configauth.Config{AuthenticatorID: doesntExistID}), }, - host: &mockHost{ext: map[component.ID]component.Component{}}, + extensions: map[component.ID]component.Component{}, }, { err: "no extensions configuration available", @@ -505,7 +496,6 @@ func TestGRPCClientSettingsError(t *testing.T) { Endpoint: "localhost:1234", Auth: configoptional.Some(configauth.Config{AuthenticatorID: doesntExistID}), }, - host: &mockHost{}, }, { err: "unsupported compression type \"zlib\"", @@ -516,7 +506,6 @@ func TestGRPCClientSettingsError(t *testing.T) { }, Compression: "zlib", }, - host: &mockHost{}, }, { err: "unsupported compression type \"deflate\"", @@ -527,7 +516,6 @@ func TestGRPCClientSettingsError(t *testing.T) { }, Compression: "deflate", }, - host: &mockHost{}, }, { err: "unsupported compression type \"bad\"", @@ -538,13 +526,12 @@ func TestGRPCClientSettingsError(t *testing.T) { }, Compression: "bad", }, - host: &mockHost{}, }, } for _, test := range tests { t.Run(test.err, func(t *testing.T) { require.NoError(t, test.settings.Validate()) - _, err := test.settings.ToClientConn(context.Background(), test.host, componenttest.NewNopTelemetrySettings()) + _, err := test.settings.ToClientConn(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(test.extensions)) require.Error(t, err) assert.ErrorContains(t, err, test.err) }) @@ -558,7 +545,7 @@ func TestUseSecure(t *testing.T) { Compression: "", TLS: configtls.ClientConfig{}, } - dialOpts, err := cc.getGrpcDialOptions(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{}) + dialOpts, err := cc.getGrpcDialOptions(context.Background(), componenttest.NewNopTelemetrySettings(), []ToClientConnOption{}) require.NoError(t, err) assert.Len(t, dialOpts, 2) } @@ -611,7 +598,7 @@ func TestGRPCServerSettingsError(t *testing.T) { } for _, test := range tests { t.Run(test.err, func(t *testing.T) { - _, err := test.settings.ToServer(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + _, err := test.settings.ToServer(context.Background(), componenttest.NewNopTelemetrySettings()) assert.ErrorContains(t, err, test.err) }) } @@ -1201,14 +1188,10 @@ func (gts *grpcTraceServer) Export(ctx context.Context, _ ptraceotlp.ExportReque return ptraceotlp.NewExportResponse(), nil } -func (gts *grpcTraceServer) startTestServer(t *testing.T, gss configoptional.Optional[ServerConfig]) (*grpc.Server, string) { - return gts.startTestServerWithHost(t, gss, componenttest.NewNopHost()) -} - -func (gts *grpcTraceServer) startTestServerWithHost(t *testing.T, gss configoptional.Optional[ServerConfig], host component.Host, opts ...ToServerOption) (*grpc.Server, string) { +func (gts *grpcTraceServer) startTestServer(t *testing.T, gss configoptional.Optional[ServerConfig], opts ...ToServerOption) (*grpc.Server, string) { listener, err := gss.Get().NetAddr.Listen(context.Background()) require.NoError(t, err) - server, err := gss.Get().ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), opts...) + server, err := gss.Get().ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), opts...) require.NoError(t, err) ptraceotlp.RegisterGRPCServer(server, gts) go func() { @@ -1217,14 +1200,14 @@ func (gts *grpcTraceServer) startTestServerWithHost(t *testing.T, gss configopti return server, listener.Addr().String() } -func (gts *grpcTraceServer) startTestServerWithHostError(_ *testing.T, gss ServerConfig, host component.Host, opts ...ToServerOption) (*grpc.Server, error) { +func (gts *grpcTraceServer) startTestServerError(_ *testing.T, gss ServerConfig, opts ...ToServerOption) (*grpc.Server, error) { listener, err := gss.NetAddr.Listen(context.Background()) if err != nil { return nil, err } defer listener.Close() - server, err := gss.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), opts...) + server, err := gss.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), opts...) if err != nil { return nil, err } @@ -1234,13 +1217,8 @@ func (gts *grpcTraceServer) startTestServerWithHostError(_ *testing.T, gss Serve } // sendTestRequest issues a ptraceotlp export request and captures metadata. -func sendTestRequest(t *testing.T, cc ClientConfig) (ptraceotlp.ExportResponse, error) { - return sendTestRequestWithHost(t, cc, componenttest.NewNopHost()) -} - -// sendTestRequestWithHost is similar to sendTestRequest but allows specifying the host -func sendTestRequestWithHost(t *testing.T, cc ClientConfig, host component.Host) (ptraceotlp.ExportResponse, error) { - grpcClientConn, errClient := cc.ToClientConn(context.Background(), host, componenttest.NewNopTelemetrySettings()) +func sendTestRequest(t *testing.T, cc ClientConfig, opts ...ToClientConnOption) (ptraceotlp.ExportResponse, error) { + grpcClientConn, errClient := cc.ToClientConn(context.Background(), componenttest.NewNopTelemetrySettings(), opts...) require.NoError(t, errClient) defer func() { assert.NoError(t, grpcClientConn.Close()) }() c := ptraceotlp.NewGRPCClient(grpcClientConn) @@ -1260,12 +1238,3 @@ func tempSocketName(t *testing.T) string { require.NoError(t, os.Remove(socket)) return socket } - -type mockHost struct { - component.Host - ext map[component.ID]component.Component -} - -func (nh *mockHost) GetExtensions() map[component.ID]component.Component { - return nh.ext -} diff --git a/config/configgrpc/server_middleware_test.go b/config/configgrpc/server_middleware_test.go index 1fc47231a13..fc9eb224f35 100644 --- a/config/configgrpc/server_middleware_test.go +++ b/config/configgrpc/server_middleware_test.go @@ -62,11 +62,9 @@ func newTestServerMiddleware(name string) extension.Extension { 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"), - }, + extensions := map[component.ID]component.Component{ + component.MustNewID("test1"): newTestServerMiddleware("test1"), + component.MustNewID("test2"): newTestServerMiddleware("test2"), } // Setup the server with both middleware options @@ -76,7 +74,7 @@ func TestGrpcServerUnaryInterceptor(t *testing.T) { // Create the server with middleware interceptors { var srv *grpc.Server - srv, addr = server.startTestServerWithHost(t, configoptional.Some(ServerConfig{ + srv, addr = server.startTestServer(t, configoptional.Some(ServerConfig{ NetAddr: confignet.AddrConfig{ Endpoint: "localhost:0", Transport: confignet.TransportTypeTCP, @@ -85,7 +83,7 @@ func TestGrpcServerUnaryInterceptor(t *testing.T) { newTestMiddlewareConfig("test1"), newTestMiddlewareConfig("test2"), }, - }), host) + }), WithServerExtensions(extensions)) defer srv.Stop() } @@ -107,16 +105,14 @@ func TestGrpcServerUnaryInterceptor(t *testing.T) { // specifically related to middleware resolution and API calls. func TestServerMiddlewareToServerErrors(t *testing.T) { tests := []struct { - name string - host component.Host - config ServerConfig - errText string + name string + extensions map[component.ID]component.Component + config ServerConfig + errText string }{ { - name: "extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "extension_not_found", + extensions: map[component.ID]component.Component{}, config: ServerConfig{ NetAddr: confignet.AddrConfig{ Endpoint: "localhost:0", @@ -132,10 +128,8 @@ func TestServerMiddlewareToServerErrors(t *testing.T) { }, { name: "get_server_options_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("get server options failed")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("get server options failed")), }, config: ServerConfig{ NetAddr: confignet.AddrConfig{ @@ -156,7 +150,7 @@ func TestServerMiddlewareToServerErrors(t *testing.T) { t.Run(tc.name, func(t *testing.T) { // Test creating the server with middleware errors server := &grpcTraceServer{} - srv, err := server.startTestServerWithHostError(t, tc.config, tc.host) + srv, err := server.startTestServerError(t, tc.config, WithServerExtensions(tc.extensions)) if srv != nil { srv.Stop() } diff --git a/config/confighttp/client.go b/config/confighttp/client.go index 34c2b3606e9..2652d0e6cc5 100644 --- a/config/confighttp/client.go +++ b/config/confighttp/client.go @@ -147,13 +147,34 @@ func (cc *ClientConfig) Validate() error { // ToClientOption is an option to change the behavior of the HTTP client // returned by ClientConfig.ToClient(). -// There are currently no available options. type ToClientOption interface { sealed() } +type clientExtensionsOption struct { + extensions map[component.ID]component.Component +} + +// WithClientExtensions is a [ToClientOption] which supplies the map of extensions to search for middlewares. +// +// Typically called with the output of host.GetExtensions(). +func WithClientExtensions(extensions map[component.ID]component.Component) ToClientOption { + return clientExtensionsOption{extensions: extensions} +} +func (clientExtensionsOption) sealed() {} + // ToClient creates an HTTP client. -func (cc *ClientConfig) ToClient(ctx context.Context, host component.Host, settings component.TelemetrySettings, _ ...ToClientOption) (*http.Client, error) { +// +// To allow the configuration to reference middleware extensions, +// use the [WithClientExtensions] option with the output of host.GetExtensions(). +func (cc *ClientConfig) ToClient(ctx context.Context, settings component.TelemetrySettings, opts ...ToClientOption) (*http.Client, error) { + var extensions map[component.ID]component.Component + for _, opt := range opts { + if opt, ok := opt.(clientExtensionsOption); ok { + extensions = opt.extensions + } + } + tlsCfg, err := cc.TLS.LoadTLSConfig(ctx) if err != nil { return nil, err @@ -201,7 +222,7 @@ func (cc *ClientConfig) ToClient(ctx context.Context, host component.Host, setti // forward order. The first middleware runs after authentication. for i := len(cc.Middlewares) - 1; i >= 0; i-- { var wrapper func(http.RoundTripper) (http.RoundTripper, error) - wrapper, err = cc.Middlewares[i].GetHTTPClientRoundTripper(ctx, host.GetExtensions()) + wrapper, err = cc.Middlewares[i].GetHTTPClientRoundTripper(ctx, extensions) // If we failed to get the middleware if err != nil { return nil, err @@ -217,7 +238,7 @@ func (cc *ClientConfig) ToClient(ctx context.Context, host component.Host, setti // request signing-based auth mechanisms operate after compression // and header middleware modifies the request if cc.Auth.HasValue() { - ext := host.GetExtensions() + ext := extensions if ext == nil { return nil, errors.New("extensions configuration not found") } diff --git a/config/confighttp/client_middleware_test.go b/config/confighttp/client_middleware_test.go index 6483eb7b750..ae3b912c796 100644 --- a/config/confighttp/client_middleware_test.go +++ b/config/confighttp/client_middleware_test.go @@ -75,11 +75,9 @@ func TestClientMiddlewares(t *testing.T) { 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"), - }, + extensions := map[component.ID]component.Component{ + component.MustNewID("test1"): newTestClientMiddleware("test1"), + component.MustNewID("test2"): newTestClientMiddleware("test2"), } // Test with different middleware configurations @@ -119,7 +117,7 @@ func TestClientMiddlewares(t *testing.T) { } // Create the client - client, err := clientConfig.ToClient(context.Background(), host, componenttest.NewNopTelemetrySettings()) + client, err := clientConfig.ToClient(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(extensions)) require.NoError(t, err) // Create a request to the test server @@ -149,16 +147,14 @@ func TestClientMiddlewareErrors(t *testing.T) { // Test cases for HTTP client middleware errors httpTests := []struct { - name string - host component.Host - config ClientConfig - errText string + name string + extensions map[component.ID]component.Component + config ClientConfig + errText string }{ { - name: "extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "extension_not_found", + extensions: map[component.ID]component.Component{}, config: ClientConfig{ Endpoint: server.URL, Middlewares: []configmiddleware.Config{ @@ -171,10 +167,8 @@ func TestClientMiddlewareErrors(t *testing.T) { }, { name: "get_round_tripper_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), }, config: ClientConfig{ Endpoint: server.URL, @@ -191,7 +185,7 @@ func TestClientMiddlewareErrors(t *testing.T) { 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()) + _, err := tc.config.ToClient(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(tc.extensions)) require.Error(t, err) assert.Contains(t, err.Error(), tc.errText) }) @@ -203,16 +197,14 @@ func TestClientMiddlewareErrors(t *testing.T) { func TestGRPCClientMiddlewareErrors(t *testing.T) { // Test cases for gRPC client middleware errors grpcTests := []struct { - name string - host component.Host - config ClientConfig - errText string + name string + extensions map[component.ID]component.Component + config ClientConfig + errText string }{ { - name: "grpc_extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "grpc_extension_not_found", + extensions: map[component.ID]component.Component{}, config: ClientConfig{ Endpoint: "localhost:1234", Middlewares: []configmiddleware.Config{ @@ -225,10 +217,8 @@ func TestGRPCClientMiddlewareErrors(t *testing.T) { }, { name: "grpc_get_client_options_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), }, config: ClientConfig{ Endpoint: "localhost:1234", @@ -247,7 +237,7 @@ func TestGRPCClientMiddlewareErrors(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()) + _, err := tc.config.ToClient(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(tc.extensions)) require.Error(t, err) assert.Contains(t, err.Error(), tc.errText) }) diff --git a/config/confighttp/client_test.go b/config/confighttp/client_test.go index b1c8289d129..e66b0d2d8b9 100644 --- a/config/confighttp/client_test.go +++ b/config/confighttp/client_test.go @@ -44,10 +44,8 @@ var ( ) func TestAllHTTPClientSettings(t *testing.T) { - host := &mockHost{ - ext: map[component.ID]component.Component{ - testAuthID: extensionauthtest.NewNopClient(), - }, + extensions := map[component.ID]component.Component{ + testAuthID: extensionauthtest.NewNopClient(), } maxIdleConns := 50 @@ -169,7 +167,7 @@ func TestAllHTTPClientSettings(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tel := componenttest.NewNopTelemetrySettings() tel.TracerProvider = nil - client, err := tt.settings.ToClient(context.Background(), host, tel) + client, err := tt.settings.ToClient(context.Background(), tel, WithClientExtensions(extensions)) if tt.shouldError { assert.Error(t, err) return @@ -192,10 +190,8 @@ func TestAllHTTPClientSettings(t *testing.T) { } func TestPartialHTTPClientSettings(t *testing.T) { - host := &mockHost{ - ext: map[component.ID]component.Component{ - testAuthID: extensionauthtest.NewNopClient(), - }, + extensions := map[component.ID]component.Component{ + testAuthID: extensionauthtest.NewNopClient(), } tests := []struct { @@ -221,7 +217,7 @@ func TestPartialHTTPClientSettings(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tel := componenttest.NewNopTelemetrySettings() tel.TracerProvider = nil - client, err := tt.settings.ToClient(context.Background(), host, tel) + client, err := tt.settings.ToClient(context.Background(), tel, WithClientExtensions(extensions)) require.NoError(t, err) transport := client.Transport.(*http.Transport) assert.Equal(t, 1024, transport.ReadBufferSize) @@ -270,7 +266,7 @@ func TestProxyURL(t *testing.T) { tel := componenttest.NewNopTelemetrySettings() tel.TracerProvider = nil - client, err := s.ToClient(context.Background(), componenttest.NewNopHost(), tel) + client, err := s.ToClient(context.Background(), tel) if tt.err { require.Error(t, err) @@ -297,9 +293,7 @@ func TestProxyURL(t *testing.T) { } func TestHTTPClientSettingsError(t *testing.T) { - host := &mockHost{ - ext: map[component.ID]component.Component{}, - } + extensions := map[component.ID]component.Component{} tests := []struct { settings ClientConfig err string @@ -340,7 +334,7 @@ func TestHTTPClientSettingsError(t *testing.T) { } for _, tt := range tests { t.Run(tt.err, func(t *testing.T) { - _, err := tt.settings.ToClient(context.Background(), host, componenttest.NewNopTelemetrySettings()) + _, err := tt.settings.ToClient(context.Background(), componenttest.NewNopTelemetrySettings(), WithClientExtensions(extensions)) assert.Regexp(t, tt.err, err) }) } @@ -371,10 +365,10 @@ func (m *mockClient) RoundTripper(http.RoundTripper) (http.RoundTripper, error) func TestHTTPClientSettingWithAuthConfig(t *testing.T) { tests := []struct { - name string - shouldErr bool - settings ClientConfig - host component.Host + name string + shouldErr bool + settings ClientConfig + extensions map[component.ID]component.Component }{ { name: "no_auth_extension_enabled", @@ -383,10 +377,8 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Auth: configoptional.None[configauth.Config](), }, shouldErr: false, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: extensionauthtest.NewNopClient(), - }, + extensions: map[component.ID]component.Component{ + mockID: extensionauthtest.NewNopClient(), }, }, { @@ -396,10 +388,8 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Auth: configoptional.Some(configauth.Config{AuthenticatorID: dummyID}), }, shouldErr: true, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: extensionauthtest.NewNopClient(), - }, + extensions: map[component.ID]component.Component{ + mockID: extensionauthtest.NewNopClient(), }, }, { @@ -409,7 +399,6 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Auth: configoptional.Some(configauth.Config{AuthenticatorID: dummyID}), }, shouldErr: true, - host: componenttest.NewNopHost(), }, { name: "with_auth_configuration_has_extension", @@ -418,10 +407,8 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Auth: configoptional.Some(configauth.Config{AuthenticatorID: mockID}), }, shouldErr: false, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: &mockClient{}, - }, + extensions: map[component.ID]component.Component{ + mockID: &mockClient{}, }, }, { @@ -434,10 +421,8 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { }, }, shouldErr: false, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: &mockClient{}, - }, + extensions: map[component.ID]component.Component{ + mockID: &mockClient{}, }, }, { @@ -448,10 +433,8 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Compression: configcompression.TypeGzip, }, shouldErr: false, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: &mockClient{}, - }, + extensions: map[component.ID]component.Component{ + mockID: &mockClient{}, }, }, { @@ -461,17 +444,15 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { Auth: configoptional.Some(configauth.Config{AuthenticatorID: mockID}), }, shouldErr: true, - host: &mockHost{ - ext: map[component.ID]component.Component{ - mockID: extensionauthtest.NewErr(errors.New("error")), - }, + extensions: map[component.ID]component.Component{ + mockID: extensionauthtest.NewErr(errors.New("error")), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Omit TracerProvider and MeterProvider in TelemetrySettings as otelhttp.Transport cannot be introspected - client, err := tt.settings.ToClient(context.Background(), tt.host, nilProvidersSettings) + client, err := tt.settings.ToClient(context.Background(), nilProvidersSettings, WithClientExtensions(tt.extensions)) if tt.shouldErr { assert.Error(t, err) return @@ -534,7 +515,7 @@ func TestHttpClientHeaders(t *testing.T) { Timeout: 0, Headers: tt.headers, } - client, _ := setting.ToClient(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + client, _ := setting.ToClient(context.Background(), componenttest.NewNopTelemetrySettings()) req, err := http.NewRequest(http.MethodGet, setting.Endpoint, http.NoBody) require.NoError(t, err) _, err = client.Do(req) @@ -570,7 +551,7 @@ func TestHttpClientHostHeader(t *testing.T) { Timeout: 0, Headers: tt.headers, } - client, _ := setting.ToClient(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + client, _ := setting.ToClient(context.Background(), componenttest.NewNopTelemetrySettings()) req, err := http.NewRequest(http.MethodGet, setting.Endpoint, http.NoBody) require.NoError(t, err) _, err = client.Do(req) @@ -589,7 +570,7 @@ func TestHttpTransportOptions(t *testing.T) { clientConfig.IdleConnTimeout = time.Duration(100) clientConfig.MaxConnsPerHost = 100 clientConfig.MaxIdleConnsPerHost = 100 - client, err := clientConfig.ToClient(context.Background(), &mockHost{}, settings) + client, err := clientConfig.ToClient(context.Background(), settings) require.NoError(t, err) transport, ok := client.Transport.(*http.Transport) require.True(t, ok, "client.Transport is not an *http.Transport") @@ -603,7 +584,7 @@ func TestHttpTransportOptions(t *testing.T) { clientConfig.IdleConnTimeout = 0 clientConfig.MaxConnsPerHost = 0 clientConfig.IdleConnTimeout = time.Duration(0) - client, err = clientConfig.ToClient(context.Background(), &mockHost{}, settings) + client, err = clientConfig.ToClient(context.Background(), settings) require.NoError(t, err) transport, ok = client.Transport.(*http.Transport) require.True(t, ok, "client.Transport is not an *http.Transport") diff --git a/config/confighttp/compression_test.go b/config/confighttp/compression_test.go index f1db77e588e..32110ef3ca3 100644 --- a/config/confighttp/compression_test.go +++ b/config/confighttp/compression_test.go @@ -179,7 +179,7 @@ func TestHTTPClientCompression(t *testing.T) { return } require.NoError(t, err) - client, err := clientSettings.ToClient(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings()) + client, err := clientSettings.ToClient(context.Background(), componenttest.NewNopTelemetrySettings()) require.NoError(t, err) res, err := client.Do(req) if tt.shouldError { diff --git a/config/confighttp/confighttp_example_test.go b/config/confighttp/confighttp_example_test.go index 73a287d9e36..e648522a411 100644 --- a/config/confighttp/confighttp_example_test.go +++ b/config/confighttp/confighttp_example_test.go @@ -14,11 +14,15 @@ func ExampleServerConfig() { settings := NewDefaultServerConfig() settings.Endpoint = "localhost:443" + // Typically obtained as an argument of Component.Start() + host := componenttest.NewNopHost() + s, err := settings.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), - http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) + http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), + WithServerExtensions(host.GetExtensions()), + ) if err != nil { panic(err) } diff --git a/config/confighttp/internal/options.go b/config/confighttp/internal/options.go index 338ca721bc2..d25f9e59fd7 100644 --- a/config/confighttp/internal/options.go +++ b/config/confighttp/internal/options.go @@ -8,6 +8,8 @@ import ( "net/http" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + + "go.opentelemetry.io/collector/component" ) // ToServerOptions has options that change the behavior of the HTTP server @@ -16,6 +18,7 @@ type ToServerOptions struct { ErrHandler func(w http.ResponseWriter, r *http.Request, errorMsg string, statusCode int) Decoders map[string]func(body io.ReadCloser) (io.ReadCloser, error) OtelhttpOpts []otelhttp.Option + Extensions map[component.ID]component.Component } func (tso *ToServerOptions) Apply(opts ...ToServerOption) { diff --git a/config/confighttp/server.go b/config/confighttp/server.go index f0f95e3f994..fa83fe0cae1 100644 --- a/config/confighttp/server.go +++ b/config/confighttp/server.go @@ -167,8 +167,20 @@ func WithDecoder(key string, dec func(body io.ReadCloser) (io.ReadCloser, error) }) } +// WithServerExtensions is a [ToServerOption] which supplies the map of extensions to search for middlewares. +// +// Typically called with the output of host.GetExtensions(). +func WithServerExtensions(extensions map[component.ID]component.Component) ToServerOption { + return internal.ToServerOptionFunc(func(opts *toServerOptions) { + opts.Extensions = extensions + }) +} + // ToServer creates an http.Server from settings object. -func (sc *ServerConfig) ToServer(ctx context.Context, host component.Host, settings component.TelemetrySettings, handler http.Handler, opts ...ToServerOption) (*http.Server, error) { +// +// To allow the configuration to reference middleware extensions, +// use the [WithServerExtensions] option with the output of host.GetExtensions(). +func (sc *ServerConfig) ToServer(ctx context.Context, settings component.TelemetrySettings, handler http.Handler, opts ...ToServerOption) (*http.Server, error) { serverOpts := &toServerOptions{} serverOpts.Apply(opts...) @@ -184,7 +196,7 @@ func (sc *ServerConfig) ToServer(ctx context.Context, host component.Host, setti // forward order. The first middleware runs after // decompression, below, preceded by Auth, CORS, etc. for i := len(sc.Middlewares) - 1; i >= 0; i-- { - wrapper, err := sc.Middlewares[i].GetHTTPServerHandler(ctx, host.GetExtensions()) + wrapper, err := sc.Middlewares[i].GetHTTPServerHandler(ctx, serverOpts.Extensions) // If we failed to get the middleware if err != nil { return nil, err @@ -210,7 +222,7 @@ func (sc *ServerConfig) ToServer(ctx context.Context, host component.Host, setti if sc.Auth.HasValue() { auth := sc.Auth.Get() - server, err := auth.GetServerAuthenticator(ctx, host.GetExtensions()) + server, err := auth.GetServerAuthenticator(ctx, serverOpts.Extensions) if err != nil { return nil, err } diff --git a/config/confighttp/server_middleware_test.go b/config/confighttp/server_middleware_test.go index 92ae8f600a5..27518f2d4a0 100644 --- a/config/confighttp/server_middleware_test.go +++ b/config/confighttp/server_middleware_test.go @@ -54,11 +54,9 @@ func newTestServerConfig(name string) configmiddleware.Config { 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"), - }, + extensions := map[component.ID]component.Component{ + component.MustNewID("test1"): newTestServerMiddleware("test1"), + component.MustNewID("test2"): newTestServerMiddleware("test2"), } // Test with different middleware configurations @@ -105,9 +103,9 @@ func TestServerMiddleware(t *testing.T) { // Create the server srv, err := cfg.ToServer( context.Background(), - host, componenttest.NewNopTelemetrySettings(), handler, + WithServerExtensions(extensions), ) require.NoError(t, err) @@ -141,16 +139,14 @@ func TestServerMiddlewareErrors(t *testing.T) { // Test cases for HTTP server middleware errors httpTests := []struct { - name string - host component.Host - config ServerConfig - errText string + name string + extensions map[component.ID]component.Component + config ServerConfig + errText string }{ { - name: "extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "extension_not_found", + extensions: map[component.ID]component.Component{}, config: ServerConfig{ Endpoint: "localhost:0", Middlewares: []configmiddleware.Config{ @@ -163,10 +159,8 @@ func TestServerMiddlewareErrors(t *testing.T) { }, { name: "get_http_handler_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("http middleware error")), }, config: ServerConfig{ Endpoint: "localhost:0", @@ -185,9 +179,9 @@ func TestServerMiddlewareErrors(t *testing.T) { // Trying to create the server should fail _, err := tc.config.ToServer( context.Background(), - tc.host, componenttest.NewNopTelemetrySettings(), handler, + WithServerExtensions(tc.extensions), ) require.Error(t, err) assert.Contains(t, err.Error(), tc.errText) @@ -196,16 +190,14 @@ func TestServerMiddlewareErrors(t *testing.T) { // Test cases for gRPC server middleware errors grpcTests := []struct { - name string - host component.Host - config ServerConfig - errText string + name string + extensions map[component.ID]component.Component + config ServerConfig + errText string }{ { - name: "grpc_extension_not_found", - host: &mockHost{ - ext: map[component.ID]component.Component{}, - }, + name: "grpc_extension_not_found", + extensions: map[component.ID]component.Component{}, config: ServerConfig{ Endpoint: "localhost:0", Middlewares: []configmiddleware.Config{ @@ -218,10 +210,8 @@ func TestServerMiddlewareErrors(t *testing.T) { }, { name: "get_grpc_handler_fails", - host: &mockHost{ - ext: map[component.ID]component.Component{ - component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), - }, + extensions: map[component.ID]component.Component{ + component.MustNewID("errormw"): extensionmiddlewaretest.NewErr(errors.New("grpc middleware error")), }, config: ServerConfig{ Endpoint: "localhost:0", @@ -240,9 +230,9 @@ func TestServerMiddlewareErrors(t *testing.T) { // Trying to create the server should fail _, err := tc.config.ToServer( context.Background(), - tc.host, componenttest.NewNopTelemetrySettings(), handler, + WithServerExtensions(tc.extensions), ) require.Error(t, err) assert.Contains(t, err.Error(), tc.errText) diff --git a/config/confighttp/server_test.go b/config/confighttp/server_test.go index 451306c3b7e..cef6a2c37f4 100644 --- a/config/confighttp/server_test.go +++ b/config/confighttp/server_test.go @@ -227,7 +227,6 @@ func TestHttpReception(t *testing.T) { s, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, errWrite := fmt.Fprint(w, "tt") @@ -252,7 +251,7 @@ func TestHttpReception(t *testing.T) { ForceAttemptHTTP2: true, } - client, errClient := cc.ToClient(context.Background(), componenttest.NewNopHost(), nilProvidersSettings) + client, errClient := cc.ToClient(context.Background(), nilProvidersSettings) require.NoError(t, errClient) if tt.forceHTTP1 { @@ -341,7 +340,6 @@ func TestHttpCors(t *testing.T) { s, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) @@ -382,7 +380,6 @@ func TestHttpCorsInvalidSettings(t *testing.T) { // This effectively does not enable CORS but should also not cause an error s, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) require.NoError(t, err) @@ -403,15 +400,13 @@ func TestHttpCorsWithSettings(t *testing.T) { }), } - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { - return ctx, errors.New("Settings failed") - }), - }, + extensions := map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + return ctx, errors.New("Settings failed") + }), } - srv, err := sc.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), nil) + srv, err := sc.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), nil, WithServerExtensions(extensions)) require.NoError(t, err) require.NotNil(t, srv) @@ -455,7 +450,6 @@ func TestHttpServerHeaders(t *testing.T) { s, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) @@ -539,13 +533,11 @@ func TestServerAuth(t *testing.T) { }), } - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { - authCalled = true - return ctx, nil - }), - }, + extensions := map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + authCalled = true + return ctx, nil + }), } handlerCalled := false @@ -553,7 +545,7 @@ func TestServerAuth(t *testing.T) { handlerCalled = true }) - srv, err := sc.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), handler) + srv, err := sc.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), handler, WithServerExtensions(extensions)) require.NoError(t, err) // tt @@ -573,7 +565,7 @@ func TestInvalidServerAuth(t *testing.T) { }), } - srv, err := sc.ToServer(context.Background(), componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.NewServeMux()) + srv, err := sc.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), http.NewServeMux()) require.Error(t, err) require.Nil(t, srv) } @@ -588,15 +580,18 @@ func TestFailedServerAuth(t *testing.T) { }, }), } - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { - return ctx, errors.New("invalid authorization") - }), - }, + extensions := map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + return ctx, errors.New("invalid authorization") + }), } - srv, err := sc.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) + srv, err := sc.ToServer( + context.Background(), + componenttest.NewNopTelemetrySettings(), + http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), + WithServerExtensions(extensions), + ) require.NoError(t, err) // tt @@ -618,12 +613,10 @@ func TestFailedServerAuthWithErrorHandler(t *testing.T) { }, }), } - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { - return ctx, errors.New("invalid authorization") - }), - }, + extensions := map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + return ctx, errors.New("invalid authorization") + }), } eh := func(w http.ResponseWriter, _ *http.Request, err string, statusCode int) { @@ -634,7 +627,13 @@ func TestFailedServerAuthWithErrorHandler(t *testing.T) { http.Error(w, err, http.StatusInternalServerError) } - srv, err := sc.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), WithErrorHandler(eh)) + srv, err := sc.ToServer( + context.Background(), + componenttest.NewNopTelemetrySettings(), + http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), + WithErrorHandler(eh), + WithServerExtensions(extensions), + ) require.NoError(t, err) // tt @@ -659,7 +658,6 @@ func TestServerWithErrorHandler(t *testing.T) { srv, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), WithErrorHandler(eh), @@ -687,7 +685,6 @@ func TestServerWithDecoder(t *testing.T) { srv, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), WithDecoder("something-else", decoder), @@ -714,7 +711,6 @@ func TestServerWithDecompression(t *testing.T) { srv, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { actualBody, err := io.ReadAll(req.Body) @@ -782,7 +778,6 @@ func TestDefaultMaxRequestBodySize(t *testing.T) { t.Run(tt.name, func(t *testing.T) { _, err := tt.settings.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), ) @@ -805,15 +800,13 @@ func TestAuthWithQueryParams(t *testing.T) { }), } - host := &mockHost{ - ext: map[component.ID]component.Component{ - mockID: newMockAuthServer(func(ctx context.Context, sources map[string][]string) (context.Context, error) { - require.Len(t, sources, 1) - assert.Equal(t, "1", sources["auth"][0]) - authCalled = true - return ctx, nil - }), - }, + extensions := map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, sources map[string][]string) (context.Context, error) { + require.Len(t, sources, 1) + assert.Equal(t, "1", sources["auth"][0]) + authCalled = true + return ctx, nil + }), } handlerCalled := false @@ -821,7 +814,7 @@ func TestAuthWithQueryParams(t *testing.T) { handlerCalled = true }) - srv, err := sc.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), handler) + srv, err := sc.ToServer(context.Background(), componenttest.NewNopTelemetrySettings(), handler, WithServerExtensions(extensions)) require.NoError(t, err) // tt @@ -832,15 +825,6 @@ func TestAuthWithQueryParams(t *testing.T) { assert.True(t, authCalled) } -type mockHost struct { - component.Host - ext map[component.ID]component.Component -} - -func (nh *mockHost) GetExtensions() map[component.ID]component.Component { - return nh.ext -} - func BenchmarkHttpRequest(b *testing.B) { tests := []struct { name string @@ -890,7 +874,6 @@ func BenchmarkHttpRequest(b *testing.B) { s, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, errWrite := fmt.Fprint(w, "tt") @@ -916,12 +899,12 @@ func BenchmarkHttpRequest(b *testing.B) { b.Run(bb.name, func(b *testing.B) { var c *http.Client if !bb.clientPerThread { - c, err = cc.ToClient(context.Background(), componenttest.NewNopHost(), nilProvidersSettings) + c, err = cc.ToClient(context.Background(), nilProvidersSettings) require.NoError(b, err) } b.RunParallel(func(pb *testing.PB) { if c == nil { - c, err = cc.ToClient(context.Background(), componenttest.NewNopHost(), nilProvidersSettings) + c, err = cc.ToClient(context.Background(), nilProvidersSettings) require.NoError(b, err) } if bb.forceHTTP1 { @@ -982,7 +965,6 @@ func TestHTTPServerKeepAlives(t *testing.T) { server, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) @@ -1043,7 +1025,6 @@ func TestHTTPServerTelemetry_Tracing(t *testing.T) { config.Endpoint = "localhost:0" srv, err := config.ToServer( context.Background(), - componenttest.NewNopHost(), telemetry.NewTelemetrySettings(), testcase.handler, ) diff --git a/config/confighttp/xconfighttp/options_test.go b/config/confighttp/xconfighttp/options_test.go index 9b7b7d48a6c..609ef9a0700 100644 --- a/config/confighttp/xconfighttp/options_test.go +++ b/config/confighttp/xconfighttp/options_test.go @@ -32,7 +32,6 @@ func TestServerWithOtelHTTPOptions(t *testing.T) { srv, err := sc.ToServer( context.Background(), - componenttest.NewNopHost(), telemetry, http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), WithOtelHTTPOptions( diff --git a/exporter/otlpexporter/otlp.go b/exporter/otlpexporter/otlp.go index ce042f85cac..fe28b2fb11f 100644 --- a/exporter/otlpexporter/otlp.go +++ b/exporter/otlpexporter/otlp.go @@ -63,7 +63,7 @@ func newExporter(cfg component.Config, set exporter.Settings) *baseExporter { // is the only place we get hold of Extensions which are required to construct auth round tripper. func (e *baseExporter) start(ctx context.Context, host component.Host) (err error) { agentOpt := configgrpc.WithGrpcDialOption(grpc.WithUserAgent(e.userAgent)) - if e.clientConn, err = e.config.ClientConfig.ToClientConn(ctx, host, e.settings, agentOpt); err != nil { + if e.clientConn, err = e.config.ClientConfig.ToClientConn(ctx, e.settings, agentOpt, configgrpc.WithClientExtensions(host.GetExtensions())); err != nil { return err } e.traceExporter = ptraceotlp.NewGRPCClient(e.clientConn) diff --git a/exporter/otlphttpexporter/otlp.go b/exporter/otlphttpexporter/otlp.go index 26573a1836d..59a1879bc48 100644 --- a/exporter/otlphttpexporter/otlp.go +++ b/exporter/otlphttpexporter/otlp.go @@ -20,6 +20,7 @@ import ( "google.golang.org/protobuf/proto" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper" @@ -82,7 +83,7 @@ func newExporter(cfg component.Config, set exporter.Settings) (*baseExporter, er // start actually creates the HTTP client. The client construction is deferred till this point as this // is the only place we get hold of Extensions which are required to construct auth round tripper. func (e *baseExporter) start(ctx context.Context, host component.Host) error { - client, err := e.config.ClientConfig.ToClient(ctx, host, e.settings) + client, err := e.config.ClientConfig.ToClient(ctx, e.settings, confighttp.WithClientExtensions(host.GetExtensions())) if err != nil { return err } diff --git a/extension/zpagesextension/zpagesextension.go b/extension/zpagesextension/zpagesextension.go index a8aec6e3329..446bc4cbec1 100644 --- a/extension/zpagesextension/zpagesextension.go +++ b/extension/zpagesextension/zpagesextension.go @@ -17,6 +17,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/config/confighttp" ) const ( @@ -92,7 +93,7 @@ func (zpe *zpagesExtension) Start(ctx context.Context, host component.Host) erro } zpe.telemetry.Logger.Info("Starting zPages extension", zap.Any("config", zpe.config)) - zpe.server, err = zpe.config.ToServer(ctx, host, zpe.telemetry, zPagesMux) + zpe.server, err = zpe.config.ToServer(ctx, zpe.telemetry, zPagesMux, confighttp.WithServerExtensions(host.GetExtensions())) if err != nil { return err } diff --git a/receiver/otlpreceiver/otlp.go b/receiver/otlpreceiver/otlp.go index 907e6a0c561..b1a615376b9 100644 --- a/receiver/otlpreceiver/otlp.go +++ b/receiver/otlpreceiver/otlp.go @@ -15,6 +15,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/xconsumer" @@ -93,7 +94,7 @@ func (r *otlpReceiver) startGRPCServer(ctx context.Context, host component.Host) grpcCfg := r.cfg.GRPC.Get() var err error - if r.serverGRPC, err = grpcCfg.ToServer(ctx, host, r.settings.TelemetrySettings); err != nil { + if r.serverGRPC, err = grpcCfg.ToServer(ctx, r.settings.TelemetrySettings, configgrpc.WithServerExtensions(host.GetExtensions())); err != nil { return err } @@ -167,7 +168,7 @@ func (r *otlpReceiver) startHTTPServer(ctx context.Context, host component.Host) } var err error - if r.serverHTTP, err = httpCfg.ServerConfig.ToServer(ctx, host, r.settings.TelemetrySettings, httpMux, confighttp.WithErrorHandler(errorHandler)); err != nil { + if r.serverHTTP, err = httpCfg.ServerConfig.ToServer(ctx, r.settings.TelemetrySettings, httpMux, confighttp.WithErrorHandler(errorHandler), confighttp.WithServerExtensions(host.GetExtensions())); err != nil { return err }