Skip to content

Commit 1527083

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

4 files changed

Lines changed: 93 additions & 11 deletions

File tree

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 use the `ServiceOptions` class from `google-cloud-core` to determine the project ID. The `ServiceOptions` has a comprehensive logic to determine the project ID, which includes checking the environment variable `GOOGLE_CLOUD_PROJECT`, the `gcloud` configuration, the local metadata server, and the `GOOGLE_APPLICATION_CREDENTIALS` file.
43+
- **Important Note**: The agent configuration will fail if this option is not set or 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 testResolveGoogleProjectIdFromServiceOptions() {
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)