Skip to content

Commit 5832c18

Browse files
authored
chore: update experimental host config (#4098)
* chore: update experimental host config * lint issues
1 parent 163b118 commit 5832c18

File tree

6 files changed

+112
-31
lines changed

6 files changed

+112
-31
lines changed

src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import java.util.Map;
4444
import java.util.logging.Level;
4545
import java.util.logging.Logger;
46-
import java.util.regex.Pattern;
4746
import javax.annotation.Nullable;
4847
import org.apache.commons.cli.CommandLine;
4948
import org.apache.commons.cli.CommandLineParser;
@@ -65,13 +64,12 @@ public class OptionsMetadata {
6564
private static final String SPANNER_EMULATOR_HOST_ENV_VAR = "SPANNER_EMULATOR_HOST";
6665

6766
static Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofSeconds(30L);
68-
private static final String CLOUD_SPANNER_HOST_FORMAT = ".*\\.googleapis\\.com.*";
69-
private static final Pattern CLOUD_SPANNER_HOST_PATTERN =
70-
Pattern.compile(CLOUD_SPANNER_HOST_FORMAT);
7167

7268
private static final String EXTERNAL_HOST_PROJECT = "default";
7369
private static final String EXTERNAL_HOST_INSTANCE = "default";
7470

71+
private static final String IS_EXPERIMENTAL_HOST_PROPERTY_NAME = "isExperimentalHost";
72+
7573
/**
7674
* Builder class for creating an instance of {@link OptionsMetadata}.
7775
*
@@ -118,6 +116,7 @@ public static class Builder {
118116
private Duration startupTimeout = DEFAULT_STARTUP_TIMEOUT;
119117
private String clientCertificate;
120118
private String clientKey;
119+
private boolean isExperimentalHost = false;
121120

122121
Builder() {}
123122

@@ -425,7 +424,7 @@ Builder setStartupTimeout(Duration timeout) {
425424

426425
/**
427426
* Configures mTLS authentication using the provided client certificate and key files. mTLS is
428-
* only supported for external spanner hosts.
427+
* only supported for experimental spanner hosts.
429428
*
430429
* @param clientCertificate Path to the client certificate file.
431430
* @param clientKey Path to the client private key file.
@@ -437,6 +436,18 @@ Builder useClientCert(String clientCertificate, String clientKey) {
437436
return this;
438437
}
439438

439+
/*
440+
* Configures connection to an experimental host endpoint
441+
*
442+
* @params experimentalHost url of the experimental host endpoint
443+
*/
444+
@ExperimentalApi("https://github.com/googleapis/java-spanner/pull/3574")
445+
Builder setExperimentalHost(String experimentalHost) {
446+
setEndpoint(experimentalHost);
447+
this.isExperimentalHost = true;
448+
return this;
449+
}
450+
440451
public OptionsMetadata build() {
441452
if (Strings.isNullOrEmpty(project) && !Strings.isNullOrEmpty(instance)) {
442453
throw SpannerExceptionFactory.newSpannerException(
@@ -504,7 +515,8 @@ private String[] toCommandLineArguments() {
504515
|| autoConfigEmulator
505516
|| useVirtualGrpcTransportThreads
506517
|| enableEndToEndTracing
507-
|| (clientKey != null && clientCertificate != null)) {
518+
|| (clientKey != null && clientCertificate != null)
519+
|| isExperimentalHost) {
508520
StringBuilder jdbcOptionBuilder = new StringBuilder();
509521
if (usePlainText) {
510522
jdbcOptionBuilder.append("usePlainText=true;");
@@ -530,6 +542,9 @@ private String[] toCommandLineArguments() {
530542
jdbcOptionBuilder.append("clientCertificate=").append(clientCertificate).append(";");
531543
jdbcOptionBuilder.append("clientKey=").append(clientKey).append(";");
532544
}
545+
if (isExperimentalHost) {
546+
jdbcOptionBuilder.append("isExperimentalHost=true;");
547+
}
533548
addOption(args, OPTION_JDBC_PROPERTIES, jdbcOptionBuilder.toString());
534549
}
535550
if (logGrpcMessages) {
@@ -733,8 +748,7 @@ private OptionsMetadata(Builder builder) {
733748
if (!propertyMap.containsKey("defaultSequenceKind")) {
734749
propertyMap.put("defaultSequenceKind", "bit_reversed_positive");
735750
}
736-
boolean usesExternalHost =
737-
isExternalHost(Preconditions.checkNotNull(environment), commandLine, propertyMap);
751+
boolean usesExperimentalHost = isExperimentalHost(commandLine, propertyMap);
738752

739753
this.environment = environment;
740754
this.osName = osName;
@@ -752,7 +766,7 @@ private OptionsMetadata(Builder builder) {
752766
+ "OR use -c to set the credentials in PGAdapter and use these credentials for all connections.");
753767
}
754768
if (this.commandLine.hasOption(OPTION_DATABASE_NAME)
755-
&& !usesExternalHost
769+
&& !usesExperimentalHost
756770
&& !(this.commandLine.hasOption(OPTION_PROJECT_ID)
757771
&& this.commandLine.hasOption(OPTION_INSTANCE_ID))) {
758772
throw SpannerExceptionFactory.newSpannerException(
@@ -761,7 +775,7 @@ private OptionsMetadata(Builder builder) {
761775
+ "Use the options -p <project-id> -i <instance-id> -d <database-id> to specify the "
762776
+ "database that all connections to this instance of PGAdapter should use.");
763777
}
764-
if ((usesExternalHost
778+
if ((usesExperimentalHost
765779
|| (this.commandLine.hasOption(OPTION_PROJECT_ID)
766780
&& this.commandLine.hasOption(OPTION_INSTANCE_ID)))
767781
&& this.commandLine.hasOption(OPTION_DATABASE_NAME)) {
@@ -1023,7 +1037,7 @@ public SessionPoolOptions getSessionPoolOptions() {
10231037
*/
10241038
public String buildCredentialsFile() {
10251039
// Skip if a com.google.auth.Credentials instance has been set.
1026-
if (isExternalHost() || credentials != null) {
1040+
if (isExperimentalHost() || credentials != null) {
10271041
return null;
10281042
}
10291043
if (!commandLine.hasOption(OPTION_CREDENTIALS_FILE)) {
@@ -1113,17 +1127,23 @@ private static boolean usesEmulator(
11131127
|| isAutoConfigEmulator(propertyMap);
11141128
}
11151129

1116-
private boolean isExternalHost() {
1117-
return isExternalHost(this.environment, this.commandLine, this.propertyMap);
1130+
private boolean isExperimentalHost() {
1131+
if (this.propertyMap == null) {
1132+
return isExperimentalHost(
1133+
this.commandLine,
1134+
parseProperties(this.commandLine.getOptionValue(OPTION_JDBC_PROPERTIES, "")));
1135+
}
1136+
return isExperimentalHost(this.commandLine, this.propertyMap);
11181137
}
11191138

1120-
private static boolean isExternalHost(
1121-
Map<String, String> environment, CommandLine commandLine, Map<String, String> propertyMap) {
1122-
return !usesEmulator(environment, propertyMap)
1123-
&& commandLine.hasOption(OPTION_SPANNER_ENDPOINT)
1124-
&& !CLOUD_SPANNER_HOST_PATTERN
1125-
.matcher(commandLine.getOptionValue(OPTION_SPANNER_ENDPOINT))
1126-
.matches();
1139+
private static boolean isExperimentalHost(
1140+
CommandLine commandLine, Map<String, String> propertyMap) {
1141+
if (propertyMap == null) {
1142+
return false;
1143+
}
1144+
return commandLine.hasOption(OPTION_SPANNER_ENDPOINT)
1145+
&& commandLine.hasOption(OPTION_JDBC_PROPERTIES)
1146+
&& propertyMap.containsKey(IS_EXPERIMENTAL_HOST_PROPERTY_NAME);
11271147
}
11281148

11291149
/** Returns the fully qualified database name based on the given database id or name. */
@@ -1135,7 +1155,7 @@ public DatabaseName getDatabaseName(String database) {
11351155
String projectId;
11361156
if (commandLine.hasOption(OPTION_PROJECT_ID)) {
11371157
projectId = commandLine.getOptionValue(OPTION_PROJECT_ID);
1138-
} else if (isExternalHost()) {
1158+
} else if (isExperimentalHost()) {
11391159
projectId = EXTERNAL_HOST_PROJECT;
11401160
} else {
11411161
projectId = getDefaultProjectId();
@@ -1150,7 +1170,7 @@ public DatabaseName getDatabaseName(String database) {
11501170
String instanceId;
11511171
if (commandLine.hasOption(OPTION_INSTANCE_ID)) {
11521172
instanceId = commandLine.getOptionValue(OPTION_INSTANCE_ID);
1153-
} else if (isExternalHost()) {
1173+
} else if (isExperimentalHost()) {
11541174
instanceId = EXTERNAL_HOST_INSTANCE;
11551175
} else {
11561176
throw SpannerExceptionFactory.newSpannerException(
@@ -1576,10 +1596,10 @@ public boolean hasDefaultConnectionUrl() {
15761596
public DatabaseId getDefaultDatabaseId() {
15771597
return this.hasDefaultConnectionUrl()
15781598
? DatabaseId.of(
1579-
!commandLine.hasOption(OPTION_PROJECT_ID) && isExternalHost()
1599+
!commandLine.hasOption(OPTION_PROJECT_ID) && isExperimentalHost()
15801600
? EXTERNAL_HOST_PROJECT
15811601
: commandLine.getOptionValue(OPTION_PROJECT_ID),
1582-
!commandLine.hasOption(OPTION_INSTANCE_ID) && isExternalHost()
1602+
!commandLine.hasOption(OPTION_INSTANCE_ID) && isExperimentalHost()
15831603
? EXTERNAL_HOST_INSTANCE
15841604
: commandLine.getOptionValue(OPTION_INSTANCE_ID),
15851605
commandLine.getOptionValue(OPTION_DATABASE_NAME))
@@ -1588,18 +1608,18 @@ public DatabaseId getDefaultDatabaseId() {
15881608

15891609
/** Returns true if these options contain a default instance id. */
15901610
public boolean hasDefaultInstanceId() {
1591-
return isExternalHost()
1611+
return isExperimentalHost()
15921612
|| (commandLine.hasOption(OPTION_PROJECT_ID) && commandLine.hasOption(OPTION_INSTANCE_ID));
15931613
}
15941614

15951615
/** Returns the id of the default instance or null if no default has been selected. */
15961616
public InstanceId getDefaultInstanceId() {
15971617
if (hasDefaultInstanceId()) {
15981618
return InstanceId.of(
1599-
!commandLine.hasOption(OPTION_PROJECT_ID) && isExternalHost()
1619+
!commandLine.hasOption(OPTION_PROJECT_ID) && isExperimentalHost()
16001620
? EXTERNAL_HOST_PROJECT
16011621
: commandLine.getOptionValue(OPTION_PROJECT_ID),
1602-
!commandLine.hasOption(OPTION_INSTANCE_ID) && isExternalHost()
1622+
!commandLine.hasOption(OPTION_INSTANCE_ID) && isExperimentalHost()
16031623
? EXTERNAL_HOST_INSTANCE
16041624
: commandLine.getOptionValue(OPTION_INSTANCE_ID));
16051625
}

src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public String getCredentials() {
5555

5656
@BeforeClass
5757
public static void setup() throws ClassNotFoundException {
58+
IntegrationTest.skipOnExperimentalHost("Cloud auth is not applicable for experimental host");
5859
// Make sure the PG JDBC driver is loaded.
5960
Class.forName("org.postgresql.Driver");
6061

src/test/java/com/google/cloud/spanner/pgadapter/ITOpenTelemetryTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class ITOpenTelemetryTest implements IntegrationTest {
5656
@BeforeClass
5757
public static void setup() throws IOException {
5858
IntegrationTest.skipOnEmulator("This test requires credentials");
59+
IntegrationTest.skipOnExperimentalHost("Cloud auth is not applicable for experimental host");
5960

6061
OptionsMetadata.Builder openTelemetryOptionsBuilder =
6162
OptionsMetadata.newBuilder()

src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ static void skipOnEmulator(String reason) {
2222
assumeFalse(reason, isRunningOnEmulator());
2323
}
2424

25+
static void skipOnExperimentalHost(String reason) {
26+
assumeFalse(reason, isRunningOnExperimentalHost());
27+
}
28+
2529
static boolean isRunningOnEmulator() {
2630
return System.getenv("SPANNER_EMULATOR_HOST") != null;
2731
}
32+
33+
static boolean isRunningOnExperimentalHost() {
34+
return System.getProperty("SPANNER_EXPERIMENTAL_HOST") != null;
35+
}
2836
}

src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.common.primitives.Bytes;
4444
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
4545
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
46+
import io.grpc.ManagedChannelBuilder;
4647
import io.opentelemetry.api.OpenTelemetry;
4748
import java.io.DataInputStream;
4849
import java.io.DataOutputStream;
@@ -113,6 +114,9 @@ public class PgAdapterTestEnv {
113114
// Default Spanner url. Null indicates that the default URL should be used.
114115
static final String DEFAULT_SPANNER_URL = null;
115116

117+
public static final String SPANNER_EXPERIMENTAL_HOST =
118+
System.getProperty("SPANNER_EXPERIMENTAL_HOST", null);
119+
116120
public static final ImmutableList<String> DEFAULT_DATA_MODEL =
117121
ImmutableList.of(
118122
"create table numbers (num int not null primary key, name varchar(100))",
@@ -235,7 +239,7 @@ public void startPGAdapterServerWithDefaultDatabase(
235239

236240
private void startPGAdapterServer(
237241
String databaseId, Iterable<String> additionalPGAdapterOptions, OpenTelemetry openTelemetry) {
238-
if (PG_ADAPTER_ADDRESS == null) {
242+
if (PG_ADAPTER_ADDRESS == null && SPANNER_EXPERIMENTAL_HOST == null) {
239243
additionalPGAdapterOptions = maybeAddAutoConfigEmulator(additionalPGAdapterOptions);
240244
String credentials = getCredentials();
241245
ImmutableList.Builder<String> argsListBuilder =
@@ -258,6 +262,21 @@ private void startPGAdapterServer(
258262
String[] args = argsListBuilder.build().toArray(new String[0]);
259263
server = new ProxyServer(new OptionsMetadata(args), openTelemetry);
260264
server.startServer();
265+
} else if (SPANNER_EXPERIMENTAL_HOST != null) {
266+
ImmutableList.Builder<String> argsListBuilder =
267+
ImmutableList.<String>builder()
268+
.add(
269+
"-e",
270+
SPANNER_EXPERIMENTAL_HOST,
271+
"-r",
272+
"isExperimentalHost=true;usePlainText=true");
273+
if (databaseId != null) {
274+
argsListBuilder.add("-d", databaseId);
275+
}
276+
argsListBuilder.add("-s", String.valueOf(0));
277+
String[] args = argsListBuilder.build().toArray(new String[0]);
278+
server = new ProxyServer(new OptionsMetadata(args));
279+
server.startServer();
261280
}
262281
}
263282

@@ -350,6 +369,9 @@ public String getSpannerUrl() {
350369
if (hostUrl == null) {
351370
hostUrl = System.getProperty(TEST_SPANNER_URL_PROPERTY, DEFAULT_SPANNER_URL);
352371
}
372+
if (SPANNER_EXPERIMENTAL_HOST != null) {
373+
hostUrl = SPANNER_EXPERIMENTAL_HOST;
374+
}
353375
return hostUrl;
354376
}
355377

@@ -361,13 +383,19 @@ public String getProjectId() {
361383
if (projectId == null) {
362384
projectId = System.getProperty(TEST_PROJECT_PROPERTY, DEFAULT_PROJECT_ID);
363385
}
386+
if (SPANNER_EXPERIMENTAL_HOST != null) {
387+
projectId = "default";
388+
}
364389
return projectId;
365390
}
366391

367392
public String getInstanceId() {
368393
if (instanceId == null) {
369394
instanceId = System.getProperty(TEST_INSTANCE_PROPERTY, DEFAULT_INSTANCE_ID);
370395
}
396+
if (SPANNER_EXPERIMENTAL_HOST != null) {
397+
instanceId = "default";
398+
}
371399
return instanceId;
372400
}
373401

@@ -517,7 +545,7 @@ private SpannerOptions createSpannerOptions() {
517545
Map<String, String> env = System.getenv();
518546
gcpCredentials = env.get(GCP_CREDENTIALS);
519547
GoogleCredentials credentials = null;
520-
if (!Strings.isNullOrEmpty(gcpCredentials)) {
548+
if (!Strings.isNullOrEmpty(gcpCredentials) && SPANNER_EXPERIMENTAL_HOST == null) {
521549
try {
522550
credentials = GoogleCredentials.fromStream(new FileInputStream(gcpCredentials));
523551
} catch (IOException e) {
@@ -552,6 +580,18 @@ private SpannerOptions createSpannerOptions() {
552580
builder.setHost(spannerHost);
553581
}
554582

583+
if (SPANNER_EXPERIMENTAL_HOST != null) {
584+
if (!SPANNER_EXPERIMENTAL_HOST.startsWith("http")) {
585+
builder.setExperimentalHost("http://" + SPANNER_EXPERIMENTAL_HOST);
586+
} else {
587+
builder.setExperimentalHost(SPANNER_EXPERIMENTAL_HOST);
588+
}
589+
builder
590+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
591+
.setCredentials(NoCredentials.getInstance());
592+
credentials = null;
593+
}
594+
555595
if (System.getProperty(CHANNEL_PROVIDER_PROPERTY) == null && credentials != null) {
556596
builder.setCredentials(credentials);
557597
}

src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,10 @@ public void testUseClientCertParameters() {
642642
public void testExternalHostConfigurations() {
643643
assertEquals(
644644
DatabaseId.of("default", "default", "test_db"),
645-
(new OptionsMetadata(new String[] {"-d", "test_db", "-e", "localhost:8000"}))
645+
(new OptionsMetadata(
646+
new String[] {
647+
"-d", "test_db", "-e", "localhost:8000", "-r", "isExperimentalHost=true"
648+
}))
646649
.getDefaultDatabaseId());
647650
SpannerException spannerException =
648651
assertThrows(
@@ -656,7 +659,7 @@ public void testExternalHostConfigurations() {
656659
assertEquals(
657660
DatabaseId.of("default", "default", "test_db"),
658661
OptionsMetadata.newBuilder()
659-
.setEndpoint("localhost:8000")
662+
.setExperimentalHost("localhost:8000")
660663
.setDatabase("test_db")
661664
.build()
662665
.getDefaultDatabaseId());
@@ -679,5 +682,13 @@ public void testExternalHostConfigurations() {
679682
.setDatabase("test_db")
680683
.build());
681684
assertEquals(ErrorCode.INVALID_ARGUMENT, spannerException.getErrorCode());
685+
assertEquals(
686+
DatabaseId.of("default", "default", "test_db"),
687+
OptionsMetadata.newBuilder()
688+
.setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010"))
689+
.setExperimentalHost("localhost:8000")
690+
.setDatabase("test_db")
691+
.build()
692+
.getDefaultDatabaseId());
682693
}
683694
}

0 commit comments

Comments
 (0)