Skip to content

Commit 4318e35

Browse files
Try resolving GCP_PROJECT with ServiceOptions if not provided
1 parent 54e054e commit 4318e35

File tree

4 files changed

+82
-8
lines changed

4 files changed

+82
-8
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 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies {
2525

2626
// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin
2727
implementation("com.google.auth:google-auth-library-oauth2-http:1.37.1")
28+
implementation("com.google.cloud:google-cloud-core:2.58.1")
2829

2930
// Test dependencies
3031
testCompileOnly("com.google.auto.service:auto-service-annotations")

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.google.auth.oauth2.GoogleCredentials;
99
import com.google.auto.service.AutoService;
10+
import com.google.cloud.ServiceOptions;
1011
import io.opentelemetry.api.common.AttributeKey;
1112
import io.opentelemetry.api.common.Attributes;
1213
import io.opentelemetry.contrib.gcp.auth.GoogleAuthException.Reason;
@@ -21,6 +22,7 @@
2122
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
2223
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
2324
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
25+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
2426
import io.opentelemetry.sdk.metrics.export.MetricExporter;
2527
import io.opentelemetry.sdk.resources.Resource;
2628
import io.opentelemetry.sdk.trace.export.SpanExporter;
@@ -109,7 +111,12 @@ public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
109111
.addMetricExporterCustomizer(
110112
(metricExporter, configProperties) ->
111113
customizeMetricExporter(metricExporter, credentials, configProperties))
112-
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
114+
.addResourceCustomizer(
115+
(resource, configProperties) -> {
116+
String gcpProjectId = getGoogleProjectId(configProperties);
117+
118+
return customizeResource(resource, gcpProjectId);
119+
});
113120
}
114121

115122
@Override
@@ -227,12 +234,35 @@ private static Map<String, String> getRequiredHeaderMap(
227234
}
228235

229236
// Updates the current resource with the attributes required for ingesting OTLP data on GCP.
230-
private static Resource customizeResource(Resource resource, ConfigProperties configProperties) {
231-
String gcpProjectId =
232-
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties);
237+
private static Resource customizeResource(Resource resource, String gcpProjectId) {
233238
Resource res =
234239
Resource.create(
235240
Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId));
236241
return resource.merge(res);
237242
}
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(ConfigProperties configProperties) {
253+
String googleProjectId =
254+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValueWithFallback(
255+
configProperties, ServiceOptions::getDefaultProjectId);
256+
257+
if (googleProjectId == null || googleProjectId.isEmpty()) {
258+
throw new ConfigurationException(
259+
String.format(
260+
"GCP Authentication Extension not configured properly: %s not configured. Configure it by exporting environment variable %s or system property %s",
261+
ConfigurableOption.GOOGLE_CLOUD_PROJECT,
262+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getEnvironmentVariable(),
263+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty()));
264+
}
265+
266+
return googleProjectId;
267+
}
238268
}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
4040
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
4141
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
42+
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
4243
import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
4344
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
4445
import io.opentelemetry.sdk.common.CompletableResultCode;
@@ -538,6 +539,49 @@ public void testTargetSignalsBehavior(TargetSignalBehavior testCase) {
538539
}
539540
}
540541

542+
@Test
543+
void testThrowsExceptionIfGoogleProjectIsNotFound() {
544+
// Clear the system property to ensure it does not interfere with the test
545+
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
546+
System.clearProperty("GOOGLE_CLOUD_PROJECT");
547+
548+
DefaultConfigProperties configProperties =
549+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
550+
551+
assertThrows(
552+
ConfigurationException.class,
553+
() -> GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(configProperties));
554+
}
555+
556+
@Test
557+
void testResolveGoogleProjectIdFromSystemProperty() {
558+
System.setProperty(
559+
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
560+
561+
DefaultConfigProperties configProperties =
562+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
563+
564+
assertEquals(
565+
DUMMY_GCP_RESOURCE_PROJECT_ID,
566+
GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(configProperties));
567+
}
568+
569+
@Test
570+
void testResolveGoogleProjectIdFromServiceOptions() {
571+
// Clear the system property to ensure it does not interfere with the test
572+
System.clearProperty(ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty());
573+
// Property that the extension expects is google.cloud.project, ServiceOptions works with
574+
// GOOGLE_CLOUD_PROJECT, so this will highlight the fallback to ServiceOptions
575+
System.setProperty("GOOGLE_CLOUD_PROJECT", DUMMY_GCP_RESOURCE_PROJECT_ID);
576+
577+
DefaultConfigProperties configProperties =
578+
DefaultConfigProperties.create(Collections.emptyMap(), Mockito.mock(ComponentLoader.class));
579+
580+
assertEquals(
581+
DUMMY_GCP_RESOURCE_PROJECT_ID,
582+
GcpAuthAutoConfigurationCustomizerProvider.getGoogleProjectId(configProperties));
583+
}
584+
541585
/** Test cases specifying expected behavior for GOOGLE_OTEL_AUTH_TARGET_SIGNALS */
542586
private static Stream<Arguments> provideTargetSignalBehaviorTestCases() {
543587
return Stream.of(

0 commit comments

Comments
 (0)