Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
21d4a01
gcp auth
zeitlinger Jul 11, 2025
f9eb67b
./gradlew spotlessApply
otelbot[bot] Jul 11, 2025
35fc448
copy declarative config bridge from instrumentation
zeitlinger Jul 14, 2025
30d61fc
support config bridge
zeitlinger Jul 14, 2025
0f49549
support config bridge
zeitlinger Jul 14, 2025
fc7cd07
copy declarative config bridge from instrumentation
zeitlinger Jul 14, 2025
d786ea6
format
zeitlinger Jul 14, 2025
bf9c8e6
cleanup
zeitlinger Jul 14, 2025
b70486a
cleanup
zeitlinger Jul 14, 2025
4d56209
fix test
zeitlinger Jul 14, 2025
85ec1cb
fix
zeitlinger Jul 14, 2025
83e74a7
add property translation for inferred spans
zeitlinger Jul 14, 2025
9fe79a1
inferred spans
zeitlinger Jul 15, 2025
71f21b7
inferred spans
zeitlinger Jul 15, 2025
0618b39
inferred spans
zeitlinger Jul 15, 2025
27fc125
baggage processor
zeitlinger Jul 15, 2025
d27d7ce
format
zeitlinger Jul 15, 2025
1e77d6f
format
zeitlinger Jul 15, 2025
548c0c4
stack trace span processor
zeitlinger Jul 15, 2025
092fce3
stack trace span processor
zeitlinger Jul 15, 2025
dd10055
fix
zeitlinger Jul 15, 2025
444833f
fix
zeitlinger Jul 15, 2025
4211e85
fix
zeitlinger Jul 15, 2025
e4de567
make temp dir more reliable
zeitlinger Jul 15, 2025
19d017d
make temp dir more reliable
zeitlinger Jul 15, 2025
fb4a7ef
revert inferred spans (flaky)
zeitlinger Jul 15, 2025
a9d9c05
baggage is in a separate PR
zeitlinger Jul 16, 2025
4a119c9
use unified bridge
zeitlinger Jul 18, 2025
4f7014a
add experimental- suffix, add test
zeitlinger Jul 22, 2025
6587b31
add experimental- suffix, add test
zeitlinger Jul 22, 2025
d0718e6
update bridge to match agent
zeitlinger Jul 24, 2025
e042b15
update bridge to match agent
zeitlinger Jul 24, 2025
5bf5e78
Revert "update bridge to match agent"
zeitlinger Jul 24, 2025
b3bfd03
update bridge to match agent
zeitlinger Jul 24, 2025
712cf27
Update gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/…
zeitlinger Aug 4, 2025
d2e2ef4
Update gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/…
zeitlinger Aug 4, 2025
85fad27
pr review
zeitlinger Aug 4, 2025
65d2f8e
pr review
zeitlinger Aug 4, 2025
91be485
pr review
zeitlinger Aug 4, 2025
bfe1c5e
update config bridge from agent
zeitlinger Aug 15, 2025
454fe0d
./gradlew spotlessApply
otelbot[bot] Aug 15, 2025
4b10dda
move bridge to agent
zeitlinger Aug 22, 2025
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
12 changes: 12 additions & 0 deletions gcp-auth-extension/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ dependencies {
compileOnly("com.google.auto.service:auto-service-annotations")
compileOnly("io.opentelemetry:opentelemetry-api")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator")
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")

// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin
Expand All @@ -36,7 +38,9 @@ dependencies {
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")
testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator")

testImplementation("org.awaitility:awaitility")
testImplementation("org.mockito:mockito-inline")
Expand Down Expand Up @@ -130,3 +134,11 @@ tasks.register<Test>("IntegrationTestUserCreds") {
"-Dmockserver.logLevel=trace"
)
}

// todo remove when https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14497 is merged
configurations.all {
resolutionStrategy {
force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.20.0-alpha-SNAPSHOT")
force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.19.0")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.function.BiFunction;
import javax.annotation.Nullable;

/**
* An enum representing configurable options for a GCP Authentication Extension. Each option has a
Expand Down Expand Up @@ -92,57 +92,41 @@ String getUserReadableName() {
}

/**
* Retrieves the configured value for this option. This method checks the environment variable
* first and then the system property.
* Retrieves the configured value for this option.
*
* @return The configured value as a string, or throws an exception if not configured.
* @throws ConfigurationException if neither the environment variable nor the system property is
* set.
*/
String getConfiguredValue(ConfigProperties configProperties) {
String configuredValue = configProperties.getString(this.getSystemProperty());
if (configuredValue != null && !configuredValue.isEmpty()) {
return configuredValue;
} else {
<T> T getRequiredConfiguredValue(
ConfigProperties configProperties, BiFunction<ConfigProperties, String, T> extractor) {
T configuredValue = getConfiguredValue(configProperties, extractor);
if (configuredValue == null) {
throw new ConfigurationException(
String.format(
"GCP Authentication Extension not configured properly: %s not configured. Configure it by exporting environment variable %s or system property %s",
"GCP Authentication Extension not configured properly: %s not configured. "
+ "Configure it by exporting environment variable %s or system property %s",
this.userReadableName, this.getEnvironmentVariable(), this.getSystemProperty()));
}
return configuredValue;
}

/**
* Retrieves the value for this option, prioritizing environment variables and system properties.
* If neither an environment variable nor a system property is set for this option, the provided
* fallback function is used to determine the value.
* Retrieves the configured value for this option.
*
* @param fallback A {@link Supplier} that provides the default value for the option when it is
* not explicitly configured via an environment variable or system property.
* @return The configured value for the option, obtained from the environment variable, system
* property, or the fallback function, in that order of precedence.
* @return The configured value as a string, or {@code null} if not configured.
*/
String getConfiguredValueWithFallback(
ConfigProperties configProperties, Supplier<String> fallback) {
try {
return this.getConfiguredValue(configProperties);
} catch (ConfigurationException e) {
return fallback.get();
@Nullable
<T> T getConfiguredValue(
ConfigProperties configProperties, BiFunction<ConfigProperties, String, T> extractor) {
T configuredValue = extractor.apply(configProperties, this.getSystemProperty());
if (configuredValue instanceof String) {
String value = (String) configuredValue;
if (value.isEmpty()) {
configuredValue = null; // Treat empty string as not configured
}
}
}

/**
* Retrieves the value for this option, prioritizing environment variables before system
* properties. If neither an environment variable nor a system property is set for this option,
* then an empty {@link Optional} is returned.
*
* @return The configured value for the option, if set, obtained from the environment variable,
* system property, or empty {@link Optional}, in that order of precedence.
*/
Optional<String> getConfiguredValueAsOptional(ConfigProperties configProperties) {
try {
return Optional.of(this.getConfiguredValue(configProperties));
} catch (ConfigurationException e) {
return Optional.empty();
}
return configuredValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.google.auth.oauth2.GoogleCredentials;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.contrib.gcp.auth.GoogleAuthException.Reason;
Expand All @@ -25,7 +26,7 @@
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -84,8 +85,8 @@ public class GcpAuthAutoConfigurationCustomizerProvider
* customizes only the signal specific exporter.
* </ul>
*
* The 'customization' performed includes customizing the exporters by adding required headers to
* the export calls made and customizing the resource by adding required resource attributes to
* <p>The 'customization' performed includes customizing the exporters by adding required headers
* to the export calls made and customizing the resource by adding required resource attributes to
* enable GCP integration.
*
* @param autoConfiguration the AutoConfigurationCustomizer to customize.
Expand All @@ -96,12 +97,7 @@ 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);
}
GoogleCredentials credentials = getCredentials();
autoConfiguration
.addSpanExporterCustomizer(
(spanExporter, configProperties) ->
Expand All @@ -112,51 +108,80 @@ public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
}

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

@Override
public int order() {
return Integer.MAX_VALUE - 1;
}

private static SpanExporter customizeSpanExporter(
SpanExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) {
if (isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) {
if (shouldCustomizeExporter(
SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) {
return addAuthorizationHeaders(exporter, credentials, configProperties);
} else {
String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION};
logger.log(
Level.WARNING,
"GCP Authentication Extension is not configured for signal type: {0}. {1}",
params);
}
return exporter;
}

private static MetricExporter customizeMetricExporter(
MetricExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) {
if (isSignalTargeted(SIGNAL_TYPE_METRICS, configProperties)) {
if (shouldCustomizeExporter(
SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) {
return addAuthorizationHeaders(exporter, credentials, configProperties);
}
return exporter;
}

/**
* Utility method to check whether OTLP exporters should be customized for the given target
* signal.
*
* @param signal The target signal to check against. Could be one of {@value SIGNAL_TYPE_TRACES},
* {@value SIGNAL_TYPE_METRICS} or {@value SIGNAL_TYPE_ALL}.
* @param fixSuggestion A warning to alert the user that auth extension is not configured for the
* provided target signal.
* @param configProperties The {@link ConfigProperties} object used to configure the extension.
* @return A boolean indicating whether the OTLP exporters should be customized or not.
*/
static boolean shouldCustomizeExporter(
String signal, String fixSuggestion, ConfigProperties configProperties) {
if (isSignalTargeted(signal, configProperties)) {
return true;
} else {
String[] params = {SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION};
logger.log(
Level.WARNING,
"GCP Authentication Extension is not configured for signal type: {0}. {1}",
params);
new String[] {signal, fixSuggestion});
return false;
}
return exporter;
}

// Checks if the auth extension is configured to target the passed signal for authentication.
private static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) {
String userSpecifiedTargetedSignals =
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValueWithFallback(
configProperties, () -> SIGNAL_TYPE_ALL);
return Arrays.stream(userSpecifiedTargetedSignals.split(","))
.map(String::trim)
return targetSignals(configProperties).stream()
.anyMatch(
targetedSignal ->
targetedSignal.equals(checkSignal) || targetedSignal.equals(SIGNAL_TYPE_ALL));
}

@VisibleForTesting
static List<String> targetSignals(ConfigProperties configProperties) {
return Objects.requireNonNull(
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValue(
configProperties,
(properties, name) ->
properties.getList(name, Collections.singletonList(SIGNAL_TYPE_ALL))));
}

// Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and
// OtlpHttpSpanExporter.
private static SpanExporter addAuthorizationHeaders(
Expand Down Expand Up @@ -193,7 +218,7 @@ private static MetricExporter addAuthorizationHeaders(
return exporter;
}

private static Map<String, String> getRequiredHeaderMap(
static Map<String, String> getRequiredHeaderMap(
GoogleCredentials credentials, ConfigProperties configProperties) {
Map<String, List<String>> gcpHeaders;
try {
Expand All @@ -216,23 +241,31 @@ private static Map<String, String> getRequiredHeaderMap(
// Add quota user project header if not detected by the auth library and user provided it via
// system properties.
if (!flattenedHeaders.containsKey(QUOTA_USER_PROJECT_HEADER)) {
Optional<String> maybeConfiguredQuotaProjectId =
ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValueAsOptional(
configProperties);
maybeConfiguredQuotaProjectId.ifPresent(
configuredQuotaProjectId ->
flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId));
getQuotaProjectId(configProperties)
.ifPresent(
configuredQuotaProjectId ->
flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId));
}
return flattenedHeaders;
}

static Optional<String> getQuotaProjectId(ConfigProperties configProperties) {
return Optional.ofNullable(
ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValue(
configProperties, ConfigProperties::getString));
}

// Updates the current resource with the attributes required for ingesting OTLP data on GCP.
private static Resource customizeResource(Resource resource, ConfigProperties configProperties) {
String gcpProjectId =
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties);
Resource res =
Resource.create(
Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId));
Attributes.of(
AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), getProjectId(configProperties)));
return resource.merge(res);
}

static String getProjectId(ConfigProperties configProperties) {
return ConfigurableOption.GOOGLE_CLOUD_PROJECT.getRequiredConfiguredValue(
configProperties, ConfigProperties::getString);
}
}
Loading
Loading