Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,21 @@ enum ConfigurableOption {
* configured using the environment variable `GOOGLE_OTEL_AUTH_TARGET_SIGNALS` or the system
* property `google.otel.auth.target.signals`.
*/
GOOGLE_OTEL_AUTH_TARGET_SIGNALS("Target Signals for Google Authentication Extension");
GOOGLE_OTEL_AUTH_TARGET_SIGNALS("Target Signals for Google Authentication Extension"),

/**
* Represents the Google Cloud Credentials Path option. Can be configured using the environment
* variable `GOOGLE_CLOUD_CREDENTIALS_PATH` or the system property
* `google.cloud.credentials.path`.
*/
GOOGLE_CLOUD_CREDENTIALS_PATH("Google Cloud Credentials Path"),

/**
* Represents the Google Cloud Credentials JSON option. Can be configured using the environment
* variable `GOOGLE_CLOUD_CREDENTIALS_JSON` or the system property
* `google.cloud.credentials.json`.
*/
GOOGLE_CLOUD_CREDENTIALS_JSON("Google Cloud Credentials JSON String");

private final String userReadableName;
private final String environmentVariableName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
Expand All @@ -57,6 +63,8 @@ public class GcpAuthAutoConfigurationCustomizerProvider

private static final Logger logger =
Logger.getLogger(GcpAuthAutoConfigurationCustomizerProvider.class.getName());
private static final Map<ConfigProperties, GoogleCredentials> credentialsCache =
Collections.synchronizedMap(new WeakHashMap<>());
private static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION =
String.format(
"You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by exporting valid values to environment variable: %s or by setting valid values in system property: %s.",
Expand Down Expand Up @@ -99,22 +107,62 @@ public class GcpAuthAutoConfigurationCustomizerProvider
*/
@Override
public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
GoogleCredentials credentials;
try {
credentials = GoogleCredentials.getApplicationDefault();
} catch (IOException e) {
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
}
autoConfiguration
.addSpanExporterCustomizer(
(spanExporter, configProperties) ->
customizeSpanExporter(spanExporter, credentials, configProperties))
customizeSpanExporter(
spanExporter, resolveCredentials(configProperties), configProperties))
.addMetricExporterCustomizer(
(metricExporter, configProperties) ->
customizeMetricExporter(metricExporter, credentials, configProperties))
customizeMetricExporter(
metricExporter, resolveCredentials(configProperties), configProperties))
.addResourceCustomizer(
(resource, configProperties) ->
customizeResource(resource, credentials, configProperties));
customizeResource(
resource, resolveCredentials(configProperties), configProperties));
}

private static GoogleCredentials resolveCredentials(ConfigProperties configProperties) {
synchronized (credentialsCache) {
GoogleCredentials cachedCredentials = credentialsCache.get(configProperties);
if (cachedCredentials != null) {
return cachedCredentials;
}

GoogleCredentials resolvedCredentials = loadCredentials(configProperties);
credentialsCache.put(configProperties, resolvedCredentials);
return resolvedCredentials;
}
}

private static GoogleCredentials loadCredentials(ConfigProperties props) {
Optional<String> credsPath =
ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_PATH.getConfiguredValueAsOptional(props);
if (credsPath.isPresent()) {
try (FileInputStream fis = new FileInputStream(credsPath.get())) {
return GoogleCredentials.fromStream(fis);
} catch (IOException e) {
throw new ConfigurationException(
"Failed to load credentials from file: " + new File(credsPath.get()).getName(), e);
}
}

Optional<String> credsJson =
ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_JSON.getConfiguredValueAsOptional(props);
if (credsJson.isPresent()) {
try (ByteArrayInputStream bais =
new ByteArrayInputStream(credsJson.get().getBytes(StandardCharsets.UTF_8))) {
return GoogleCredentials.fromStream(bais);
} catch (IOException e) {
throw new ConfigurationException("Failed to load credentials from JSON string", e);
}
}

try {
return GoogleCredentials.getApplicationDefault();
} catch (IOException e) {
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
import java.util.AbstractMap.SimpleEntry;
Expand Down Expand Up @@ -243,6 +248,166 @@ void testTraceCustomizerOtlpGrpc() {
}
}

@Test
void testCredentialsLoadedFromFilePath() throws IOException {
File tempFile = File.createTempFile("gcp-creds", ".json");
tempFile.deleteOnExit();
try (Writer writer = Files.newBufferedWriter(tempFile.toPath(), StandardCharsets.UTF_8)) {
writer.write("{}");
}
Comment thread
keshavdandeva marked this conversation as resolved.

System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
System.setProperty(
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty(), SIGNAL_TYPE_ALL);
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_PATH.getSystemProperty(),
tempFile.getAbsolutePath());

try {
prepareMockBehaviorForGoogleCredentials();
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
configureGrpcMockSpanExporter(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);

try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(() -> GoogleCredentials.fromStream(any(InputStream.class)))
.thenReturn(mockedGoogleCredentials);

buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);

googleCredentialsMockedStatic.verify(
() -> GoogleCredentials.fromStream(any(InputStream.class)), Mockito.atLeastOnce());
Comment thread
keshavdandeva marked this conversation as resolved.
}
} finally {
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_PATH.getSystemProperty());
}
}
Comment thread
keshavdandeva marked this conversation as resolved.

@Test
void testCredentialsLoadedFromJsonString() throws IOException {
Comment thread
keshavdandeva marked this conversation as resolved.
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
System.setProperty(
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty(), SIGNAL_TYPE_ALL);
System.setProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_JSON.getSystemProperty(), "{}");

try {
prepareMockBehaviorForGoogleCredentials();
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
configureGrpcMockSpanExporter(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);

try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(() -> GoogleCredentials.fromStream(any(InputStream.class)))
.thenReturn(mockedGoogleCredentials);

buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);

googleCredentialsMockedStatic.verify(
() -> GoogleCredentials.fromStream(any(InputStream.class)), Mockito.atLeastOnce());
}
} finally {
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_JSON.getSystemProperty());
}
}

@Test
void testFallbackToApplicationDefaultCredentials() throws IOException {
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
System.setProperty(
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty(), SIGNAL_TYPE_ALL);
Comment thread
keshavdandeva marked this conversation as resolved.

try {
prepareMockBehaviorForGoogleCredentials();
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
configureGrpcMockSpanExporter(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);

try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(GoogleCredentials::getApplicationDefault)
.thenReturn(mockedGoogleCredentials);

buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);

googleCredentialsMockedStatic.verify(
GoogleCredentials::getApplicationDefault, Mockito.atLeastOnce());
googleCredentialsMockedStatic.verify(
() -> GoogleCredentials.fromStream(any(InputStream.class)), Mockito.never());
}
} finally {
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty());
}
}

@Test
void testCredentialsPathTakesPrecedenceOverJson() throws IOException {
File tempFile = File.createTempFile("gcp-creds", ".json");
tempFile.deleteOnExit();
try (Writer writer = Files.newBufferedWriter(tempFile.toPath(), StandardCharsets.UTF_8)) {
writer.write("{}");
}

System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
System.setProperty(
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty(), SIGNAL_TYPE_ALL);
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_PATH.getSystemProperty(),
tempFile.getAbsolutePath());
System.setProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_JSON.getSystemProperty(), "{}");

try {
prepareMockBehaviorForGoogleCredentials();
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
configureGrpcMockSpanExporter(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);

try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(() -> GoogleCredentials.fromStream(any(InputStream.class)))
.thenReturn(mockedGoogleCredentials);

buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);

googleCredentialsMockedStatic.verify(
() -> GoogleCredentials.fromStream(any(InputStream.class)), Mockito.atLeastOnce());
googleCredentialsMockedStatic.verify(
GoogleCredentials::getApplicationDefault, Mockito.never());
}
} finally {
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_PATH.getSystemProperty());
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_CREDENTIALS_JSON.getSystemProperty());
}
}

// TODO: Use parameterized test for testing metrics customizer for http & grpc.
@Test
void testMetricCustomizerOtlpHttp() {
Expand Down
Loading