-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for Custom Exporter Authenticators as Extensions (#3128)
This PR adds support to add client side (exporter) authenticators for HTTP and gRPC clients through extension based authenticators. This is built of top of what was added for receiver (server) side authenticators via extensions in #2603 **Link to tracking Issue:** #3115 **Testing:** - Did a manual testing for static bearer token. - Added unit tests
- Loading branch information
1 parent
db093a6
commit af71182
Showing
49 changed files
with
1,315 additions
and
338 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// 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 ( | ||
"fmt" | ||
"net/http" | ||
|
||
"google.golang.org/grpc/credentials" | ||
|
||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/config" | ||
) | ||
|
||
// ClientAuthenticator is an Extension that can be used as an authenticator for the configauth.Authentication option. | ||
// Authenticators are then included as part of OpenTelemetry Collector builds and can be referenced by their | ||
// names from the Authentication configuration. | ||
type ClientAuthenticator interface { | ||
component.Extension | ||
} | ||
|
||
// HTTPClientAuthenticator is a ClientAuthenticator that can be used as an authenticator | ||
// for the configauth.Authentication option for HTTP clients. | ||
type HTTPClientAuthenticator interface { | ||
ClientAuthenticator | ||
RoundTripper(base http.RoundTripper) (http.RoundTripper, error) | ||
} | ||
|
||
// GRPCClientAuthenticator is a ClientAuthenticator that can be used as an authenticator for | ||
// the configauth.Authentication option for gRPC clients. | ||
type GRPCClientAuthenticator interface { | ||
ClientAuthenticator | ||
PerRPCCredentials() (credentials.PerRPCCredentials, error) | ||
} | ||
|
||
// GetHTTPClientAuthenticator attempts to select the appropriate HTTPClientAuthenticator from the list of extensions, | ||
// based on the component id of the extension. If an authenticator is not found, an error is returned. | ||
// This should be only used by HTTP clients. | ||
func GetHTTPClientAuthenticator(extensions map[config.ComponentID]component.Extension, | ||
componentID config.ComponentID) (HTTPClientAuthenticator, error) { | ||
for name, ext := range extensions { | ||
if name == componentID { | ||
if auth, ok := ext.(HTTPClientAuthenticator); ok { | ||
return auth, nil | ||
} | ||
return nil, fmt.Errorf("requested authenticator is not for HTTP clients") | ||
} | ||
} | ||
return nil, fmt.Errorf("failed to resolve authenticator %q: %w", componentID.String(), errAuthenticatorNotFound) | ||
} | ||
|
||
// GetGRPCClientAuthenticator attempts to select the appropriate GRPCClientAuthenticator from the list of extensions, | ||
// based on the component id of the extension. If an authenticator is not found, an error is returned. | ||
// This should only be used by gRPC clients. | ||
func GetGRPCClientAuthenticator(extensions map[config.ComponentID]component.Extension, | ||
componentID config.ComponentID) (GRPCClientAuthenticator, error) { | ||
for name, ext := range extensions { | ||
if name == componentID { | ||
if auth, ok := ext.(GRPCClientAuthenticator); ok { | ||
return auth, nil | ||
} | ||
return nil, fmt.Errorf("requested authenticator is not for gRPC clients") | ||
} | ||
} | ||
return nil, fmt.Errorf("failed to resolve authenticator %q: %w", componentID.String(), errAuthenticatorNotFound) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// 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" | ||
"errors" | ||
"net/http" | ||
|
||
"google.golang.org/grpc/credentials" | ||
|
||
"go.opentelemetry.io/collector/component" | ||
) | ||
|
||
var ( | ||
_ HTTPClientAuthenticator = (*MockClientAuthenticator)(nil) | ||
_ GRPCClientAuthenticator = (*MockClientAuthenticator)(nil) | ||
errMockError = errors.New("mock Error") | ||
) | ||
|
||
// MockClientAuthenticator provides a mock implementation of GRPCClientAuthenticator and HTTPClientAuthenticator interfaces | ||
type MockClientAuthenticator struct { | ||
ResultRoundTripper http.RoundTripper | ||
ResultPerRPCCredentials credentials.PerRPCCredentials | ||
MustError bool | ||
} | ||
|
||
// Start for the MockClientAuthenticator does nothing | ||
func (m *MockClientAuthenticator) Start(ctx context.Context, host component.Host) error { | ||
return nil | ||
} | ||
|
||
// Shutdown for the MockClientAuthenticator does nothing | ||
func (m *MockClientAuthenticator) Shutdown(ctx context.Context) error { | ||
return nil | ||
} | ||
|
||
// RoundTripper for the MockClientAuthenticator either returns error if the mock authenticator is forced to or | ||
// returns the supplied resultRoundTripper. | ||
func (m *MockClientAuthenticator) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { | ||
if m.MustError { | ||
return nil, errMockError | ||
} | ||
return m.ResultRoundTripper, nil | ||
} | ||
|
||
// PerRPCCredentials for the MockClientAuthenticator either returns error if the mock authenticator is forced to or | ||
// returns the supplied resultPerRPCCredentials. | ||
func (m *MockClientAuthenticator) PerRPCCredentials() (credentials.PerRPCCredentials, error) { | ||
if m.MustError { | ||
return nil, errMockError | ||
} | ||
return m.ResultPerRPCCredentials, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// 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" | ||
) | ||
|
||
func TestNilStartAndShutdown(t *testing.T) { | ||
// prepare | ||
m := &MockClientAuthenticator{} | ||
|
||
// test and verify | ||
origCtx := context.Background() | ||
|
||
err := m.Start(origCtx, nil) | ||
assert.NoError(t, err) | ||
|
||
err = m.Shutdown(origCtx) | ||
assert.NoError(t, err) | ||
} | ||
|
||
type customRoundTripper struct{} | ||
|
||
func (c *customRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { | ||
return nil, nil | ||
} | ||
|
||
func TestMockRoundTripper(t *testing.T) { | ||
testcases := []struct { | ||
name string | ||
expectedErr bool | ||
clientAuth MockClientAuthenticator | ||
}{ | ||
{ | ||
name: "no_error", | ||
expectedErr: false, | ||
clientAuth: MockClientAuthenticator{ | ||
ResultRoundTripper: &customRoundTripper{}, | ||
MustError: false, | ||
}, | ||
}, | ||
{ | ||
name: "error", | ||
expectedErr: true, | ||
clientAuth: MockClientAuthenticator{ | ||
ResultRoundTripper: &customRoundTripper{}, | ||
MustError: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, testcase := range testcases { | ||
t.Run(testcase.name, func(t *testing.T) { | ||
tripper, err := testcase.clientAuth.RoundTripper(nil) | ||
if testcase.expectedErr { | ||
assert.Error(t, err) | ||
return | ||
} | ||
assert.NotNil(t, tripper) | ||
assert.NoError(t, err) | ||
// check if the resultant tripper is indeed the one provided | ||
_, ok := tripper.(*customRoundTripper) | ||
assert.True(t, ok) | ||
}) | ||
} | ||
} | ||
|
||
type customPerRPCCredentials struct{} | ||
|
||
var _ credentials.PerRPCCredentials = (*customPerRPCCredentials)(nil) | ||
|
||
func (c *customPerRPCCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { | ||
return nil, nil | ||
} | ||
|
||
func (c *customPerRPCCredentials) RequireTransportSecurity() bool { | ||
return true | ||
} | ||
|
||
func TestMockPerRPCCredential(t *testing.T) { | ||
testcases := []struct { | ||
name string | ||
expectedErr bool | ||
clientAuth MockClientAuthenticator | ||
}{ | ||
{ | ||
name: "no_error", | ||
expectedErr: false, | ||
clientAuth: MockClientAuthenticator{ | ||
ResultPerRPCCredentials: &customPerRPCCredentials{}, | ||
MustError: false, | ||
}, | ||
}, | ||
{ | ||
name: "error", | ||
expectedErr: true, | ||
clientAuth: MockClientAuthenticator{ | ||
ResultPerRPCCredentials: &customPerRPCCredentials{}, | ||
MustError: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, testcase := range testcases { | ||
t.Run(testcase.name, func(t *testing.T) { | ||
credential, err := testcase.clientAuth.PerRPCCredentials() | ||
if err != nil { | ||
return | ||
} | ||
if testcase.expectedErr { | ||
assert.Error(t, err) | ||
return | ||
} | ||
assert.NotNil(t, credential) | ||
assert.NoError(t, err) | ||
// check if the resultant tripper is indeed the one provided | ||
_, ok := credential.(*customPerRPCCredentials) | ||
assert.True(t, ok) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.