diff --git a/CHANGELOG.md b/CHANGELOG.md index e4398ccf14a..ae43cc0b074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ ### 💡 Enhancements 💡 - Add validation to check at least one endpoint is specified in otlphttpexporter's configuration (#4860) +- Implement default client authenticators (#4837) ## 🧰 Bug fixes 🧰 diff --git a/config/configauth/default_clientauthenticator.go b/config/configauth/default_clientauthenticator.go new file mode 100644 index 00000000000..c10f3398cc5 --- /dev/null +++ b/config/configauth/default_clientauthenticator.go @@ -0,0 +1,104 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package configauth // import "go.opentelemetry.io/collector/config/configauth" + +import ( + "context" + "net/http" + + "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/collector/component" +) + +var _ ClientAuthenticator = (*defaultClientAuthenticator)(nil) + +// Option represents the possible options for NewServerAuthenticator. +type ClientOption func(*defaultClientAuthenticator) + +type defaultClientAuthenticator struct { + component.StartFunc + component.ShutdownFunc + roundTripperFunc func(base http.RoundTripper) (http.RoundTripper, error) + perRPCCredentialsFunc func() (credentials.PerRPCCredentials, error) +} + +// WithClientStart overrides the default `Start` function for a component.Component. +// The default always returns nil. +func WithClientStart(startFunc component.StartFunc) ClientOption { + return func(o *defaultClientAuthenticator) { + o.StartFunc = startFunc + } +} + +// WithClientShutdown overrides the default `Shutdown` function for a component.Component. +// The default always returns nil. +func WithClientShutdown(shutdownFunc component.ShutdownFunc) ClientOption { + return func(o *defaultClientAuthenticator) { + o.ShutdownFunc = shutdownFunc + } +} + +// WithClientRoundTripper provides a `RoundTripper` function for this client authenticator. +// The default round tripper is no-op. +func WithClientRoundTripper(roundTripperFunc func(base http.RoundTripper) (http.RoundTripper, error)) ClientOption { + return func(o *defaultClientAuthenticator) { + o.roundTripperFunc = roundTripperFunc + } +} + +// WithPerRPCCredentials provides a `PerRPCCredentials` function for this client authenticator. +// There's no default. +func WithPerRPCCredentials(perRPCCredentialsFunc func() (credentials.PerRPCCredentials, error)) ClientOption { + return func(o *defaultClientAuthenticator) { + o.perRPCCredentialsFunc = perRPCCredentialsFunc + } +} + +// NewClientAuthenticator returns a ClientAuthenticator configured with the provided options. +func NewClientAuthenticator(options ...ClientOption) ClientAuthenticator { + bc := &defaultClientAuthenticator{ + StartFunc: func(ctx context.Context, host component.Host) error { return nil }, + ShutdownFunc: func(ctx context.Context) error { return nil }, + roundTripperFunc: func(base http.RoundTripper) (http.RoundTripper, error) { return base, nil }, + perRPCCredentialsFunc: func() (credentials.PerRPCCredentials, error) { return nil, nil }, + } + + for _, op := range options { + op(bc) + } + + return bc +} + +// Start the component. +func (a *defaultClientAuthenticator) Start(ctx context.Context, host component.Host) error { + return a.StartFunc(ctx, host) +} + +// Shutdown stops the component. +func (a *defaultClientAuthenticator) Shutdown(ctx context.Context) error { + return a.ShutdownFunc(ctx) +} + +// RoundTripper adds the base HTTP RoundTripper in this authenticator's round tripper. +func (a *defaultClientAuthenticator) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { + return a.roundTripperFunc(base) +} + +// PerRPCCredentials returns this authenticator's credentials.PerRPCCredentials implementation. +func (a *defaultClientAuthenticator) PerRPCCredentials() (credentials.PerRPCCredentials, error) { + return a.perRPCCredentialsFunc() +} diff --git a/config/configauth/default_clientauthenticator_test.go b/config/configauth/default_clientauthenticator_test.go new file mode 100644 index 00000000000..22d9e008a53 --- /dev/null +++ b/config/configauth/default_clientauthenticator_test.go @@ -0,0 +1,118 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package configauth + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" +) + +func TestClientDefaultValues(t *testing.T) { + // prepare + e := NewClientAuthenticator() + + // test + t.Run("start", func(t *testing.T) { + err := e.Start(context.Background(), componenttest.NewNopHost()) + assert.NoError(t, err) + }) + + t.Run("roundtripper", func(t *testing.T) { + ctx, err := e.RoundTripper(http.DefaultTransport) + assert.NotNil(t, ctx) + assert.NoError(t, err) + }) + + t.Run("per-rpc-credentials", func(t *testing.T) { + p, err := e.PerRPCCredentials() + assert.Nil(t, p) + assert.NoError(t, err) + }) + + t.Run("shutdown", func(t *testing.T) { + err := e.Shutdown(context.Background()) + assert.NoError(t, err) + }) +} + +func TestWithClientStart(t *testing.T) { + called := false + e := NewClientAuthenticator(WithClientStart(func(c context.Context, h component.Host) error { + called = true + return nil + })) + + // test + err := e.Start(context.Background(), componenttest.NewNopHost()) + + // verify + assert.True(t, called) + assert.NoError(t, err) +} + +func TestWithClientShutdown(t *testing.T) { + called := false + e := NewClientAuthenticator(WithClientShutdown(func(c context.Context) error { + called = true + return nil + })) + + // test + err := e.Shutdown(context.Background()) + + // verify + assert.True(t, called) + assert.NoError(t, err) +} + +func TestWithClientRoundTripper(t *testing.T) { + called := false + e := NewClientAuthenticator(WithClientRoundTripper(func(base http.RoundTripper) (http.RoundTripper, error) { + called = true + return base, nil + })) + + // test + rt, err := e.RoundTripper(http.DefaultTransport) + + // verify + assert.True(t, called) + assert.NotNil(t, rt) + assert.NoError(t, err) +} + +func TestWithPerRPCCredentials(t *testing.T) { + called := false + e := NewClientAuthenticator(WithPerRPCCredentials(func() (credentials.PerRPCCredentials, error) { + called = true + return &customPerRPCCredentials{}, nil + })) + + // test + p, err := e.PerRPCCredentials() + + // verify + assert.True(t, called) + assert.NotNil(t, p) + assert.NoError(t, err) + +}