diff --git a/extension/googleclientauthextension/config.go b/extension/googleclientauthextension/config.go index adaf2d1f..63faf031 100644 --- a/extension/googleclientauthextension/config.go +++ b/extension/googleclientauthextension/config.go @@ -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, } diff --git a/extension/googleclientauthextension/factory.go b/extension/googleclientauthextension/factory.go index 25b5b5c4..773ed135 100644 --- a/extension/googleclientauthextension/factory.go +++ b/extension/googleclientauthextension/factory.go @@ -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" @@ -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. diff --git a/extension/googleclientauthextension/factory_test.go b/extension/googleclientauthextension/factory_test.go index b4708a39..f4926897 100644 --- a/extension/googleclientauthextension/factory_test.go +++ b/extension/googleclientauthextension/factory_test.go @@ -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) +} diff --git a/extension/googleclientauthextension/go.mod b/extension/googleclientauthextension/go.mod index 02622d11..238b511f 100644 --- a/extension/googleclientauthextension/go.mod +++ b/extension/googleclientauthextension/go.mod @@ -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 ) diff --git a/extension/googleclientauthextension/go.sum b/extension/googleclientauthextension/go.sum index f180c865..56797f41 100644 --- a/extension/googleclientauthextension/go.sum +++ b/extension/googleclientauthextension/go.sum @@ -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= diff --git a/extension/googleclientauthextension/grpc.go b/extension/googleclientauthextension/grpc.go index 73778920..13cf6fd3 100644 --- a/extension/googleclientauthextension/grpc.go +++ b/extension/googleclientauthextension/grpc.go @@ -16,13 +16,17 @@ 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 } @@ -30,6 +34,10 @@ func (ca clientAuthenticator) perRPCCredentials() (credentials.PerRPCCredentials // 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 diff --git a/extension/googleclientauthextension/grpc_test.go b/extension/googleclientauthextension/grpc_test.go new file mode 100644 index 00000000..6e9bfecc --- /dev/null +++ b/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) +} diff --git a/extension/googleclientauthextension/http.go b/extension/googleclientauthextension/http.go index b77d050c..b7120799 100644 --- a/extension/googleclientauthextension/http.go +++ b/extension/googleclientauthextension/http.go @@ -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: ¶meterTransport{ diff --git a/extension/googleclientauthextension/http_test.go b/extension/googleclientauthextension/http_test.go index 4fa17b55..2a6f1971 100644 --- a/extension/googleclientauthextension/http_test.go +++ b/extension/googleclientauthextension/http_test.go @@ -15,6 +15,7 @@ package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" import ( + "context" "net/http" "testing" @@ -22,14 +23,35 @@ import ( ) 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{