Skip to content

Commit 2802de3

Browse files
authored
Merge pull request #38846: [OpenTelemetry] Add turning off to gcpauth extension
2 parents 597f925 + 56d4ffd commit 2802de3

5 files changed

Lines changed: 282 additions & 193 deletions

File tree

buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ class BeamModulePlugin implements Plugin<Project> {
851851
log4j2_log4j12_api : "org.apache.logging.log4j:log4j-1.2-api:$log4j2_version",
852852
mockito_core : "org.mockito:mockito-core:4.11.0",
853853
mockito_inline : "org.mockito:mockito-inline:4.11.0",
854+
mockito_junit_jupiter : "org.mockito:mockito-junit-jupiter:4.11.0",
854855
mongo_java_driver : "org.mongodb:mongodb-driver-sync:5.5.0",
855856
mongo_bson : "org.mongodb:bson:5.5.0",
856857
mongodb_driver_core : "org.mongodb:mongodb-driver-core:5.5.0",

sdks/java/extensions/opentelemetry-gcp-auth-extension/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependencies {
3737
testImplementation library.java.junit
3838
testImplementation library.java.mockito_core
3939
testImplementation library.java.mockito_inline
40+
testImplementation library.java.mockito_junit_jupiter
4041
testImplementation library.java.jupiter_api
4142
testImplementation library.java.jupiter_params
4243
testRuntimeOnly library.java.jupiter_engine

sdks/java/extensions/opentelemetry-gcp-auth-extension/src/main/java/org/apache/beam/sdk/extensions/opentelemetry/gcp/auth/ConfigurableOption.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum ConfigurableOption {
6161
* <li>{@code metrics} - Enables authentication for metric exports.
6262
* <li>{@code traces} - Enables authentication for trace exports.
6363
* <li>{@code all} - Enables authentication for all exports.
64+
* <li>{@code none} - Disables authentication for all exports.
6465
* </ul>
6566
*
6667
* <p>The values are case-sensitive. Whitespace around commas and values is ignored. Can be

sdks/java/extensions/opentelemetry-gcp-auth-extension/src/main/java/org/apache/beam/sdk/extensions/opentelemetry/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java

Lines changed: 103 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,20 @@
2020
import static io.opentelemetry.api.common.AttributeKey.stringKey;
2121
import static java.util.Arrays.stream;
2222
import static java.util.stream.Collectors.joining;
23+
import static java.util.stream.Collectors.toList;
2324
import static java.util.stream.Collectors.toMap;
2425

2526
import com.google.auth.oauth2.GoogleCredentials;
2627
import com.google.auto.service.AutoService;
2728
import io.opentelemetry.api.common.Attributes;
2829
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
30+
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
2931
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
32+
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
3033
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
34+
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
3135
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
36+
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
3237
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
3338
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
3439
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
@@ -41,8 +46,9 @@
4146
import java.util.Map;
4247
import java.util.Objects;
4348
import java.util.Optional;
49+
import java.util.logging.Level;
50+
import java.util.logging.Logger;
4451
import javax.annotation.Nonnull;
45-
import javax.annotation.Nullable;
4652
import org.apache.beam.sdk.annotations.Internal;
4753
import org.apache.beam.sdk.extensions.opentelemetry.gcp.auth.GoogleAuthException.Reason;
4854

@@ -62,25 +68,34 @@
6268
* @see AutoConfigurationCustomizerProvider
6369
* @see GoogleCredentials
6470
*/
65-
@AutoService(AutoConfigurationCustomizerProvider.class)
6671
@Internal
72+
@AutoService(AutoConfigurationCustomizerProvider.class)
6773
public class GcpAuthAutoConfigurationCustomizerProvider
6874
implements AutoConfigurationCustomizerProvider {
6975

76+
private static final Logger LOG =
77+
Logger.getLogger(GcpAuthAutoConfigurationCustomizerProvider.class.getName());
78+
private static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION =
79+
String.format(
80+
"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.",
81+
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getUserReadableName(),
82+
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getEnvironmentVariable(),
83+
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty());
84+
7085
static final String QUOTA_USER_PROJECT_HEADER = "x-goog-user-project";
7186
static final String GCP_USER_PROJECT_ID_KEY = "gcp.project_id";
7287

7388
static final String SIGNAL_TYPE_TRACES = "traces";
7489
static final String SIGNAL_TYPE_METRICS = "metrics";
7590
static final String SIGNAL_TYPE_ALL = "all";
76-
77-
private @Nullable GoogleCredentials credentials;
91+
static final String SIGNAL_TYPE_NONE = "none";
7892

7993
/**
8094
* Customizes the provided {@link AutoConfigurationCustomizer} such that authenticated exports to
8195
* GCP Telemetry API are possible from the configured OTLP exporter.
8296
*
83-
* <p>This method performs the following:
97+
* <p>This method attempts to retrieve Google Application Default Credentials (ADC) and performs
98+
* the following:
8499
*
85100
* <ul>
86101
* <li>Verifies whether the configured OTLP endpoint (base or signal specific) is a known GCP
@@ -99,147 +114,139 @@ public class GcpAuthAutoConfigurationCustomizerProvider
99114
*/
100115
@Override
101116
public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
117+
java.util.function.Supplier<GoogleCredentials> credentialsSupplier =
118+
new java.util.function.Supplier<GoogleCredentials>() {
119+
private @javax.annotation.Nullable GoogleCredentials credentials;
120+
121+
@Override
122+
public synchronized GoogleCredentials get() {
123+
if (credentials == null) {
124+
try {
125+
credentials = GoogleCredentials.getApplicationDefault();
126+
} catch (IOException e) {
127+
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
128+
}
129+
}
130+
return credentials;
131+
}
132+
};
102133
autoConfiguration
103-
.addSpanExporterCustomizer(this::customizeSpanExporter)
104-
.addMetricExporterCustomizer(this::customizeMetricExporter)
105-
.addResourceCustomizer(this::customizeResource);
134+
.addSpanExporterCustomizer(
135+
(spanExporter, configProperties) ->
136+
customizeSpanExporter(spanExporter, credentialsSupplier, configProperties))
137+
.addMetricExporterCustomizer(
138+
(metricExporter, configProperties) ->
139+
customizeMetricExporter(metricExporter, credentialsSupplier, configProperties))
140+
.addResourceCustomizer(
141+
(resource, configProperties) ->
142+
customizeResource(resource, credentialsSupplier, configProperties));
106143
}
107144

108145
@Override
109146
public int order() {
110147
return Integer.MAX_VALUE - 1;
111148
}
112149

113-
private synchronized GoogleCredentials getCredentials() {
114-
if (credentials == null) {
115-
try {
116-
credentials = GoogleCredentials.getApplicationDefault();
117-
} catch (IOException e) {
118-
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
119-
}
120-
}
121-
return credentials;
122-
}
123-
124-
private SpanExporter customizeSpanExporter(
125-
SpanExporter exporter, ConfigProperties configProperties) {
150+
private static SpanExporter customizeSpanExporter(
151+
SpanExporter exporter,
152+
java.util.function.Supplier<GoogleCredentials> credentialsSupplier,
153+
ConfigProperties configProperties) {
126154
if (isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) {
127-
return addAuthorizationHeaders(exporter, configProperties);
155+
return addAuthorizationHeaders(exporter, credentialsSupplier.get(), configProperties);
156+
} else {
157+
String[] params = {
158+
SIGNAL_TYPE_TRACES, SIGNAL_TYPE_NONE, SIGNAL_TARGET_WARNING_FIX_SUGGESTION
159+
};
160+
LOG.log(
161+
Level.WARNING,
162+
"GCP Authentication Extension is not configured for signal type: {0} or is configured with signal type: {1}. {2}",
163+
params);
128164
}
129165
return exporter;
130166
}
131167

132-
private MetricExporter customizeMetricExporter(
133-
MetricExporter exporter, ConfigProperties configProperties) {
168+
private static MetricExporter customizeMetricExporter(
169+
MetricExporter exporter,
170+
java.util.function.Supplier<GoogleCredentials> credentialsSupplier,
171+
ConfigProperties configProperties) {
134172
if (isSignalTargeted(SIGNAL_TYPE_METRICS, configProperties)) {
135-
return addAuthorizationHeaders(exporter, configProperties);
173+
return addAuthorizationHeaders(exporter, credentialsSupplier.get(), configProperties);
174+
} else {
175+
String[] params = {
176+
SIGNAL_TYPE_METRICS, SIGNAL_TYPE_NONE, SIGNAL_TARGET_WARNING_FIX_SUGGESTION
177+
};
178+
LOG.log(
179+
Level.WARNING,
180+
"GCP Authentication Extension is not configured for signal type: {0} or is configured with signal type: {1}. {2}",
181+
params);
136182
}
137183
return exporter;
138184
}
139185

140186
// Checks if the auth extension is configured to target the passed signal for authentication.
141187
private static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) {
142-
String endpoint = configProperties.getString("otel.exporter.otlp." + checkSignal + ".endpoint");
143-
if (endpoint == null) {
144-
endpoint = configProperties.getString("otel.exporter.otlp.endpoint");
145-
}
146-
if (endpoint == null) {
147-
return false;
148-
}
149-
150-
try {
151-
java.net.URI uri = new java.net.URI(endpoint);
152-
String host = uri.getHost();
153-
String scheme = uri.getScheme();
154-
if (host == null
155-
|| scheme == null
156-
|| !scheme.equalsIgnoreCase("https")
157-
|| (!host.equalsIgnoreCase("telemetry.googleapis.com")
158-
&& !host.equalsIgnoreCase("telemetry.mtls.googleapis.com"))) {
159-
return false;
160-
}
161-
} catch (java.net.URISyntaxException e) {
162-
return false;
163-
}
164-
165188
String userSpecifiedTargetedSignals =
166189
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValueWithFallback(
167190
configProperties, () -> SIGNAL_TYPE_ALL);
168-
return stream(userSpecifiedTargetedSignals.split(","))
169-
.map(String::trim)
170-
.anyMatch(
171-
targetedSignal ->
172-
targetedSignal.equals(checkSignal) || targetedSignal.equals(SIGNAL_TYPE_ALL));
173-
}
174-
175-
private boolean isAnySignalTargeted(ConfigProperties configProperties) {
176-
return isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)
177-
|| isSignalTargeted(SIGNAL_TYPE_METRICS, configProperties);
191+
List<String> targetedSignals =
192+
stream(userSpecifiedTargetedSignals.split(",")).map(String::trim).collect(toList());
193+
if (targetedSignals.contains(SIGNAL_TYPE_NONE)) {
194+
return false;
195+
}
196+
return targetedSignals.contains(checkSignal) || targetedSignals.contains(SIGNAL_TYPE_ALL);
178197
}
179198

180199
// Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and
181200
// OtlpHttpSpanExporter.
182-
private SpanExporter addAuthorizationHeaders(
183-
SpanExporter exporter, ConfigProperties configProperties) {
201+
private static SpanExporter addAuthorizationHeaders(
202+
SpanExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) {
184203
if (exporter instanceof OtlpHttpSpanExporter) {
185-
SpanExporter result =
204+
OtlpHttpSpanExporterBuilder builder =
186205
((OtlpHttpSpanExporter) exporter)
187206
.toBuilder()
188-
.setHeaders(() -> getRequiredHeaderMap(configProperties))
189-
.build();
190-
exporter.shutdown();
191-
return result;
207+
.setHeaders(() -> getRequiredHeaderMap(credentials, configProperties));
208+
return builder.build();
192209
} else if (exporter instanceof OtlpGrpcSpanExporter) {
193-
SpanExporter result =
210+
OtlpGrpcSpanExporterBuilder builder =
194211
((OtlpGrpcSpanExporter) exporter)
195212
.toBuilder()
196-
.setHeaders(() -> getRequiredHeaderMap(configProperties))
197-
.build();
198-
exporter.shutdown();
199-
return result;
213+
.setHeaders(() -> getRequiredHeaderMap(credentials, configProperties));
214+
return builder.build();
200215
}
201216
return exporter;
202217
}
203218

204219
// Adds authorization headers to the calls made by the OtlpGrpcMetricExporter and
205220
// OtlpHttpMetricExporter.
206-
private MetricExporter addAuthorizationHeaders(
207-
MetricExporter exporter, ConfigProperties configProperties) {
221+
private static MetricExporter addAuthorizationHeaders(
222+
MetricExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) {
208223
if (exporter instanceof OtlpHttpMetricExporter) {
209-
MetricExporter result =
224+
OtlpHttpMetricExporterBuilder builder =
210225
((OtlpHttpMetricExporter) exporter)
211226
.toBuilder()
212-
.setHeaders(() -> getRequiredHeaderMap(configProperties))
213-
.build();
214-
exporter.shutdown();
215-
return result;
227+
.setHeaders(() -> getRequiredHeaderMap(credentials, configProperties));
228+
return builder.build();
216229
} else if (exporter instanceof OtlpGrpcMetricExporter) {
217-
MetricExporter result =
230+
OtlpGrpcMetricExporterBuilder builder =
218231
((OtlpGrpcMetricExporter) exporter)
219232
.toBuilder()
220-
.setHeaders(() -> getRequiredHeaderMap(configProperties))
221-
.build();
222-
exporter.shutdown();
223-
return result;
233+
.setHeaders(() -> getRequiredHeaderMap(credentials, configProperties));
234+
return builder.build();
224235
}
225236
return exporter;
226237
}
227238

228-
private Map<String, String> getRequiredHeaderMap(ConfigProperties configProperties) {
229-
GoogleCredentials creds = getCredentials();
239+
private static Map<String, String> getRequiredHeaderMap(
240+
GoogleCredentials credentials, ConfigProperties configProperties) {
230241
Map<String, List<String>> gcpHeaders;
231242
try {
232243
// this also refreshes the credentials, if required
233-
gcpHeaders = creds.getRequestMetadata();
244+
gcpHeaders = credentials.getRequestMetadata();
234245
} catch (IOException e) {
235246
throw new GoogleAuthException(Reason.FAILED_ADC_REFRESH, e);
236247
}
237-
if (gcpHeaders == null) {
238-
return Map.of();
239-
}
240248
Map<String, String> flattenedHeaders =
241249
gcpHeaders.entrySet().stream()
242-
.filter(entry -> entry.getKey() != null && entry.getValue() != null)
243250
.collect(
244251
toMap(
245252
Map.Entry::getKey,
@@ -262,16 +269,19 @@ private Map<String, String> getRequiredHeaderMap(ConfigProperties configProperti
262269
}
263270

264271
// Updates the current resource with the attributes required for ingesting OTLP data on GCP.
265-
private Resource customizeResource(Resource resource, ConfigProperties configProperties) {
266-
if (!isAnySignalTargeted(configProperties)) {
272+
private static Resource customizeResource(
273+
Resource resource,
274+
java.util.function.Supplier<GoogleCredentials> credentialsSupplier,
275+
ConfigProperties configProperties) {
276+
if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)
277+
&& !isSignalTargeted(SIGNAL_TYPE_METRICS, configProperties)) {
267278
return resource;
268279
}
269-
270280
String gcpProjectId;
271281
try {
272282
gcpProjectId = ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties);
273283
} catch (ConfigurationException e) {
274-
gcpProjectId = getCredentials().getProjectId();
284+
gcpProjectId = credentialsSupplier.get().getProjectId();
275285
if (gcpProjectId == null || gcpProjectId.isEmpty()) {
276286
throw e;
277287
}

0 commit comments

Comments
 (0)