diff --git a/exporter/chronicleexporter/grpc_exporter.go b/exporter/chronicleexporter/grpc_exporter.go index 5b8150b81..17c127db0 100644 --- a/exporter/chronicleexporter/grpc_exporter.go +++ b/exporter/chronicleexporter/grpc_exporter.go @@ -26,6 +26,7 @@ import ( "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/pdata/plog" + "golang.org/x/oauth2" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -69,7 +70,12 @@ func (exp *grpcExporter) Capabilities() consumer.Capabilities { } func (exp *grpcExporter) Start(ctx context.Context, _ component.Host) error { - conn, err := getGRPCClient(ctx, exp.cfg) + ts, err := tokenSource(ctx, exp.cfg) + if err != nil { + return fmt.Errorf("load Google credentials: %w", err) + } + endpoint, dialOpts := grpcClientParams(exp.cfg.Endpoint, ts) + conn, err := grpc.NewClient(endpoint, dialOpts...) if err != nil { return fmt.Errorf("dial: %w", err) } @@ -145,15 +151,10 @@ func (exp *grpcExporter) buildOptions() []grpc.CallOption { return opts } -var getGRPCClient func(context.Context, *Config) (*grpc.ClientConn, error) = buildGRPCClient - -func buildGRPCClient(ctx context.Context, cfg *Config) (*grpc.ClientConn, error) { - creds, err := googleCredentials(ctx, cfg) - if err != nil { - return nil, fmt.Errorf("load Google credentials: %w", err) - } - return grpc.NewClient(cfg.Endpoint+":443", - grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: creds.TokenSource}), +// Override for testing +var grpcClientParams = func(cfgEndpoint string, ts oauth2.TokenSource) (string, []grpc.DialOption) { + return cfgEndpoint + ":443", []grpc.DialOption{ + grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: ts}), grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), - ) + } } diff --git a/exporter/chronicleexporter/grpc_exporter_test.go b/exporter/chronicleexporter/grpc_exporter_test.go index 4ffb6a547..f95e89ade 100644 --- a/exporter/chronicleexporter/grpc_exporter_test.go +++ b/exporter/chronicleexporter/grpc_exporter_test.go @@ -25,6 +25,7 @@ import ( "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/pdata/plog" + "golang.org/x/oauth2" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" @@ -66,6 +67,15 @@ func (s *mockGRPCServer) BatchCreateLogs(_ context.Context, req *api.BatchCreate } func TestGRPCExporter(t *testing.T) { + // Override the token source so that we don't have to provide real credentials + secureTokenSource := tokenSource + defer func() { + tokenSource = secureTokenSource + }() + tokenSource = func(context.Context, *Config) (oauth2.TokenSource, error) { + return &emptyTokenSource{}, nil + } + // By default, tests will apply the following changes to NewFactory.CreateDefaultConfig() defaultCfgMod := func(cfg *Config) { cfg.Protocol = protocolGRPC @@ -145,13 +155,14 @@ func TestGRPCExporter(t *testing.T) { mockServer, endpoint := newMockGRPCServer(t, tc.handler) defer mockServer.srv.GracefulStop() - // Override the client builder so we can use the mock server - getGRPCClient = func(_ context.Context, _ *Config) (*grpc.ClientConn, error) { - return grpc.NewClient(endpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) - } + // Override the client params for testing to we can connect to the mock server + secureGPPCClientParams := grpcClientParams defer func() { - getGRPCClient = buildGRPCClient + grpcClientParams = secureGPPCClientParams }() + grpcClientParams = func(string, oauth2.TokenSource) (string, []grpc.DialOption) { + return endpoint, []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + } f := NewFactory() cfg := f.CreateDefaultConfig().(*Config) diff --git a/exporter/chronicleexporter/http_exporter.go b/exporter/chronicleexporter/http_exporter.go index 7513ea345..e0d1e62c5 100644 --- a/exporter/chronicleexporter/http_exporter.go +++ b/exporter/chronicleexporter/http_exporter.go @@ -66,7 +66,7 @@ func (exp *httpExporter) Capabilities() consumer.Capabilities { } func (exp *httpExporter) Start(ctx context.Context, _ component.Host) error { - ts, err := getTokenSource(ctx, exp.cfg) + ts, err := tokenSource(ctx, exp.cfg) if err != nil { return fmt.Errorf("load Google credentials: %w", err) } @@ -119,7 +119,7 @@ func (exp *httpExporter) uploadToChronicleHTTP(ctx context.Context, logs *api.Im body = bytes.NewBuffer(data) } - request, err := http.NewRequestWithContext(ctx, "POST", getHTTPEndpoint(exp.cfg, logType), body) + request, err := http.NewRequestWithContext(ctx, "POST", httpEndpoint(exp.cfg, logType), body) if err != nil { return fmt.Errorf("create request: %w", err) } @@ -157,22 +157,10 @@ func (exp *httpExporter) uploadToChronicleHTTP(ctx context.Context, logs *api.Im } } -// Override for testing -var getHTTPEndpoint = buildHTTPEndpoint - // This uses the DataPlane URL for the request // URL for the request: https://{region}-chronicle.googleapis.com/{version}/projects/{project}/location/{region}/instances/{customerID} -func buildHTTPEndpoint(cfg *Config, logType string) string { +// Override for testing +var httpEndpoint = func(cfg *Config, logType string) string { formatString := "https://%s-%s/v1alpha/projects/%s/locations/%s/instances/%s/logTypes/%s/logs:import" return fmt.Sprintf(formatString, cfg.Location, cfg.Endpoint, cfg.Project, cfg.Location, cfg.CustomerID, logType) } - -var getTokenSource func(context.Context, *Config) (oauth2.TokenSource, error) = googleTokenSource - -func googleTokenSource(ctx context.Context, cfg *Config) (oauth2.TokenSource, error) { - creds, err := googleCredentials(ctx, cfg) - if err != nil { - return nil, err - } - return creds.TokenSource, nil -} diff --git a/exporter/chronicleexporter/http_exporter_test.go b/exporter/chronicleexporter/http_exporter_test.go index f98652bd8..491c10d8e 100644 --- a/exporter/chronicleexporter/http_exporter_test.go +++ b/exporter/chronicleexporter/http_exporter_test.go @@ -26,6 +26,7 @@ import ( "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/pdata/plog" + "golang.org/x/oauth2" ) type mockHTTPServer struct { @@ -47,12 +48,21 @@ func newMockHTTPServer(logTypeHandlers map[string]http.HandlerFunc) *mockHTTPSer return &mockServer } +type emptyTokenSource struct{} + +func (t *emptyTokenSource) Token() (*oauth2.Token, error) { + return &oauth2.Token{}, nil +} + func TestHTTPExporter(t *testing.T) { // Override the token source so that we don't have to provide real credentials - getTokenSource = testTokenSource + secureTokenSource := tokenSource defer func() { - getTokenSource = googleTokenSource + tokenSource = secureTokenSource }() + tokenSource = func(context.Context, *Config) (oauth2.TokenSource, error) { + return &emptyTokenSource{}, nil + } // By default, tests will apply the following changes to NewFactory.CreateDefaultConfig() defaultCfgMod := func(cfg *Config) { @@ -150,12 +160,13 @@ func TestHTTPExporter(t *testing.T) { defer mockServer.srv.Close() // Override the endpoint builder so that we can point to the mock server - getHTTPEndpoint = func(_ *Config, logType string) string { - return fmt.Sprintf("%s/logTypes/%s/logs:import", mockServer.srv.URL, logType) - } + secureHTTPEndpoint := httpEndpoint defer func() { - getHTTPEndpoint = buildHTTPEndpoint + httpEndpoint = secureHTTPEndpoint }() + httpEndpoint = func(_ *Config, logType string) string { + return fmt.Sprintf("%s/logTypes/%s/logs:import", mockServer.srv.URL, logType) + } f := NewFactory() cfg := f.CreateDefaultConfig().(*Config) diff --git a/exporter/chronicleexporter/util.go b/exporter/chronicleexporter/util.go index 02c212d60..21ef88e3d 100644 --- a/exporter/chronicleexporter/util.go +++ b/exporter/chronicleexporter/util.go @@ -20,9 +20,19 @@ import ( "fmt" "os" + "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) +// Override for testing +var tokenSource = func(ctx context.Context, cfg *Config) (oauth2.TokenSource, error) { + creds, err := googleCredentials(ctx, cfg) + if err != nil { + return nil, err + } + return creds.TokenSource, nil +} + func googleCredentials(ctx context.Context, cfg *Config) (*google.Credentials, error) { scope := grpcScope if cfg.Protocol == protocolHTTPS { diff --git a/exporter/chronicleexporter/util_test.go b/exporter/chronicleexporter/util_test.go deleted file mode 100644 index 366efe0a6..000000000 --- a/exporter/chronicleexporter/util_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright observIQ, Inc. -// -// 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 chronicleexporter - -import ( - "context" - - "golang.org/x/oauth2" -) - -func testTokenSource(_ context.Context, _ *Config) (oauth2.TokenSource, error) { - return &emptyTokenSource{}, nil -} - -type emptyTokenSource struct{} - -func (t *emptyTokenSource) Token() (*oauth2.Token, error) { - return &oauth2.Token{}, nil -}