Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 8 additions & 5 deletions java-bigquery-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
Expand All @@ -374,6 +378,10 @@
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure-spi</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.contrib</groupId>
<artifactId>opentelemetry-gcp-auth-extension</artifactId>
Expand Down Expand Up @@ -439,11 +447,6 @@
<artifactId>opentelemetry-sdk-logs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-trace</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
Expand All @@ -61,8 +66,6 @@ public class BigQueryJdbcOpenTelemetry {
private static final String OTEL_LOGS_EXPORTER = "otel.logs.exporter";
private static final String OTEL_METRICS_EXPORTER = "otel.metrics.exporter";
private static final String GOOGLE_CLOUD_PROJECT = "google.cloud.project";
private static final String CREDENTIALS_JSON = "google.cloud.credentials.json";
private static final String CREDENTIALS_PATH = "google.cloud.credentials.path";
private static final String OTLP_ENDPOINT_VALUE = "https://telemetry.googleapis.com:443";
private static final String EXPORTER_NONE = "none";
private static final String EXPORTER_OTLP = "otlp";
Expand Down Expand Up @@ -230,6 +233,26 @@ public static Collection<TelemetryConfig> getRegisteredConfigs() {
return connectionConfigs.values();
}

private static Map<String, String> getAuthHeaders(Credentials credentials) {
try {
Map<String, List<String>> metadata =
credentials.getRequestMetadata(URI.create(OTLP_ENDPOINT_VALUE));
Map<String, String> headers = new HashMap<>();
metadata.forEach(
(headerKey, headerValues) -> {
if (!headerValues.isEmpty()) {
headers.put(headerKey, headerValues.get(0));
}
});
return headers;
} catch (IOException e) {
// We log the warning and return an empty map, allowing the exporter to fail gracefully
// with a standard OTLP response code (e.g., 401 Unauthorized) handled by OTel.
LOG.warning(e, "Failed to get auth headers");
return new HashMap<>();
}
}
Comment thread
keshavdandeva marked this conversation as resolved.

private static String getCredentialsIdentifier(String credentials) {
if (credentials == null) {
return "";
Expand Down Expand Up @@ -261,8 +284,6 @@ public static OpenTelemetry getOpenTelemetry(
return GlobalOpenTelemetry.get();
}

// NOTE: Currently, tracing only fully supports Application Default Credentials (ADC).
// Once b/503721589 is completed, Service Account (SA) will work as well.
if (!enableGcpTraceExporter && !enableGcpLogExporter) {
return OpenTelemetry.noop();
}
Expand All @@ -276,14 +297,6 @@ public static OpenTelemetry getOpenTelemetry(
key,
k -> {
Map<String, String> props = new HashMap<>();
if (gcpTelemetryCredentials != null) {
byte[] credsBytes = gcpTelemetryCredentials.getBytes(StandardCharsets.UTF_8);
if (BigQueryJdbcOAuthUtility.isJson(credsBytes)) {
props.put(CREDENTIALS_JSON, gcpTelemetryCredentials);
} else {
props.put(CREDENTIALS_PATH, gcpTelemetryCredentials);
}
}

if (enableGcpTraceExporter) {
props.put(OTEL_TRACES_EXPORTER, EXPORTER_OTLP);
Expand Down Expand Up @@ -313,7 +326,35 @@ public static OpenTelemetry getOpenTelemetry(
}

AutoConfiguredOpenTelemetrySdk autoConfigured =
AutoConfiguredOpenTelemetrySdk.builder().addPropertiesSupplier(() -> props).build();
AutoConfiguredOpenTelemetrySdk.builder()
.addPropertiesSupplier(() -> props)
.addSpanExporterCustomizer(
(spanExporter, configProperties) -> {
if (gcpTelemetryCredentials != null) {
try {
Credentials credentials =
resolveCredentialsFromString(gcpTelemetryCredentials);
if (spanExporter instanceof OtlpHttpSpanExporter) {
return ((OtlpHttpSpanExporter) spanExporter)
.toBuilder()
.setHeaders(() -> getAuthHeaders(credentials))
.build();
}
if (spanExporter instanceof OtlpGrpcSpanExporter) {
return ((OtlpGrpcSpanExporter) spanExporter)
.toBuilder()
.setHeaders(() -> getAuthHeaders(credentials))
.build();
}
} catch (Exception e) {
LOG.warning(
e,
"Failed to resolve telemetry credentials. Telemetry will be exported using default OpenTelemetry configuration (custom authentication headers will not be injected).");
}
}
return spanExporter;
})
Comment thread
keshavdandeva marked this conversation as resolved.
.build();

OpenTelemetrySdk sdk = autoConfigured.getOpenTelemetrySdk();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.ServiceOptions;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
Expand All @@ -48,25 +45,6 @@
public class ITAuthTests extends ITBase {
static final String PROJECT_ID = ServiceOptions.getDefaultProjectId();

private JsonObject getAuthJson() throws IOException {
final String secret = requireEnvVar("SA_SECRET");
JsonObject authJson;
// Supporting both formats of SA_SECRET:
// - Local runs can point to a json file
// - Cloud Build has JSON value
try {
InputStream stream = Files.newInputStream(Paths.get(secret));
InputStreamReader reader = new InputStreamReader(stream);
authJson = JsonParser.parseReader(reader).getAsJsonObject();
} catch (IOException e) {
authJson = JsonParser.parseString(secret).getAsJsonObject();
}
assertTrue(authJson.has("client_email"));
assertTrue(authJson.has("private_key"));
assertTrue(authJson.has("project_id"));
return authJson;
}

private void validateConnection(String connection_uri) throws SQLException {
Connection connection = DriverManager.getConnection(connection_uri);
assertNotNull(connection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,23 @@
package com.google.cloud.bigquery.jdbc.it;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.ServiceOptions;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.jdbc.BigQueryJdbcBaseTest;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand Down Expand Up @@ -291,6 +302,31 @@ protected static String requireEnvVar(String varName) {
return value;
}

protected static JsonObject getAuthJson() throws IOException {
final String secret = requireEnvVar("SA_SECRET");
JsonObject authJson;
// Supporting both formats of SA_SECRET:
// - Local runs can point to a json file
// - Cloud Build has JSON value
try {
InputStream stream = Files.newInputStream(Paths.get(secret));
InputStreamReader reader = new InputStreamReader(stream);
authJson = JsonParser.parseReader(reader).getAsJsonObject();
} catch (IOException e) {
authJson = JsonParser.parseString(secret).getAsJsonObject();
}
assertTrue(authJson.has("client_email"));
assertTrue(authJson.has("private_key"));
assertTrue(authJson.has("project_id"));
return authJson;
}

protected static GoogleCredentials getCredentials() throws IOException {
JsonObject authJson = getAuthJson();
return GoogleCredentials.fromStream(
new ByteArrayInputStream(authJson.toString().getBytes(StandardCharsets.UTF_8)));
}

protected int resultSetRowCount(ResultSet resultSet) throws SQLException {
int rowCount = 0;
while (resultSet.next()) {
Expand Down
Loading
Loading