@@ -18,14 +18,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 2.45.1
+ 2.46.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 2.45.1
+ 2.46.0
pom
import
diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlags.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlags.java
index 4d18dd4c1d..f8dc326085 100644
--- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlags.java
+++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlags.java
@@ -202,6 +202,42 @@ public boolean getClientSideMetricsEnabled() {
return clientSideMetricsEnabled_;
}
+ public static final int TRAFFIC_DIRECTOR_ENABLED_FIELD_NUMBER = 9;
+ private boolean trafficDirectorEnabled_ = false;
+ /**
+ *
+ *
+ *
+ * Notify the server that the client using Traffic Director endpoint.
+ *
+ *
+ * bool traffic_director_enabled = 9;
+ *
+ * @return The trafficDirectorEnabled.
+ */
+ @java.lang.Override
+ public boolean getTrafficDirectorEnabled() {
+ return trafficDirectorEnabled_;
+ }
+
+ public static final int DIRECT_ACCESS_REQUESTED_FIELD_NUMBER = 10;
+ private boolean directAccessRequested_ = false;
+ /**
+ *
+ *
+ *
+ * Notify the server that the client explicitly opted in for Direct Access.
+ *
+ *
+ * bool direct_access_requested = 10;
+ *
+ * @return The directAccessRequested.
+ */
+ @java.lang.Override
+ public boolean getDirectAccessRequested() {
+ return directAccessRequested_;
+ }
+
private byte memoizedIsInitialized = -1;
@java.lang.Override
@@ -237,6 +273,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io
if (clientSideMetricsEnabled_ != false) {
output.writeBool(8, clientSideMetricsEnabled_);
}
+ if (trafficDirectorEnabled_ != false) {
+ output.writeBool(9, trafficDirectorEnabled_);
+ }
+ if (directAccessRequested_ != false) {
+ output.writeBool(10, directAccessRequested_);
+ }
getUnknownFields().writeTo(output);
}
@@ -267,6 +309,12 @@ public int getSerializedSize() {
if (clientSideMetricsEnabled_ != false) {
size += com.google.protobuf.CodedOutputStream.computeBoolSize(8, clientSideMetricsEnabled_);
}
+ if (trafficDirectorEnabled_ != false) {
+ size += com.google.protobuf.CodedOutputStream.computeBoolSize(9, trafficDirectorEnabled_);
+ }
+ if (directAccessRequested_ != false) {
+ size += com.google.protobuf.CodedOutputStream.computeBoolSize(10, directAccessRequested_);
+ }
size += getUnknownFields().getSerializedSize();
memoizedSize = size;
return size;
@@ -289,6 +337,8 @@ public boolean equals(final java.lang.Object obj) {
if (getRoutingCookie() != other.getRoutingCookie()) return false;
if (getRetryInfo() != other.getRetryInfo()) return false;
if (getClientSideMetricsEnabled() != other.getClientSideMetricsEnabled()) return false;
+ if (getTrafficDirectorEnabled() != other.getTrafficDirectorEnabled()) return false;
+ if (getDirectAccessRequested() != other.getDirectAccessRequested()) return false;
if (!getUnknownFields().equals(other.getUnknownFields())) return false;
return true;
}
@@ -314,6 +364,10 @@ public int hashCode() {
hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getRetryInfo());
hash = (37 * hash) + CLIENT_SIDE_METRICS_ENABLED_FIELD_NUMBER;
hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getClientSideMetricsEnabled());
+ hash = (37 * hash) + TRAFFIC_DIRECTOR_ENABLED_FIELD_NUMBER;
+ hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getTrafficDirectorEnabled());
+ hash = (37 * hash) + DIRECT_ACCESS_REQUESTED_FIELD_NUMBER;
+ hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getDirectAccessRequested());
hash = (29 * hash) + getUnknownFields().hashCode();
memoizedHashCode = hash;
return hash;
@@ -466,6 +520,8 @@ public Builder clear() {
routingCookie_ = false;
retryInfo_ = false;
clientSideMetricsEnabled_ = false;
+ trafficDirectorEnabled_ = false;
+ directAccessRequested_ = false;
return this;
}
@@ -522,6 +578,12 @@ private void buildPartial0(com.google.bigtable.v2.FeatureFlags result) {
if (((from_bitField0_ & 0x00000040) != 0)) {
result.clientSideMetricsEnabled_ = clientSideMetricsEnabled_;
}
+ if (((from_bitField0_ & 0x00000080) != 0)) {
+ result.trafficDirectorEnabled_ = trafficDirectorEnabled_;
+ }
+ if (((from_bitField0_ & 0x00000100) != 0)) {
+ result.directAccessRequested_ = directAccessRequested_;
+ }
}
@java.lang.Override
@@ -590,6 +652,12 @@ public Builder mergeFrom(com.google.bigtable.v2.FeatureFlags other) {
if (other.getClientSideMetricsEnabled() != false) {
setClientSideMetricsEnabled(other.getClientSideMetricsEnabled());
}
+ if (other.getTrafficDirectorEnabled() != false) {
+ setTrafficDirectorEnabled(other.getTrafficDirectorEnabled());
+ }
+ if (other.getDirectAccessRequested() != false) {
+ setDirectAccessRequested(other.getDirectAccessRequested());
+ }
this.mergeUnknownFields(other.getUnknownFields());
onChanged();
return this;
@@ -658,6 +726,18 @@ public Builder mergeFrom(
bitField0_ |= 0x00000040;
break;
} // case 64
+ case 72:
+ {
+ trafficDirectorEnabled_ = input.readBool();
+ bitField0_ |= 0x00000080;
+ break;
+ } // case 72
+ case 80:
+ {
+ directAccessRequested_ = input.readBool();
+ bitField0_ |= 0x00000100;
+ break;
+ } // case 80
default:
{
if (!super.parseUnknownField(input, extensionRegistry, tag)) {
@@ -1072,6 +1152,112 @@ public Builder clearClientSideMetricsEnabled() {
return this;
}
+ private boolean trafficDirectorEnabled_;
+ /**
+ *
+ *
+ *
+ * Notify the server that the client using Traffic Director endpoint.
+ *
+ *
+ * bool traffic_director_enabled = 9;
+ *
+ * @return The trafficDirectorEnabled.
+ */
+ @java.lang.Override
+ public boolean getTrafficDirectorEnabled() {
+ return trafficDirectorEnabled_;
+ }
+ /**
+ *
+ *
+ *
+ * Notify the server that the client using Traffic Director endpoint.
+ *
+ *
+ * bool traffic_director_enabled = 9;
+ *
+ * @param value The trafficDirectorEnabled to set.
+ * @return This builder for chaining.
+ */
+ public Builder setTrafficDirectorEnabled(boolean value) {
+
+ trafficDirectorEnabled_ = value;
+ bitField0_ |= 0x00000080;
+ onChanged();
+ return this;
+ }
+ /**
+ *
+ *
+ *
+ * Notify the server that the client using Traffic Director endpoint.
+ *
+ *
+ * bool traffic_director_enabled = 9;
+ *
+ * @return This builder for chaining.
+ */
+ public Builder clearTrafficDirectorEnabled() {
+ bitField0_ = (bitField0_ & ~0x00000080);
+ trafficDirectorEnabled_ = false;
+ onChanged();
+ return this;
+ }
+
+ private boolean directAccessRequested_;
+ /**
+ *
+ *
+ *
+ * Notify the server that the client explicitly opted in for Direct Access.
+ *
+ *
+ * bool direct_access_requested = 10;
+ *
+ * @return The directAccessRequested.
+ */
+ @java.lang.Override
+ public boolean getDirectAccessRequested() {
+ return directAccessRequested_;
+ }
+ /**
+ *
+ *
+ *
+ * Notify the server that the client explicitly opted in for Direct Access.
+ *
+ *
+ * bool direct_access_requested = 10;
+ *
+ * @param value The directAccessRequested to set.
+ * @return This builder for chaining.
+ */
+ public Builder setDirectAccessRequested(boolean value) {
+
+ directAccessRequested_ = value;
+ bitField0_ |= 0x00000100;
+ onChanged();
+ return this;
+ }
+ /**
+ *
+ *
+ *
+ * Notify the server that the client explicitly opted in for Direct Access.
+ *
+ *
+ * bool direct_access_requested = 10;
+ *
+ * @return This builder for chaining.
+ */
+ public Builder clearDirectAccessRequested() {
+ bitField0_ = (bitField0_ & ~0x00000100);
+ directAccessRequested_ = false;
+ onChanged();
+ return this;
+ }
+
@java.lang.Override
public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFields(unknownFields);
diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsOrBuilder.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsOrBuilder.java
index 2a61edabdb..a4f62d10eb 100644
--- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsOrBuilder.java
+++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsOrBuilder.java
@@ -122,4 +122,30 @@ public interface FeatureFlagsOrBuilder
* @return The clientSideMetricsEnabled.
*/
boolean getClientSideMetricsEnabled();
+
+ /**
+ *
+ *
+ *
+ * Notify the server that the client using Traffic Director endpoint.
+ *
+ *
+ * bool traffic_director_enabled = 9;
+ *
+ * @return The trafficDirectorEnabled.
+ */
+ boolean getTrafficDirectorEnabled();
+
+ /**
+ *
+ *
+ *
+ * Notify the server that the client explicitly opted in for Direct Access.
+ *
+ *
+ * bool direct_access_requested = 10;
+ *
+ * @return The directAccessRequested.
+ */
+ boolean getDirectAccessRequested();
}
diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsProto.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsProto.java
index e5875d27ee..78a36f7647 100644
--- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsProto.java
+++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/FeatureFlagsProto.java
@@ -42,17 +42,19 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
static {
java.lang.String[] descriptorData = {
"\n&google/bigtable/v2/feature_flags.proto"
- + "\022\022google.bigtable.v2\"\333\001\n\014FeatureFlags\022\025\n"
+ + "\022\022google.bigtable.v2\"\236\002\n\014FeatureFlags\022\025\n"
+ "\rreverse_scans\030\001 \001(\010\022\036\n\026mutate_rows_rate"
+ "_limit\030\003 \001(\010\022\037\n\027mutate_rows_rate_limit2\030"
+ "\005 \001(\010\022\"\n\032last_scanned_row_responses\030\004 \001("
+ "\010\022\026\n\016routing_cookie\030\006 \001(\010\022\022\n\nretry_info\030"
+ "\007 \001(\010\022#\n\033client_side_metrics_enabled\030\010 \001"
- + "(\010B\273\001\n\026com.google.bigtable.v2B\021FeatureFl"
- + "agsProtoP\001Z8cloud.google.com/go/bigtable"
- + "/apiv2/bigtablepb;bigtablepb\252\002\030Google.Cl"
- + "oud.Bigtable.V2\312\002\030Google\\Cloud\\Bigtable\\"
- + "V2\352\002\033Google::Cloud::Bigtable::V2b\006proto3"
+ + "(\010\022 \n\030traffic_director_enabled\030\t \001(\010\022\037\n\027"
+ + "direct_access_requested\030\n \001(\010B\273\001\n\026com.go"
+ + "ogle.bigtable.v2B\021FeatureFlagsProtoP\001Z8c"
+ + "loud.google.com/go/bigtable/apiv2/bigtab"
+ + "lepb;bigtablepb\252\002\030Google.Cloud.Bigtable."
+ + "V2\312\002\030Google\\Cloud\\Bigtable\\V2\352\002\033Google::"
+ + "Cloud::Bigtable::V2b\006proto3"
};
descriptor =
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
@@ -70,6 +72,8 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
"RoutingCookie",
"RetryInfo",
"ClientSideMetricsEnabled",
+ "TrafficDirectorEnabled",
+ "DirectAccessRequested",
});
}
diff --git a/proto-google-cloud-bigtable-v2/src/main/proto/google/bigtable/v2/feature_flags.proto b/proto-google-cloud-bigtable-v2/src/main/proto/google/bigtable/v2/feature_flags.proto
index e97f23e15a..d4c3bdbd71 100644
--- a/proto-google-cloud-bigtable-v2/src/main/proto/google/bigtable/v2/feature_flags.proto
+++ b/proto-google-cloud-bigtable-v2/src/main/proto/google/bigtable/v2/feature_flags.proto
@@ -61,4 +61,10 @@ message FeatureFlags {
// Notify the server that the client has client side metrics enabled.
bool client_side_metrics_enabled = 8;
+
+ // Notify the server that the client using Traffic Director endpoint.
+ bool traffic_director_enabled = 9;
+
+ // Notify the server that the client explicitly opted in for Direct Access.
+ bool direct_access_requested = 10;
}
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 71414af391..e925783962 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-bigtable
- 2.45.1
+ 2.46.0
diff --git a/test-proxy/pom.xml b/test-proxy/pom.xml
index f351f56a97..acc7936844 100644
--- a/test-proxy/pom.xml
+++ b/test-proxy/pom.xml
@@ -12,11 +12,11 @@
google-cloud-bigtable-parent
com.google.cloud
- 2.45.1
+ 2.46.0
- 2.45.1
+ 2.46.0
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
index 6e563d4df0..16b5c8257c 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Google LLC
+ * Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,8 @@
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ServerStream;
-import com.google.auth.oauth2.GoogleCredentials;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.OAuth2Credentials;
import com.google.auto.value.AutoValue;
import com.google.bigtable.v2.Column;
import com.google.bigtable.v2.Family;
@@ -41,6 +42,7 @@
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
import com.google.cloud.bigtable.data.v2.models.RowCell;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
+import com.google.cloud.bigtable.data.v2.models.sql.ResultSet;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings;
import com.google.cloud.bigtable.testproxy.CloudBigtableV2TestProxyGrpc.CloudBigtableV2TestProxyImplBase;
import com.google.common.base.Preconditions;
@@ -50,6 +52,7 @@
import io.grpc.ManagedChannelBuilder;
import io.grpc.Status;
import io.grpc.StatusException;
+import io.grpc.StatusRuntimeException;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
@@ -57,19 +60,16 @@
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import javax.annotation.Nullable;
import org.threeten.bp.Duration;
/** Java implementation of the CBT test proxy. Used to test the Java CBT client. */
@@ -92,50 +92,13 @@ static CbtClient create(BigtableDataSettings settings, BigtableDataClient dataCl
private static final Logger logger = Logger.getLogger(CbtTestProxy.class.getName());
- private CbtTestProxy(
- boolean encrypted,
- @Nullable String rootCerts,
- @Nullable String sslTarget,
- @Nullable String credential) {
- this.encrypted = encrypted;
- this.rootCerts = rootCerts;
- this.sslTarget = sslTarget;
- this.credential = credential;
+ private CbtTestProxy() {
this.idClientMap = new ConcurrentHashMap<>();
}
- /**
- * Factory method to return a proxy instance that interacts with server unencrypted and
- * unauthenticated.
- */
- public static CbtTestProxy createUnencrypted() {
- return new CbtTestProxy(false, null, null, null);
- }
-
- /**
- * Factory method to return a proxy instance that interacts with server encrypted. Default
- * authority and public certificates are used if null values are passed in.
- *
- * @param rootCertsPemPath The path to a root certificate PEM file
- * @param sslTarget The override of SSL target name
- * @param credentialJsonPath The path to a credential JSON file
- */
- public static CbtTestProxy createEncrypted(
- @Nullable String rootCertsPemPath,
- @Nullable String sslTarget,
- @Nullable String credentialJsonPath)
- throws IOException {
- String tmpRootCerts = null, tmpCredential = null;
- if (rootCertsPemPath != null) {
- Path file = Paths.get(rootCertsPemPath);
- tmpRootCerts = new String(Files.readAllBytes(file), UTF_8);
- }
- if (credentialJsonPath != null) {
- Path file = Paths.get(credentialJsonPath);
- tmpCredential = new String(Files.readAllBytes(file), UTF_8);
- }
-
- return new CbtTestProxy(true, tmpRootCerts, sslTarget, tmpCredential);
+ /** Factory method to return a proxy instance. */
+ public static CbtTestProxy create() {
+ return new CbtTestProxy();
}
/**
@@ -159,6 +122,8 @@ private static BigtableDataSettings.Builder overrideTimeoutSetting(
settingsBuilder.stubSettings().readModifyWriteRowSettings().retrySettings(), newTimeout);
updateTimeout(
settingsBuilder.stubSettings().sampleRowKeysSettings().retrySettings(), newTimeout);
+ updateTimeout(
+ settingsBuilder.stubSettings().executeQuerySettings().retrySettings(), newTimeout);
return settingsBuilder;
}
@@ -191,8 +156,12 @@ public synchronized void createClient(
Preconditions.checkArgument(!request.getProjectId().isEmpty(), "project id must be provided");
Preconditions.checkArgument(!request.getInstanceId().isEmpty(), "instance id must be provided");
Preconditions.checkArgument(!request.getDataTarget().isEmpty(), "data target must be provided");
+ Preconditions.checkArgument(
+ !request.getSecurityOptions().getUseSsl()
+ || !request.getSecurityOptions().getSslRootCertsPemBytes().isEmpty(),
+ "security_options.ssl_root_certs_pem must be provided if security_options.use_ssl is true");
- if (idClientMap.contains(request.getClientId())) {
+ if (idClientMap.containsKey(request.getClientId())) {
responseObserver.onError(
Status.ALREADY_EXISTS
.withDescription("Client " + request.getClientId() + " already exists.")
@@ -200,6 +169,8 @@ public synchronized void createClient(
return;
}
+ // setRefreshingChannel is needed for now.
+ @SuppressWarnings("deprecation")
BigtableDataSettings.Builder settingsBuilder =
BigtableDataSettings.newBuilder()
// Disable channel refreshing when not using the real server
@@ -208,9 +179,6 @@ public synchronized void createClient(
.setInstanceId(request.getInstanceId())
.setAppProfileId(request.getAppProfileId());
- settingsBuilder.stubSettings().setEnableRoutingCookie(false);
- settingsBuilder.stubSettings().setEnableRetryInfo(false);
-
if (request.hasPerOperationTimeout()) {
Duration newTimeout = Duration.ofMillis(Durations.toMillis(request.getPerOperationTimeout()));
settingsBuilder = overrideTimeoutSetting(newTimeout, settingsBuilder);
@@ -244,8 +212,13 @@ public synchronized void createClient(
settingsBuilder
.stubSettings()
.setEndpoint(request.getDataTarget())
- .setTransportChannelProvider(getTransportChannel())
- .setCredentialsProvider(getCredentialsProvider());
+ .setTransportChannelProvider(
+ getTransportChannel(
+ request.getSecurityOptions().getUseSsl(),
+ request.getSecurityOptions().getSslRootCertsPem(),
+ request.getSecurityOptions().getSslEndpointOverride()))
+ .setCredentialsProvider(
+ getCredentialsProvider(request.getSecurityOptions().getAccessToken()));
}
BigtableDataSettings settings = settingsBuilder.build();
BigtableDataClient client = BigtableDataClient.create(settings);
@@ -698,6 +671,64 @@ public void readModifyWriteRow(
responseObserver.onCompleted();
}
+ @Override
+ public void executeQuery(
+ ExecuteQueryRequest request, StreamObserver responseObserver) {
+ CbtClient client;
+ try {
+ client = getClient(request.getClientId());
+ } catch (StatusException e) {
+ responseObserver.onError(e);
+ return;
+ }
+ try (ResultSet resultSet =
+ client.dataClient().executeQuery(StatementDeserializer.toStatement(request))) {
+ responseObserver.onNext(ResultSetSerializer.toExecuteQueryResult(resultSet));
+ } catch (InterruptedException e) {
+ responseObserver.onError(e);
+ return;
+ } catch (ExecutionException e) {
+ responseObserver.onError(e);
+ return;
+ } catch (ApiException e) {
+ responseObserver.onNext(
+ ExecuteQueryResult.newBuilder()
+ .setStatus(
+ com.google.rpc.Status.newBuilder()
+ .setCode(e.getStatusCode().getCode().ordinal())
+ .setMessage(e.getMessage())
+ .build())
+ .build());
+ responseObserver.onCompleted();
+ return;
+ } catch (StatusRuntimeException e) {
+ responseObserver.onNext(
+ ExecuteQueryResult.newBuilder()
+ .setStatus(
+ com.google.rpc.Status.newBuilder()
+ .setCode(e.getStatus().getCode().value())
+ .setMessage(e.getStatus().getDescription())
+ .build())
+ .build());
+ responseObserver.onCompleted();
+ return;
+ } catch (RuntimeException e) {
+ // If client encounters problem, don't return any results.
+ responseObserver.onNext(
+ ExecuteQueryResult.newBuilder()
+ .setStatus(
+ com.google.rpc.Status.newBuilder()
+ .setCode(Code.INTERNAL.getNumber())
+ .setMessage(e.getMessage())
+ .build())
+ .build());
+ responseObserver.onCompleted();
+ return;
+ }
+ responseObserver.onCompleted();
+ return;
+ }
+
@Override
public synchronized void close() {
Iterator> it = idClientMap.entrySet().iterator();
@@ -717,52 +748,60 @@ private static String extractTableIdFromTableName(String fullTableName)
return matcher.group(3);
}
- private InstantiatingGrpcChannelProvider getTransportChannel() throws IOException {
+ @SuppressWarnings("rawtypes")
+ private InstantiatingGrpcChannelProvider getTransportChannel(
+ boolean encrypted, String rootCertsPem, String sslTarget) {
if (!encrypted) {
return EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder()
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
.build();
}
- if (rootCerts == null) {
- return EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder().build();
+ final SslContext sslContext;
+ if (rootCertsPem.isEmpty()) {
+ sslContext = null;
+ } else {
+ try {
+ sslContext =
+ GrpcSslContexts.forClient()
+ .trustManager(new ByteArrayInputStream(rootCertsPem.getBytes(UTF_8)))
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
}
- final SslContext secureContext =
- GrpcSslContexts.forClient()
- .trustManager(new ByteArrayInputStream(rootCerts.getBytes(UTF_8)))
- .build();
return EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder()
.setChannelConfigurator(
new ApiFunction() {
@Override
public ManagedChannelBuilder apply(ManagedChannelBuilder input) {
NettyChannelBuilder channelBuilder = (NettyChannelBuilder) input;
- channelBuilder.sslContext(secureContext).overrideAuthority(sslTarget);
+
+ if (sslContext != null) {
+ channelBuilder.sslContext(sslContext);
+ }
+
+ if (!sslTarget.isEmpty()) {
+ channelBuilder.overrideAuthority(sslTarget);
+ }
+
return channelBuilder;
}
})
.build();
}
- private CredentialsProvider getCredentialsProvider() throws IOException {
- if (credential == null) {
+ private CredentialsProvider getCredentialsProvider(String accessToken) {
+ if (accessToken.isEmpty()) {
return NoCredentialsProvider.create();
}
- final GoogleCredentials creds =
- GoogleCredentials.fromStream(new ByteArrayInputStream(credential.getBytes(UTF_8)));
-
- return FixedCredentialsProvider.create(creds);
+ return FixedCredentialsProvider.create(
+ OAuth2Credentials.create(new AccessToken(accessToken, null)));
}
private final ConcurrentHashMap idClientMap;
- private final boolean encrypted;
-
- // Parameters that may be needed when "encrypted" is true.
- private final String rootCerts;
- private final String sslTarget;
- private final String credential;
private static final Pattern tablePattern =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)");
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxyMain.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxyMain.java
index 8750909f1a..f817197d14 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxyMain.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxyMain.java
@@ -32,19 +32,7 @@ public static void main(String[] args) throws InterruptedException, IOException
throw new IllegalArgumentException(String.format("Port %d is not > 0.", port));
}
- CbtTestProxy cbtTestProxy;
-
- // If encryption is specified
- boolean encrypted = Boolean.getBoolean("encrypted");
- if (encrypted) {
- String rootCertsPemPath = System.getProperty("root.certs.pem.path");
- String sslTarget = System.getProperty("ssl.target");
- String credentialJsonPath = System.getProperty("credential.json.path");
- cbtTestProxy = CbtTestProxy.createEncrypted(rootCertsPemPath, sslTarget, credentialJsonPath);
- } else {
- cbtTestProxy = CbtTestProxy.createUnencrypted();
- }
-
+ CbtTestProxy cbtTestProxy = CbtTestProxy.create();
logger.info(String.format("Test proxy starting on %d", port));
ServerBuilder.forPort(port).addService(cbtTestProxy).build().start().awaitTermination();
}
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java
new file mode 100644
index 0000000000..c138c82a6b
--- /dev/null
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.testproxy;
+
+import com.google.bigtable.v2.ArrayValue;
+import com.google.bigtable.v2.Type;
+import com.google.bigtable.v2.Type.Array;
+import com.google.bigtable.v2.Type.Bool;
+import com.google.bigtable.v2.Type.Bytes;
+import com.google.bigtable.v2.Type.Float32;
+import com.google.bigtable.v2.Type.Float64;
+import com.google.bigtable.v2.Type.Int64;
+import com.google.bigtable.v2.Type.Map;
+import com.google.bigtable.v2.Type.Struct;
+import com.google.bigtable.v2.Type.Timestamp;
+import com.google.bigtable.v2.Value;
+import com.google.cloud.Date;
+import com.google.cloud.bigtable.data.v2.models.sql.ColumnMetadata;
+import com.google.cloud.bigtable.data.v2.models.sql.ResultSet;
+import com.google.cloud.bigtable.data.v2.models.sql.SqlType;
+import com.google.cloud.bigtable.data.v2.models.sql.StructReader;
+import com.google.protobuf.ByteString;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.threeten.bp.Instant;
+
+public class ResultSetSerializer {
+ public static ExecuteQueryResult toExecuteQueryResult(ResultSet resultSet)
+ throws ExecutionException, InterruptedException {
+ ExecuteQueryResult.Builder resultBuilder = ExecuteQueryResult.newBuilder();
+ for (ColumnMetadata columnMetadata : resultSet.getMetadata().getColumns()) {
+ resultBuilder
+ .getMetadataBuilder()
+ .addColumnsBuilder()
+ .setName(columnMetadata.name())
+ .setType(toProtoType(columnMetadata.type()));
+ }
+
+ while (resultSet.next()) {
+ SqlRow.Builder rowBuilder = resultBuilder.addRowsBuilder();
+
+ for (int i = 0; i < resultSet.getMetadata().getColumns().size(); i++) {
+ SqlType> colType = resultSet.getMetadata().getColumnType(i);
+ rowBuilder.addValues(toProtoValue(getColumn(resultSet, i, colType), colType));
+ }
+ }
+
+ return resultBuilder.build();
+ }
+
+ private static Value toProtoValue(Object value, SqlType> type) {
+ if (value == null) {
+ return Value.getDefaultInstance();
+ }
+
+ Value.Builder valueBuilder = Value.newBuilder();
+ switch (type.getCode()) {
+ case BYTES:
+ valueBuilder.setBytesValue((ByteString) value);
+ break;
+ case STRING:
+ valueBuilder.setStringValue((String) value);
+ break;
+ case INT64:
+ valueBuilder.setIntValue((Long) value);
+ break;
+ case FLOAT32:
+ valueBuilder.setFloatValue((Float) value);
+ break;
+ case FLOAT64:
+ valueBuilder.setFloatValue((Double) value);
+ break;
+ case BOOL:
+ valueBuilder.setBoolValue((Boolean) value);
+ break;
+ case TIMESTAMP:
+ Instant ts = (Instant) value;
+ valueBuilder.setTimestampValue(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(ts.getEpochSecond())
+ .setNanos(ts.getNano())
+ .build());
+ break;
+ case DATE:
+ Date date = (Date) value;
+ valueBuilder.setDateValue(
+ com.google.type.Date.newBuilder()
+ .setYear(date.getYear())
+ .setMonth(date.getMonth())
+ .setDay(date.getDayOfMonth())
+ .build());
+ break;
+ case ARRAY:
+ SqlType> elementType = ((SqlType.Array>) type).getElementType();
+ ArrayValue.Builder arrayValue = ArrayValue.newBuilder();
+ for (Object item : (List>) value) {
+ arrayValue.addValues(toProtoValue(item, elementType));
+ }
+ valueBuilder.setArrayValue(arrayValue.build());
+ break;
+ case MAP:
+ SqlType.Map, ?> mapType = (SqlType.Map, ?>) type;
+ SqlType> mapKeyType = mapType.getKeyType();
+ SqlType> mapValueType = mapType.getValueType();
+
+ ArrayValue.Builder mapArrayValue = ArrayValue.newBuilder();
+ ((java.util.Map, ?>) value)
+ .forEach(
+ (k, v) ->
+ mapArrayValue.addValues(
+ Value.newBuilder()
+ .setArrayValue(
+ ArrayValue.newBuilder()
+ .addValues(toProtoValue(k, mapKeyType))
+ .addValues(toProtoValue(v, mapValueType))
+ .build())));
+ valueBuilder.setArrayValue(mapArrayValue.build());
+ break;
+ case STRUCT:
+ StructReader structValue = (StructReader) value;
+ SqlType.Struct structType = (SqlType.Struct) type;
+ ArrayValue.Builder structArrayValue = ArrayValue.newBuilder();
+ for (int i = 0; i < structType.getFields().size(); ++i) {
+ SqlType> fieldType = structType.getType(i);
+ structArrayValue.addValues(toProtoValue(getColumn(structValue, i, fieldType), fieldType));
+ }
+ valueBuilder.setArrayValue(structArrayValue);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected Type: " + type);
+ }
+
+ return valueBuilder.build();
+ }
+
+ private static Object getColumn(StructReader struct, int fieldIndex, SqlType> fieldType) {
+ if (struct.isNull(fieldIndex)) {
+ return null;
+ }
+
+ switch (fieldType.getCode()) {
+ case ARRAY:
+ return struct.getList(fieldIndex, (SqlType.Array>) fieldType);
+ case BOOL:
+ return struct.getBoolean(fieldIndex);
+ case BYTES:
+ return struct.getBytes(fieldIndex);
+ case DATE:
+ return struct.getDate(fieldIndex);
+ case FLOAT32:
+ return struct.getFloat(fieldIndex);
+ case FLOAT64:
+ return struct.getDouble(fieldIndex);
+ case INT64:
+ return struct.getLong(fieldIndex);
+ case MAP:
+ return struct.getMap(fieldIndex, (SqlType.Map, ?>) fieldType);
+ case STRING:
+ return struct.getString(fieldIndex);
+ case STRUCT:
+ return struct.getStruct(fieldIndex);
+ case TIMESTAMP:
+ return struct.getTimestamp(fieldIndex);
+ default:
+ throw new IllegalStateException("Unexpected Type: " + fieldType);
+ }
+ }
+
+ private static Type toProtoType(SqlType> type) {
+ switch (type.getCode()) {
+ case BYTES:
+ return Type.newBuilder().setBytesType(Bytes.getDefaultInstance()).build();
+ case STRING:
+ return Type.newBuilder()
+ .setStringType(com.google.bigtable.v2.Type.String.getDefaultInstance())
+ .build();
+ case INT64:
+ return Type.newBuilder().setInt64Type(Int64.getDefaultInstance()).build();
+ case FLOAT32:
+ return Type.newBuilder().setFloat32Type(Float32.getDefaultInstance()).build();
+ case FLOAT64:
+ return Type.newBuilder().setFloat64Type(Float64.getDefaultInstance()).build();
+ case BOOL:
+ return Type.newBuilder().setBoolType(Bool.getDefaultInstance()).build();
+ case TIMESTAMP:
+ return Type.newBuilder().setTimestampType(Timestamp.getDefaultInstance()).build();
+ case DATE:
+ return Type.newBuilder()
+ .setDateType(com.google.bigtable.v2.Type.Date.getDefaultInstance())
+ .build();
+ case ARRAY:
+ SqlType.Array> arrayType = (SqlType.Array>) type;
+ return Type.newBuilder()
+ .setArrayType(
+ Array.newBuilder().setElementType(toProtoType(arrayType.getElementType())))
+ .build();
+ case MAP:
+ SqlType.Map, ?> mapType = (SqlType.Map, ?>) type;
+ return Type.newBuilder()
+ .setMapType(
+ Map.newBuilder()
+ .setKeyType(toProtoType(mapType.getKeyType()))
+ .setValueType(toProtoType(mapType.getValueType())))
+ .build();
+ case STRUCT:
+ SqlType.Struct structType = (SqlType.Struct) type;
+ Struct.Builder structBuilder = Struct.newBuilder();
+ for (SqlType.Struct.Field field : structType.getFields()) {
+ structBuilder
+ .addFieldsBuilder()
+ .setFieldName(field.name())
+ .setType(toProtoType(field.type()));
+ }
+ return Type.newBuilder().setStructType(structBuilder.build()).build();
+
+ default:
+ throw new IllegalStateException("Unexpected Type: " + type);
+ }
+ }
+}
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java
new file mode 100644
index 0000000000..ae3b50aa7f
--- /dev/null
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.testproxy;
+
+import com.google.bigtable.v2.Value;
+import com.google.bigtable.v2.Value.KindCase;
+import com.google.cloud.Date;
+import com.google.cloud.bigtable.data.v2.models.sql.SqlType;
+import com.google.cloud.bigtable.data.v2.models.sql.Statement;
+import com.google.protobuf.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.threeten.bp.Instant;
+
+public class StatementDeserializer {
+
+ static Statement toStatement(ExecuteQueryRequest request) {
+ Statement.Builder statementBuilder = Statement.newBuilder(request.getRequest().getQuery());
+ for (Map.Entry paramEntry : request.getRequest().getParamsMap().entrySet()) {
+ String name = paramEntry.getKey();
+ Value value = paramEntry.getValue();
+ switch (value.getType().getKindCase()) {
+ case BYTES_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setBytesParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.BYTES_VALUE)) {
+ statementBuilder.setBytesParam(name, value.getBytesValue());
+ } else {
+ throw new IllegalArgumentException("Unexpected bytes value: " + value);
+ }
+ break;
+ case STRING_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setStringParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.STRING_VALUE)) {
+ statementBuilder.setStringParam(name, value.getStringValue());
+ } else {
+ throw new IllegalArgumentException("Malformed string value: " + value);
+ }
+ break;
+ case INT64_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setLongParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.INT_VALUE)) {
+ statementBuilder.setLongParam(name, value.getIntValue());
+ } else {
+ throw new IllegalArgumentException("Malformed int64 value: " + value);
+ }
+ break;
+ case FLOAT32_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setFloatParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.FLOAT_VALUE)) {
+ statementBuilder.setFloatParam(name, (float) value.getFloatValue());
+ } else {
+ throw new IllegalArgumentException("Malformed float32 value: " + value);
+ }
+ break;
+ case FLOAT64_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setDoubleParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.FLOAT_VALUE)) {
+ statementBuilder.setDoubleParam(name, value.getFloatValue());
+ } else {
+ throw new IllegalArgumentException("Malformed float64 value: " + value);
+ }
+ break;
+ case BOOL_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setBooleanParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.BOOL_VALUE)) {
+ statementBuilder.setBooleanParam(name, value.getBoolValue());
+ } else {
+ throw new IllegalArgumentException("Malformed boolean value: " + value);
+ }
+ break;
+ case TIMESTAMP_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setTimestampParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.TIMESTAMP_VALUE)) {
+ Timestamp ts = value.getTimestampValue();
+ statementBuilder.setTimestampParam(name, toInstant(ts));
+ } else {
+ throw new IllegalArgumentException("Malformed timestamp value: " + value);
+ }
+ break;
+ case DATE_TYPE:
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setDateParam(name, null);
+ } else if (value.getKindCase().equals(KindCase.DATE_VALUE)) {
+ com.google.type.Date protoDate = value.getDateValue();
+ statementBuilder.setDateParam(name, fromProto(protoDate));
+ } else {
+ throw new IllegalArgumentException("Malformed boolean value: " + value);
+ }
+ break;
+ case ARRAY_TYPE:
+ SqlType.Array sqlType = (SqlType.Array) SqlType.fromProto(value.getType());
+ if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) {
+ statementBuilder.setListParam(name, null, sqlType);
+ } else if (value.getKindCase().equals(KindCase.ARRAY_VALUE)) {
+ List