Skip to content

Commit 5becf5e

Browse files
Try resolving project id from Google credentials if not provided
1 parent 091ebd2 commit 5becf5e

File tree

4 files changed

+93
-11
lines changed

4 files changed

+93
-11
lines changed

gcp-auth-extension/README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ The extension can be configured either by environment variables or system proper
3434

3535
Here is a list of required and optional configuration available for the extension:
3636

37-
#### Required Config
37+
#### Optional Config
3838

3939
- `GOOGLE_CLOUD_PROJECT`: Environment variable that represents the Google Cloud Project ID to which the telemetry needs to be exported.
4040

4141
- Can also be configured using `google.cloud.project` system property.
42-
- This is a required option, the agent configuration will fail if this option is not set.
43-
44-
#### Optional Config
42+
- If neither of these options are set, the extension will attempt to infer the project id from the credentials used, however notice that not all credentials implementations will be able to provide a project id, so the inference is only a best-effort attempt.
43+
- **Important Note**: The agent configuration will fail if this option is not set and cannot be inferred.
4544

4645
- `GOOGLE_CLOUD_QUOTA_PROJECT`: Environment variable that represents the Google Cloud Quota Project ID which will be charged for the GCP API usage. To learn more about a *quota project*, see the [Quota project overview](https://cloud.google.com/docs/quotas/quota-project) page. Additional details about configuring the *quota project* can be found on the [Set the quota project](https://cloud.google.com/docs/quotas/set-quota-project) page.
4746

gcp-auth-extension/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ dependencies {
2424
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
2525

2626
// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin
27-
implementation("com.google.auth:google-auth-library-oauth2-http:1.39.1")
27+
implementation("com.google.auth:google-auth-library-oauth2-http:1.40.0")
2828

2929
// Test dependencies
3030
testCompileOnly("com.google.auto.service:auto-service-annotations")
3131
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
3232
testImplementation("org.junit.jupiter:junit-jupiter-api")
3333
testCompileOnly("org.junit.jupiter:junit-jupiter-params")
34+
testImplementation("org.junit-pioneer:junit-pioneer")
3435

3536
testImplementation("io.opentelemetry:opentelemetry-api")
3637
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")

gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
package io.opentelemetry.contrib.gcp.auth;
77

8-
import static io.opentelemetry.api.common.AttributeKey.stringKey;
98
import static java.util.Arrays.stream;
109
import static java.util.stream.Collectors.joining;
1110
import static java.util.stream.Collectors.toMap;
1211

1312
import com.google.auth.oauth2.GoogleCredentials;
1413
import com.google.auto.service.AutoService;
14+
import io.opentelemetry.api.common.AttributeKey;
1515
import io.opentelemetry.api.common.Attributes;
1616
import io.opentelemetry.contrib.gcp.auth.GoogleAuthException.Reason;
1717
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
@@ -25,6 +25,7 @@
2525
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
2626
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
2727
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
28+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
2829
import io.opentelemetry.sdk.metrics.export.MetricExporter;
2930
import io.opentelemetry.sdk.resources.Resource;
3031
import io.opentelemetry.sdk.trace.export.SpanExporter;
@@ -111,7 +112,12 @@ public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
111112
.addMetricExporterCustomizer(
112113
(metricExporter, configProperties) ->
113114
customizeMetricExporter(metricExporter, credentials, configProperties))
114-
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
115+
.addResourceCustomizer(
116+
(resource, configProperties) -> {
117+
String gcpProjectId = getGoogleProjectId(configProperties, credentials);
118+
119+
return customizeResource(resource, gcpProjectId);
120+
});
115121
}
116122

117123
@Override
@@ -228,10 +234,36 @@ private static Map<String, String> getRequiredHeaderMap(
228234
}
229235

230236
// Updates the current resource with the attributes required for ingesting OTLP data on GCP.
231-
private static Resource customizeResource(Resource resource, ConfigProperties configProperties) {
232-
String gcpProjectId =
233-
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties);
234-
Resource res = Resource.create(Attributes.of(stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId));
237+
private static Resource customizeResource(Resource resource, String gcpProjectId) {
238+
Resource res =
239+
Resource.create(
240+
Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId));
235241
return resource.merge(res);
236242
}
243+
244+
/**
245+
* Retrieves the Google Cloud Project ID from the configuration properties, falling back to
246+
* google-cloud-core's ServiceOptions project ID resolution if not explicitly set.
247+
*
248+
* @param configProperties The configuration properties containing the GCP project ID.
249+
* @return The Google Cloud Project ID.
250+
*/
251+
@Nonnull
252+
static String getGoogleProjectId(
253+
ConfigProperties configProperties, GoogleCredentials credentials) {
254+
String googleProjectId =
255+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValueWithFallback(
256+
configProperties, credentials::getProjectId);
257+
258+
if (googleProjectId == null || googleProjectId.isEmpty()) {
259+
throw new ConfigurationException(
260+
String.format(
261+
"GCP Authentication Extension not configured properly: %s not configured. Configure it by exporting environment variable %s or system property %s",
262+
ConfigurableOption.GOOGLE_CLOUD_PROJECT,
263+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getEnvironmentVariable(),
264+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty()));
265+
}
266+
267+
return googleProjectId;
268+
}
237269
}

gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProviderTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.SIGNAL_TYPE_TRACES;
1313
import static org.assertj.core.api.Assertions.assertThat;
1414
import static org.assertj.core.api.Assertions.assertThatThrownBy;
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertThrows;
1517
import static org.mockito.ArgumentMatchers.any;
1618
import static org.mockito.Mockito.mock;
1719
import static org.mockito.Mockito.when;
@@ -40,6 +42,7 @@
4042
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
4143
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
4244
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
45+
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
4346
import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
4447
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
4548
import io.opentelemetry.sdk.common.CompletableResultCode;
@@ -74,6 +77,8 @@
7477
import org.junit.jupiter.params.ParameterizedTest;
7578
import org.junit.jupiter.params.provider.Arguments;
7679
import org.junit.jupiter.params.provider.MethodSource;
80+
import org.junitpioneer.jupiter.ClearSystemProperty;
81+
import org.junitpioneer.jupiter.SetSystemProperty;
7782
import org.mockito.ArgumentCaptor;
7883
import org.mockito.Captor;
7984
import org.mockito.Mock;
@@ -538,6 +543,51 @@ void testTargetSignalsBehavior(TargetSignalBehavior testCase) {
538543
}
539544
}
540545

546+
@Test
547+
@ClearSystemProperty(key = "GOOGLE_CLOUD_PROJECT")
548+
@ClearSystemProperty(key = "google.cloud.project")
549+
void testThrowsExceptionIfGoogleProjectIsNotFound() {
550+
DefaultConfigProperties configProperties =
551+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
552+
553+
Mockito.when(mockedGoogleCredentials.getProjectId()).thenReturn(null);
554+
555+
assertThrows(
556+
ConfigurationException.class,
557+
() ->
558+
GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(
559+
configProperties, mockedGoogleCredentials));
560+
}
561+
562+
@Test
563+
@SetSystemProperty(key = "google.cloud.project", value = DUMMY_GCP_RESOURCE_PROJECT_ID)
564+
void testResolveGoogleProjectIdFromSystemProperty() {
565+
DefaultConfigProperties configProperties =
566+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
567+
568+
assertEquals(
569+
DUMMY_GCP_RESOURCE_PROJECT_ID,
570+
GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(
571+
configProperties, mockedGoogleCredentials));
572+
573+
Mockito.verifyNoInteractions(mockedGoogleCredentials);
574+
}
575+
576+
@Test
577+
@ClearSystemProperty(key = "google.cloud.project")
578+
@ClearSystemProperty(key = "GOOGLE_CLOUD_PROJECT")
579+
void testResolveGoogleProjectIdFromCredentials() {
580+
DefaultConfigProperties configProperties =
581+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
582+
583+
Mockito.when(mockedGoogleCredentials.getProjectId()).thenReturn(DUMMY_GCP_RESOURCE_PROJECT_ID);
584+
585+
assertEquals(
586+
DUMMY_GCP_RESOURCE_PROJECT_ID,
587+
GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(
588+
configProperties, mockedGoogleCredentials));
589+
}
590+
541591
/** Test cases specifying expected behavior for GOOGLE_OTEL_AUTH_TARGET_SIGNALS */
542592
private static Stream<Arguments> provideTargetSignalBehaviorTestCases() {
543593
return Stream.of(

0 commit comments

Comments
 (0)