Skip to content

Commit

Permalink
Make client auth extension public and implement component.Component (#…
Browse files Browse the repository at this point in the history
…814)

* Make client auth extension CreateExtension()/CreateDefaultConfig() public

* Implement component.Component

* make gotidy

* Add nil checks and tests

* Fix tests
  • Loading branch information
damemi committed Mar 18, 2024
1 parent 5476951 commit 2be0414
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 29 deletions.
2 changes: 1 addition & 1 deletion extension/googleclientauthextension/config.go
Expand Up @@ -54,7 +54,7 @@ var defaultScopes = []string{
"https://www.googleapis.com/auth/trace.append",
}

func createDefaultConfig() component.Config {
func CreateDefaultConfig() component.Config {
return &Config{
Scopes: defaultScopes,
}
Expand Down
43 changes: 23 additions & 20 deletions extension/googleclientauthextension/factory.go
Expand Up @@ -21,7 +21,6 @@ import (

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension"
"go.opentelemetry.io/collector/extension/auth"
"golang.org/x/oauth2/google"
"google.golang.org/grpc/credentials/oauth"

Expand All @@ -32,43 +31,47 @@ import (
func NewFactory() extension.Factory {
return extension.NewFactory(
metadata.Type,
createDefaultConfig,
createExtension,
CreateDefaultConfig,
CreateExtension,
metadata.ExtensionStability,
)
}

func createExtension(ctx context.Context, set extension.CreateSettings, cfg component.Config) (extension.Extension, error) {
func CreateExtension(ctx context.Context, set extension.CreateSettings, cfg component.Config) (extension.Extension, error) {
config := cfg.(*Config)
ca := &clientAuthenticator{
config: config,
}
return ca, nil
}

// clientAuthenticator supplies credentials from an oauth.TokenSource.
type clientAuthenticator struct {
*oauth.TokenSource
config *Config
}

func (ca *clientAuthenticator) Start(ctx context.Context, _ component.Host) error {
config := ca.config
creds, err := google.FindDefaultCredentials(ctx, config.Scopes...)
if err != nil {
return nil, err
return err
}
if config.Project == "" {
config.Project = creds.ProjectID
}
if config.Project == "" {
return nil, errors.New("no project set in config or found with application default credentials")
return errors.New("no project set in config or found with application default credentials")
}
if config.QuotaProject == "" {
config.QuotaProject = quotaProjectFromCreds(creds)
}

ca := clientAuthenticator{
TokenSource: oauth.TokenSource{TokenSource: creds.TokenSource},
config: config,
}

return auth.NewClient(
auth.WithClientRoundTripper(ca.roundTripper),
auth.WithClientPerRPCCredentials(ca.perRPCCredentials),
), nil
ca.TokenSource = &oauth.TokenSource{TokenSource: creds.TokenSource}
return nil
}

// clientAuthenticator supplies credentials from an oauth.TokenSource.
type clientAuthenticator struct {
oauth.TokenSource
config *Config
func (ca *clientAuthenticator) Shutdown(ctx context.Context) error {
return nil
}

// quotaProjectFromCreds retrieves quota project from the credentials file.
Expand Down
20 changes: 19 additions & 1 deletion extension/googleclientauthextension/factory_test.go
Expand Up @@ -37,7 +37,25 @@ func TestNewFactory(t *testing.T) {

func TestCreateExtension(t *testing.T) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json")
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, createDefaultConfig())
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, CreateDefaultConfig())
assert.NotNil(t, ext)
assert.NoError(t, err)
}

func TestStart(t *testing.T) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json")
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, CreateDefaultConfig())
assert.NotNil(t, ext)
assert.NoError(t, err)
err = ext.Start(context.Background(), nil)
assert.NoError(t, err)
}

func TestStart_WithError(t *testing.T) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/foo.json")
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, CreateDefaultConfig())
assert.NotNil(t, ext)
assert.NoError(t, err)
err = ext.Start(context.Background(), nil)
assert.Error(t, err)
}
1 change: 0 additions & 1 deletion extension/googleclientauthextension/go.mod
Expand Up @@ -7,7 +7,6 @@ require (
go.opentelemetry.io/collector/component v0.94.0
go.opentelemetry.io/collector/confmap v0.94.0
go.opentelemetry.io/collector/extension v0.94.0
go.opentelemetry.io/collector/extension/auth v0.94.0
golang.org/x/oauth2 v0.16.0
google.golang.org/grpc v1.61.0
)
Expand Down
2 changes: 0 additions & 2 deletions extension/googleclientauthextension/go.sum
Expand Up @@ -63,8 +63,6 @@ go.opentelemetry.io/collector/confmap v0.94.0 h1:yjzdGPHCeae7VooJkgeOWO2Y3XFKwOI
go.opentelemetry.io/collector/confmap v0.94.0/go.mod h1:pCT5UtcHaHVJ5BIILv1Z2VQyjZzmT9uTdBmC9+Z0AgA=
go.opentelemetry.io/collector/extension v0.94.0 h1:8XspN+A1aQhN3vQb2reE4kYF7qSkKULKF++TE4VEA9o=
go.opentelemetry.io/collector/extension v0.94.0/go.mod h1:G0+tNTYZf0D0d42tWKv8rILMi4YkalPw2qtXTn80SOU=
go.opentelemetry.io/collector/extension/auth v0.94.0 h1:TJxKM9dArGnaz6DYO3ASYYBU/DIHfzr622BPtCY/Z04=
go.opentelemetry.io/collector/extension/auth v0.94.0/go.mod h1:kL5FQQtUAUTs8ngP51ft1Guzn2czFZloG9lBANXekNM=
go.opentelemetry.io/collector/pdata v1.1.0 h1:cE6Al1rQieUjMHro6p6cKwcu3sjHXGG59BZ3kRVUvsM=
go.opentelemetry.io/collector/pdata v1.1.0/go.mod h1:IDkDj+B4Fp4wWOclBELN97zcb98HugJ8Q2gA4ZFsN8Q=
go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
Expand Down
10 changes: 9 additions & 1 deletion extension/googleclientauthextension/grpc.go
Expand Up @@ -16,20 +16,28 @@ package googleclientauthextension // import "github.com/GoogleCloudPlatform/open

import (
"context"
"errors"

"google.golang.org/grpc/credentials"
)

// perRPCCredentials returns gRPC credentials using the OAuth TokenSource, and adds
// google metadata.
func (ca clientAuthenticator) perRPCCredentials() (credentials.PerRPCCredentials, error) {
func (ca clientAuthenticator) PerRPCCredentials() (credentials.PerRPCCredentials, error) {
if ca.TokenSource == nil {
return nil, errors.New("not started")
}
return ca, nil
}

// GetRequestMetadata gets the request metadata as a map from a clientAuthenticator.
// Based on metadata added by the google go client:
// https://github.com/googleapis/google-api-go-client/blob/113082d14d54f188d1b6c34c652e416592fc51b5/transport/grpc/dial.go#L224
func (ca clientAuthenticator) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
if ca.TokenSource == nil {
return nil, errors.New("not started")
}

metadata, err := ca.TokenSource.GetRequestMetadata(ctx, uri...)
if err != nil {
return nil, err
Expand Down
46 changes: 46 additions & 0 deletions extension/googleclientauthextension/grpc_test.go
@@ -0,0 +1,46 @@
// Copyright 2024 Google LLC
//
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
)

func TestPerRPCCredentials(t *testing.T) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json")
ca := clientAuthenticator{config: &Config{
Project: "my-project",
QuotaProject: "other-project",
}}
err := ca.Start(context.Background(), nil)
assert.NoError(t, err)

perrpc, err := ca.PerRPCCredentials()
assert.NotNil(t, perrpc)
assert.NoError(t, err)
}

func TestPerRPCCredentialsNotStarted(t *testing.T) {
ca := clientAuthenticator{config: &Config{
Project: "my-project",
QuotaProject: "other-project",
}}
perrpc, err := ca.PerRPCCredentials()
assert.Nil(t, perrpc)
assert.Error(t, err)
}
5 changes: 4 additions & 1 deletion extension/googleclientauthextension/http.go
Expand Up @@ -23,7 +23,10 @@ import (

// roundTripper provides an HTTP RoundTripper which adds gcp credentials and
// headers.
func (ca *clientAuthenticator) roundTripper(base http.RoundTripper) (http.RoundTripper, error) {
func (ca *clientAuthenticator) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) {
if ca.TokenSource == nil {
return nil, errors.New("not started")
}
return &oauth2.Transport{
Source: ca,
Base: &parameterTransport{
Expand Down
26 changes: 24 additions & 2 deletions extension/googleclientauthextension/http_test.go
Expand Up @@ -15,21 +15,43 @@
package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"context"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

func TestRoundTripper(t *testing.T) {
ca := clientAuthenticator{}
rt, err := ca.roundTripper(roundTripperFunc(func(r *http.Request) (*http.Response, error) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json")
ca := clientAuthenticator{config: &Config{
Project: "my-project",
QuotaProject: "other-project",
},
}
err := ca.Start(context.Background(), nil)
assert.NoError(t, err)

rt, err := ca.RoundTripper(roundTripperFunc(func(r *http.Request) (*http.Response, error) {
return nil, nil
}))
assert.NotNil(t, rt)
assert.NoError(t, err)
}

func TestRoundTripperNotStarted(t *testing.T) {
ca := clientAuthenticator{config: &Config{
Project: "my-project",
QuotaProject: "other-project",
}}

rt, err := ca.RoundTripper(roundTripperFunc(func(r *http.Request) (*http.Response, error) {
return nil, nil
}))
assert.Nil(t, rt)
assert.Error(t, err)
}

func TestRoundTrip(t *testing.T) {
tr := parameterTransport{
config: &Config{
Expand Down

0 comments on commit 2be0414

Please sign in to comment.