From 042616506e9bb15a930a033d895276156848e85f Mon Sep 17 00:00:00 2001 From: Patrick Wrobel Date: Fri, 21 Jun 2024 14:17:51 -0400 Subject: [PATCH 1/3] test: Test proxy support SSL backend and ExecuteQuery Change-Id: I39f81dcf098b93eff79973648c57515e8908fde3 --- .../bigtable/testproxy/CbtTestProxy.java | 160 ++++++------ .../bigtable/testproxy/CbtTestProxyMain.java | 14 +- .../testproxy/ResultSetSerializer.java | 241 ++++++++++++++++++ test-proxy/src/main/proto/test_proxy.proto | 66 ++++- 4 files changed, 395 insertions(+), 86 deletions(-) create mode 100644 test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java 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..0859b2b50f 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 @@ -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,8 @@ 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.models.sql.Statement; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import com.google.cloud.bigtable.testproxy.CloudBigtableV2TestProxyGrpc.CloudBigtableV2TestProxyImplBase; import com.google.common.base.Preconditions; @@ -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(); } /** @@ -187,12 +150,17 @@ private CbtClient getClient(String id) throws StatusException { @Override public synchronized void createClient( CreateClientRequest request, StreamObserver responseObserver) { + Preconditions.checkArgument(!request.getClientId().isEmpty(), "client id must be provided"); 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 +168,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 +178,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 +211,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 +670,42 @@ 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(Statement.of(request.getRequest().getQuery()))) { + responseObserver.onNext(ResultSetSerializer.toExecuteQueryResult(resultSet)); + } catch (InterruptedException e) { + responseObserver.onError(e); + } catch (ExecutionException e) { + responseObserver.onError(e); + } catch (ApiException e) { + responseObserver.onNext( + ExecuteQueryResult.newBuilder() + .setStatus( + com.google.rpc.Status.newBuilder() + .setCode(e.getStatusCode().getCode().ordinal()) + .setMessage(e.getMessage()) + .build()) + .build()); + } catch (RuntimeException e) { + responseObserver.onError(e); + } finally { + responseObserver.onCompleted(); + } + + return; + } + @Override public synchronized void close() { Iterator> it = idClientMap.entrySet().iterator(); @@ -717,52 +725,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..966e688cd8 --- /dev/null +++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java @@ -0,0 +1,241 @@ +/* + * 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().setBytesType(Bytes.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/proto/test_proxy.proto b/test-proxy/src/main/proto/test_proxy.proto index e7caef0e7b..0c9b64b8cd 100644 --- a/test-proxy/src/main/proto/test_proxy.proto +++ b/test-proxy/src/main/proto/test_proxy.proto @@ -1,4 +1,4 @@ -// Copyright 2023 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. @@ -38,6 +38,27 @@ enum OptionalFeatureConfig { // Request to test proxy service to create a client object. message CreateClientRequest { + message SecurityOptions { + // Access token to use for client credentials. If empty, the client will not + // use any call credentials. Certain implementations may require `use_ssl` + // to be set when using this. + string access_token = 1; + + // Whether to use SSL channel credentials when connecting to the data + // endpoint. + bool use_ssl = 2; + + // If using SSL channel credentials, override the SSL endpoint to match the + // host that is specified in the backend's certificate. Also sets the + // client's authority header value. + string ssl_endpoint_override = 3; + + // PEM encoding of the server root certificates. If not set, the default + // root certs will be used instead. The default can be overridden via the + // GRPC_DEFAULT_SSL_ROOTS_FILE_PATH env var. + string ssl_root_certs_pem = 4; + } + // A unique ID associated with the client object to be created. string client_id = 1; @@ -66,6 +87,17 @@ message CreateClientRequest { // Optional config that dictates how the optional features should be enabled // during the client creation. Please check the enum type's docstring above. OptionalFeatureConfig optional_feature_config = 7; + + // Options to allow connecting to backends with channel and/or call + // credentials. This is needed internally by Cloud Bigtable's own testing + // frameworks.It is not necessary to support these fields for client + // conformance testing. + // + // WARNING: this allows the proxy to connect to a real production + // CBT backend with the right options, however, the proxy itself is insecure + // so it is not recommended to use it with real credentials or outside testing + // contexts. + SecurityOptions security_options = 8; } // Response from test proxy service for CreateClientRequest. @@ -217,6 +249,35 @@ message ReadModifyWriteRowRequest { google.bigtable.v2.ReadModifyWriteRowRequest request = 2; } +message ExecuteQueryRequest { + // The ID of the target client object. + string client_id = 1; + + google.bigtable.v2.ExecuteQueryRequest request = 2; +} + +message ExecuteQueryResult { + // The RPC status from the client binding. + google.rpc.Status status = 1; + + google.bigtable.v2.ResultSetMetadata result_set_metadata = 2; // deprecated + + // Name and type information for the query result. + ResultSetMetadata metadata = 4; + + // Encoded version of the ResultSet. Should not contain type information. + repeated SqlRow rows = 3; +} + +message ResultSetMetadata { + repeated google.bigtable.v2.ColumnMetadata columns = 1; +} + +message SqlRow { + // Columnar values returned by the query. + repeated google.bigtable.v2.Value values = 1; +} + // Note that all RPCs are unary, even when the equivalent client binding call // may be streaming. This is an intentional simplification. // @@ -279,4 +340,7 @@ service CloudBigtableV2TestProxy { // Performs a read-modify-write operation with the client. rpc ReadModifyWriteRow(ReadModifyWriteRowRequest) returns (RowResult) {} + + // Executes a BTQL query with the client. + rpc ExecuteQuery(ExecuteQueryRequest) returns (ExecuteQueryResult) {} } From 52832b2bcd86e82e051f5e61b8c190c9110192bd Mon Sep 17 00:00:00 2001 From: Jack Dingilian Date: Mon, 15 Jul 2024 14:53:52 -0400 Subject: [PATCH 2/3] test: Support query params in test proxy Change-Id: I53122fdf0301fb41d4f24881dc8de65d174dee35 --- .../bigtable/testproxy/CbtTestProxy.java | 36 +++- .../testproxy/ResultSetSerializer.java | 14 +- .../testproxy/StatementDeserializer.java | 167 ++++++++++++++++++ test-proxy/src/main/proto/test_proxy.proto | 9 +- 4 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java 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 0859b2b50f..068fe54f76 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 @@ -43,7 +43,6 @@ 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.models.sql.Statement; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import com.google.cloud.bigtable.testproxy.CloudBigtableV2TestProxyGrpc.CloudBigtableV2TestProxyImplBase; import com.google.common.base.Preconditions; @@ -53,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; @@ -122,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; } @@ -680,14 +682,15 @@ public void executeQuery( responseObserver.onError(e); return; } - try (ResultSet resultSet = - client.dataClient().executeQuery(Statement.of(request.getRequest().getQuery()))) { + 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() @@ -697,12 +700,33 @@ public void executeQuery( .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) { - responseObserver.onError(e); - } finally { + // 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; } 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 index 966e688cd8..c138c82a6b 100644 --- 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 @@ -71,26 +71,21 @@ private static Value toProtoValue(Object value, SqlType type) { 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( @@ -99,7 +94,6 @@ private static Value toProtoValue(Object value, SqlType type) { .setNanos(ts.getNano()) .build()); break; - case DATE: Date date = (Date) value; valueBuilder.setDateValue( @@ -109,7 +103,6 @@ private static Value toProtoValue(Object value, SqlType type) { .setDay(date.getDayOfMonth()) .build()); break; - case ARRAY: SqlType elementType = ((SqlType.Array) type).getElementType(); ArrayValue.Builder arrayValue = ArrayValue.newBuilder(); @@ -118,7 +111,6 @@ private static Value toProtoValue(Object value, SqlType type) { } valueBuilder.setArrayValue(arrayValue.build()); break; - case MAP: SqlType.Map mapType = (SqlType.Map) type; SqlType mapKeyType = mapType.getKeyType(); @@ -137,7 +129,6 @@ private static Value toProtoValue(Object value, SqlType type) { .build()))); valueBuilder.setArrayValue(mapArrayValue.build()); break; - case STRUCT: StructReader structValue = (StructReader) value; SqlType.Struct structType = (SqlType.Struct) type; @@ -148,7 +139,6 @@ private static Value toProtoValue(Object value, SqlType type) { } valueBuilder.setArrayValue(structArrayValue); break; - default: throw new IllegalStateException("Unexpected Type: " + type); } @@ -208,7 +198,9 @@ private static Type toProtoType(SqlType type) { case TIMESTAMP: return Type.newBuilder().setTimestampType(Timestamp.getDefaultInstance()).build(); case DATE: - return Type.newBuilder().setBytesType(Bytes.getDefaultInstance()).build(); + return Type.newBuilder() + .setDateType(com.google.bigtable.v2.Type.Date.getDefaultInstance()) + .build(); case ARRAY: SqlType.Array arrayType = (SqlType.Array) type; return Type.newBuilder() 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 array = new ArrayList<>(); + for (Value elem : value.getArrayValue().getValuesList()) { + array.add(decodeArrayElement(elem, sqlType.getElementType())); + } + statementBuilder.setListParam(name, array, sqlType); + } else { + throw new IllegalArgumentException("Malformed array value: " + value); + } + break; + default: + throw new IllegalArgumentException("Unexpected query param type in param: " + value); + } + } + return statementBuilder.build(); + } + + static Object decodeArrayElement(Value value, SqlType elemType) { + if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) { + return null; + } + switch (elemType.getCode()) { + case BYTES: + return value.getBytesValue(); + case STRING: + return value.getStringValue(); + case INT64: + return value.getIntValue(); + case FLOAT64: + return value.getFloatValue(); + case FLOAT32: + // cast to float so we produce List, etc + return (float) value.getFloatValue(); + case BOOL: + return value.getBoolValue(); + case TIMESTAMP: + return toInstant(value.getTimestampValue()); + case DATE: + return fromProto(value.getDateValue()); + default: + // We should have already thrown an exception in the SqlRowMerger + throw new IllegalStateException("Unsupported array query param element type: " + elemType); + } + } + + private static Instant toInstant(Timestamp timestamp) { + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); + } + + private static Date fromProto(com.google.type.Date proto) { + return Date.fromYearMonthDay(proto.getYear(), proto.getMonth(), proto.getDay()); + } +} diff --git a/test-proxy/src/main/proto/test_proxy.proto b/test-proxy/src/main/proto/test_proxy.proto index 0c9b64b8cd..b82354b08e 100644 --- a/test-proxy/src/main/proto/test_proxy.proto +++ b/test-proxy/src/main/proto/test_proxy.proto @@ -249,18 +249,22 @@ message ReadModifyWriteRowRequest { google.bigtable.v2.ReadModifyWriteRowRequest request = 2; } +// Request to test proxy service to execute a query. message ExecuteQueryRequest { // The ID of the target client object. string client_id = 1; + // The raw request to the Bigtable server. google.bigtable.v2.ExecuteQueryRequest request = 2; } +// Response from test proxy service for ExecuteQueryRequest. message ExecuteQueryResult { // The RPC status from the client binding. google.rpc.Status status = 1; - google.bigtable.v2.ResultSetMetadata result_set_metadata = 2; // deprecated + // deprecated + google.bigtable.v2.ResultSetMetadata result_set_metadata = 2; // Name and type information for the query result. ResultSetMetadata metadata = 4; @@ -269,10 +273,13 @@ message ExecuteQueryResult { repeated SqlRow rows = 3; } +// Schema information for the query result. message ResultSetMetadata { + // Column metadata for each column inthe query result. repeated google.bigtable.v2.ColumnMetadata columns = 1; } +// Representation of a single row in the query result. message SqlRow { // Columnar values returned by the query. repeated google.bigtable.v2.Value values = 1; From 5a617b49a19059d0cfa310ecf178a03f0e3ac243 Mon Sep 17 00:00:00 2001 From: Jack Dingilian Date: Mon, 7 Oct 2024 17:48:28 -0400 Subject: [PATCH 3/3] Remove security options for a separate PR Change-Id: Ibe2fb899f83744d4e38debc58379e9942350a83f --- .../bigtable/testproxy/CbtTestProxy.java | 123 +++++++++++------- .../bigtable/testproxy/CbtTestProxyMain.java | 14 +- test-proxy/src/main/proto/test_proxy.proto | 32 ----- 3 files changed, 86 insertions(+), 83 deletions(-) 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 068fe54f76..1c72704b62 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,8 +26,7 @@ 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.AccessToken; -import com.google.auth.oauth2.OAuth2Credentials; +import com.google.auth.oauth2.GoogleCredentials; import com.google.auto.value.AutoValue; import com.google.bigtable.v2.Column; import com.google.bigtable.v2.Family; @@ -60,6 +59,9 @@ 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; @@ -70,6 +72,7 @@ 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,13 +95,50 @@ static CbtClient create(BigtableDataSettings settings, BigtableDataClient dataCl private static final Logger logger = Logger.getLogger(CbtTestProxy.class.getName()); - private CbtTestProxy() { + 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; this.idClientMap = new ConcurrentHashMap<>(); } - /** Factory method to return a proxy instance. */ - public static CbtTestProxy create() { - return new CbtTestProxy(); + /** + * 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); } /** @@ -152,17 +192,12 @@ private CbtClient getClient(String id) throws StatusException { @Override public synchronized void createClient( CreateClientRequest request, StreamObserver responseObserver) { - Preconditions.checkArgument(!request.getClientId().isEmpty(), "client id must be provided"); 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.containsKey(request.getClientId())) { + if (idClientMap.contains(request.getClientId())) { responseObserver.onError( Status.ALREADY_EXISTS .withDescription("Client " + request.getClientId() + " already exists.") @@ -170,8 +205,6 @@ 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 @@ -180,6 +213,9 @@ 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); @@ -213,13 +249,8 @@ public synchronized void createClient( settingsBuilder .stubSettings() .setEndpoint(request.getDataTarget()) - .setTransportChannelProvider( - getTransportChannel( - request.getSecurityOptions().getUseSsl(), - request.getSecurityOptions().getSslRootCertsPem(), - request.getSecurityOptions().getSslEndpointOverride())) - .setCredentialsProvider( - getCredentialsProvider(request.getSecurityOptions().getAccessToken())); + .setTransportChannelProvider(getTransportChannel()) + .setCredentialsProvider(getCredentialsProvider()); } BigtableDataSettings settings = settingsBuilder.build(); BigtableDataClient client = BigtableDataClient.create(settings); @@ -749,60 +780,52 @@ private static String extractTableIdFromTableName(String fullTableName) return matcher.group(3); } - @SuppressWarnings("rawtypes") - private InstantiatingGrpcChannelProvider getTransportChannel( - boolean encrypted, String rootCertsPem, String sslTarget) { + private InstantiatingGrpcChannelProvider getTransportChannel() throws IOException { if (!encrypted) { return EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder() .setChannelConfigurator(ManagedChannelBuilder::usePlaintext) .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); - } + if (rootCerts == null) { + return EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder().build(); } + 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; - - if (sslContext != null) { - channelBuilder.sslContext(sslContext); - } - - if (!sslTarget.isEmpty()) { - channelBuilder.overrideAuthority(sslTarget); - } - + channelBuilder.sslContext(secureContext).overrideAuthority(sslTarget); return channelBuilder; } }) .build(); } - private CredentialsProvider getCredentialsProvider(String accessToken) { - if (accessToken.isEmpty()) { + private CredentialsProvider getCredentialsProvider() throws IOException { + if (credential == null) { return NoCredentialsProvider.create(); } - return FixedCredentialsProvider.create( - OAuth2Credentials.create(new AccessToken(accessToken, null))); + final GoogleCredentials creds = + GoogleCredentials.fromStream(new ByteArrayInputStream(credential.getBytes(UTF_8))); + + return FixedCredentialsProvider.create(creds); } 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 f817197d14..8750909f1a 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,7 +32,19 @@ public static void main(String[] args) throws InterruptedException, IOException throw new IllegalArgumentException(String.format("Port %d is not > 0.", port)); } - CbtTestProxy cbtTestProxy = CbtTestProxy.create(); + 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(); + } + 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/proto/test_proxy.proto b/test-proxy/src/main/proto/test_proxy.proto index b82354b08e..753ca82cc0 100644 --- a/test-proxy/src/main/proto/test_proxy.proto +++ b/test-proxy/src/main/proto/test_proxy.proto @@ -38,27 +38,6 @@ enum OptionalFeatureConfig { // Request to test proxy service to create a client object. message CreateClientRequest { - message SecurityOptions { - // Access token to use for client credentials. If empty, the client will not - // use any call credentials. Certain implementations may require `use_ssl` - // to be set when using this. - string access_token = 1; - - // Whether to use SSL channel credentials when connecting to the data - // endpoint. - bool use_ssl = 2; - - // If using SSL channel credentials, override the SSL endpoint to match the - // host that is specified in the backend's certificate. Also sets the - // client's authority header value. - string ssl_endpoint_override = 3; - - // PEM encoding of the server root certificates. If not set, the default - // root certs will be used instead. The default can be overridden via the - // GRPC_DEFAULT_SSL_ROOTS_FILE_PATH env var. - string ssl_root_certs_pem = 4; - } - // A unique ID associated with the client object to be created. string client_id = 1; @@ -87,17 +66,6 @@ message CreateClientRequest { // Optional config that dictates how the optional features should be enabled // during the client creation. Please check the enum type's docstring above. OptionalFeatureConfig optional_feature_config = 7; - - // Options to allow connecting to backends with channel and/or call - // credentials. This is needed internally by Cloud Bigtable's own testing - // frameworks.It is not necessary to support these fields for client - // conformance testing. - // - // WARNING: this allows the proxy to connect to a real production - // CBT backend with the right options, however, the proxy itself is insecure - // so it is not recommended to use it with real credentials or outside testing - // contexts. - SecurityOptions security_options = 8; } // Response from test proxy service for CreateClientRequest.