Skip to content

Commit f6764dd

Browse files
authoredApr 15, 2024··
feat: add support for TPC (#1901)
1 parent 87c14a2 commit f6764dd

File tree

10 files changed

+149
-7
lines changed

10 files changed

+149
-7
lines changed
 

‎core/src/main/java/com/google/cloud/sql/ConnectorConfig.java

+25-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class ConnectorConfig {
3636
private final GoogleCredentials googleCredentials;
3737
private final String googleCredentialsPath;
3838
private final String adminQuotaProject;
39+
private final String universeDomain;
3940

4041
private ConnectorConfig(
4142
String targetPrincipal,
@@ -45,7 +46,8 @@ private ConnectorConfig(
4546
Supplier<GoogleCredentials> googleCredentialsSupplier,
4647
GoogleCredentials googleCredentials,
4748
String googleCredentialsPath,
48-
String adminQuotaProject) {
49+
String adminQuotaProject,
50+
String universeDomain) {
4951
this.targetPrincipal = targetPrincipal;
5052
this.delegates = delegates;
5153
this.adminRootUrl = adminRootUrl;
@@ -54,6 +56,7 @@ private ConnectorConfig(
5456
this.googleCredentials = googleCredentials;
5557
this.googleCredentialsPath = googleCredentialsPath;
5658
this.adminQuotaProject = adminQuotaProject;
59+
this.universeDomain = universeDomain;
5760
}
5861

5962
@Override
@@ -72,7 +75,8 @@ public boolean equals(Object o) {
7275
&& Objects.equal(googleCredentialsSupplier, that.googleCredentialsSupplier)
7376
&& Objects.equal(googleCredentials, that.googleCredentials)
7477
&& Objects.equal(googleCredentialsPath, that.googleCredentialsPath)
75-
&& Objects.equal(adminQuotaProject, that.adminQuotaProject);
78+
&& Objects.equal(adminQuotaProject, that.adminQuotaProject)
79+
&& Objects.equal(universeDomain, that.universeDomain);
7680
}
7781

7882
@Override
@@ -85,7 +89,8 @@ public int hashCode() {
8589
googleCredentialsSupplier,
8690
googleCredentials,
8791
googleCredentialsPath,
88-
adminQuotaProject);
92+
adminQuotaProject,
93+
universeDomain);
8994
}
9095

9196
public String getTargetPrincipal() {
@@ -120,6 +125,10 @@ public String getAdminQuotaProject() {
120125
return adminQuotaProject;
121126
}
122127

128+
public String getUniverseDomain() {
129+
return universeDomain;
130+
}
131+
123132
/** The builder for the ConnectionConfig. */
124133
public static class Builder {
125134

@@ -131,6 +140,7 @@ public static class Builder {
131140
private GoogleCredentials googleCredentials;
132141
private String googleCredentialsPath;
133142
private String adminQuotaProject;
143+
private String universeDomain;
134144

135145
public Builder withTargetPrincipal(String targetPrincipal) {
136146
this.targetPrincipal = targetPrincipal;
@@ -173,6 +183,11 @@ public Builder withAdminQuotaProject(String adminQuotaProject) {
173183
return this;
174184
}
175185

186+
public Builder withUniverseDomain(String universeDomain) {
187+
this.universeDomain = universeDomain;
188+
return this;
189+
}
190+
176191
/** Builds a new instance of {@code ConnectionConfig}. */
177192
public ConnectorConfig build() {
178193
// validate only one GoogleCredentials configuration field set
@@ -191,6 +206,11 @@ public ConnectorConfig build() {
191206
"Invalid configuration, more than one GoogleCredentials field has a value "
192207
+ "(googleCredentials, googleCredentialsPath, googleCredentialsSupplier)");
193208
}
209+
if (adminRootUrl != null && universeDomain != null) {
210+
throw new IllegalStateException(
211+
"Can not set Admin API Endpoint and Universe Domain together, "
212+
+ "set only Admin API Endpoint (it already contains the universe domain)");
213+
}
194214

195215
return new ConnectorConfig(
196216
targetPrincipal,
@@ -200,7 +220,8 @@ public ConnectorConfig build() {
200220
googleCredentialsSupplier,
201221
googleCredentials,
202222
googleCredentialsPath,
203-
adminQuotaProject);
223+
adminQuotaProject,
224+
universeDomain);
204225
}
205226
}
206227
}

‎core/src/main/java/com/google/cloud/sql/core/ConnectionConfig.java

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class ConnectionConfig {
4646
public static final String ENABLE_IAM_AUTH_PROPERTY = "enableIamAuth";
4747
public static final String IP_TYPES_PROPERTY = "ipTypes";
4848
public static final String CLOUD_SQL_ADMIN_QUOTA_PROJECT_PROPERTY = "cloudSqlAdminQuotaProject";
49+
public static final String CLOUD_SQL_UNIVERSE_DOMAIN = "cloudSqlUniverseDomain";
4950
public static final AuthType DEFAULT_AUTH_TYPE = AuthType.PASSWORD;
5051
public static final String DEFAULT_IP_TYPES = "PUBLIC,PRIVATE";
5152
public static final List<IpType> DEFAULT_IP_TYPE_LIST =
@@ -95,6 +96,8 @@ public static ConnectionConfig fromConnectionProperties(Properties props) {
9596
props.getProperty(ConnectionConfig.CLOUD_SQL_GOOGLE_CREDENTIALS_PATH);
9697
final String adminQuotaProject =
9798
props.getProperty(ConnectionConfig.CLOUD_SQL_ADMIN_QUOTA_PROJECT_PROPERTY);
99+
final String universeDomain = props.getProperty(ConnectionConfig.CLOUD_SQL_UNIVERSE_DOMAIN);
100+
98101
return new ConnectionConfig(
99102
csqlInstanceName,
100103
namedConnection,
@@ -109,6 +112,7 @@ public static ConnectionConfig fromConnectionProperties(Properties props) {
109112
.withAdminServicePath(adminServicePath)
110113
.withGoogleCredentialsPath(googleCredentialsPath)
111114
.withAdminQuotaProject(adminQuotaProject)
115+
.withUniverseDomain(universeDomain)
112116
.build());
113117
}
114118

‎core/src/main/java/com/google/cloud/sql/core/DefaultConnectionInfoRepositoryFactory.java

+3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public DefaultConnectionInfoRepository create(
6868
.build();
6969
adminApiBuilder.setGoogleClientRequestInitializer(clientRequestInitializer);
7070
}
71+
if (config.getUniverseDomain() != null) {
72+
adminApiBuilder.setUniverseDomain(config.getUniverseDomain());
73+
}
7174
return new DefaultConnectionInfoRepository(adminApiBuilder.build());
7275
}
7376
}

‎core/src/main/java/com/google/cloud/sql/core/InternalConnectorRegistry.java

+19
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,25 @@ private Connector createConnector(ConnectorConfig config) {
305305
CredentialFactory instanceCredentialFactory =
306306
credentialFactoryProvider.getInstanceCredentialFactory(config);
307307

308+
String universeDomain = config.getUniverseDomain();
309+
String credentialsUniverse;
310+
try {
311+
credentialsUniverse = instanceCredentialFactory.getCredentials().getUniverseDomain();
312+
} catch (IOException e) {
313+
throw new IllegalStateException("Fail to fetch the credential universe domain");
314+
}
315+
316+
// Verify that the universe domain provided matches the credential universe domain.
317+
if (credentialsUniverse != null
318+
&& universeDomain != null
319+
&& !credentialsUniverse.equals(universeDomain)) {
320+
throw new IllegalStateException(
321+
String.format(
322+
"The configured universe domain (%s) does not match "
323+
+ "the credential universe domain (%s)",
324+
universeDomain, credentialsUniverse));
325+
}
326+
308327
return new Connector(
309328
config,
310329
connectionInfoRepositoryFactory,

‎core/src/test/java/com/google/cloud/sql/ConnectorConfigTest.java

+45-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,28 @@ public void testBuild_withGoogleCredentialsSupplier() {
7979
assertThat(cc.getGoogleCredentialsSupplier()).isSameInstanceAs(wantGoogleCredentialSupplier);
8080
}
8181

82+
@Test
83+
public void testBuild_withUniverseDomain() {
84+
final String wantUniverseDomain = "test-universe.test";
85+
ConnectorConfig cc =
86+
new ConnectorConfig.Builder().withUniverseDomain(wantUniverseDomain).build();
87+
assertThat(cc.getUniverseDomain()).isEqualTo(wantUniverseDomain);
88+
}
89+
90+
@Test
91+
public void testBuild_failsWhenAdminAPIAndUniverseDomainAreSet() {
92+
final String wantAdminRootUrl = "https://googleapis.example.com/";
93+
final String wantUniverseDomain = "test-universe.test";
94+
95+
assertThrows(
96+
IllegalStateException.class,
97+
() ->
98+
new ConnectorConfig.Builder()
99+
.withAdminRootUrl(wantAdminRootUrl)
100+
.withUniverseDomain(wantUniverseDomain)
101+
.build());
102+
}
103+
82104
@Test
83105
public void testBuild_failsWhenManyGoogleCredentialFieldsSet() {
84106
final Supplier<GoogleCredentials> wantGoogleCredentialSupplier =
@@ -309,6 +331,27 @@ public void testEqual_withAdminQuotaProjectEqual() {
309331
assertThat(k1.hashCode()).isEqualTo(k2.hashCode());
310332
}
311333

334+
@Test
335+
public void testNotEqual_withUniverseDomainNotEqual() {
336+
ConnectorConfig k1 =
337+
new ConnectorConfig.Builder().withUniverseDomain("test-universe.test").build();
338+
ConnectorConfig k2 = new ConnectorConfig.Builder().withUniverseDomain("googleapis.com").build();
339+
340+
assertThat(k1).isNotEqualTo(k2);
341+
assertThat(k1.hashCode()).isNotEqualTo(k2.hashCode());
342+
}
343+
344+
@Test
345+
public void testNotEqual_withUniverseDomainEqual() {
346+
ConnectorConfig k1 =
347+
new ConnectorConfig.Builder().withUniverseDomain("test-universe.test").build();
348+
ConnectorConfig k2 =
349+
new ConnectorConfig.Builder().withUniverseDomain("test-universe.test").build();
350+
351+
assertThat(k1).isEqualTo(k2);
352+
assertThat(k1.hashCode()).isEqualTo(k2.hashCode());
353+
}
354+
312355
@Test
313356
public void testHashCode() {
314357
final String wantTargetPrincipal = "test@example.com";
@@ -337,6 +380,7 @@ public void testHashCode() {
337380
null, // googleCredentialsSupplier
338381
null, // googleCredentials
339382
wantGoogleCredentialsPath,
340-
wantAdminQuotaProject));
383+
wantAdminQuotaProject,
384+
null)); // universeDomain
341385
}
342386
}

‎core/src/test/java/com/google/cloud/sql/core/InternalConnectorRegistryTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,38 @@ public void registerConnectionFailsWithDuplicateNameAndDifferentConfig()
196196
assertThrows(IllegalArgumentException.class, () -> registry.register("my-connection", config2));
197197
}
198198

199+
@Test
200+
public void registerConnectionFails_withUniverseDomainDoesNotMatchCredentialsDomain()
201+
throws InterruptedException {
202+
final String googleCredentialsPath =
203+
InternalConnectorRegistryTest.class.getResource("/sample-credentials.json").getFile();
204+
final String universeDomain = "test-universe.test";
205+
206+
InternalConnectorRegistry registry = createRegistry(PUBLIC_IP, stubCredentialFactoryProvider);
207+
ConnectorConfig config =
208+
new ConnectorConfig.Builder()
209+
.withGoogleCredentialsPath(googleCredentialsPath)
210+
.withUniverseDomain(universeDomain)
211+
.build();
212+
assertThrows(IllegalStateException.class, () -> registry.register("my-connection", config));
213+
}
214+
215+
@Test
216+
public void registerConnection_withUniverseDomainMatchingCredentialsDomain()
217+
throws InterruptedException {
218+
final String googleCredentialsPath =
219+
InternalConnectorRegistryTest.class.getResource("/sample-credentials.json").getFile();
220+
final String universeDomain = "googleapis.com";
221+
222+
InternalConnectorRegistry registry = createRegistry(PUBLIC_IP, stubCredentialFactoryProvider);
223+
ConnectorConfig config =
224+
new ConnectorConfig.Builder()
225+
.withGoogleCredentialsPath(googleCredentialsPath)
226+
.withUniverseDomain(universeDomain)
227+
.build();
228+
registry.register("my-connection", config);
229+
}
230+
199231
@Test
200232
public void closeNamedConnectionFailsWhenNotFound() throws InterruptedException {
201233
InternalConnectorRegistry registry = createRegistry(PUBLIC_IP, stubCredentialFactoryProvider);

‎core/src/test/resources/sample-credentials.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"quota_project_id": "sample",
55
"refresh_token": "sample",
66
"access_token": "sample",
7-
"type": "authorized_user"
7+
"type": "authorized_user",
8+
"universe_domain": "googleapis.com"
89
}

‎docs/configuration.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,13 @@ configuration using the Cloud SQL Admin API.
280280
| cloudSqlGoogleCredentialsPath | GOOGLE_CREDENTIALS_PATH | A file path to a JSON file containing a GoogleCredentials oauth token. | `/home/alice/secrets/my-credentials.json` |
281281
| cloudSqlAdminRootUrl | ADMIN_ROOT_URL | An alternate root url for the Cloud SQL admin API. Must end in '/' See [rootUrl](java-api-root-url) | `https://googleapis.example.com/` |
282282
| cloudSqlAdminServicePath | ADMIN_SERVICE_PATH | An alternate path to the SQL Admin API endpoint. Must not begin with '/'. Must end with '/'. See [servicePath](java-api-service-path) | `sqladmin/v1beta1/` |
283-
| cloudSqlAdminQuotaProject | ADMIN_QUOTA_PROJECT | A project ID for quota and billing. See [Quota Project](quota-project) | `my-project` |
283+
| cloudSqlAdminQuotaProject | ADMIN_QUOTA_PROJECT | A project ID for quota and billing. See [Quota Project][quota-project] | `my-project` |
284+
| cloudSqlUniverseDomain | UNIVERSE_DOMAIN | A universe domain for the TPC environment (default is googleapis.com). See [TPC][tpc] | test-universe.test
284285

285286
[java-api-root-url]: https://github.com/googleapis/google-api-java-client/blob/main/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClient.java#L49
286287
[java-api-service-path]: https://github.com/googleapis/google-api-java-client/blob/main/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClient.java#L52
287288
[quota-project]: jdbc.md#quota-project
289+
[tpc]: jdbc.md#trusted-partner-cloud-tpc-support
288290

289291
### Connection Configuration Properties
290292

‎docs/jdbc.md

+13
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,19 @@ Properties connProps = new Properties();
532532
connProps.setProperty("cloudSqlAdminQuotaProject", "PROJECT_NAME");
533533
```
534534

535+
### Trusted Partner Cloud (TPC) support
536+
537+
The Java Connector supports setting the universe domain for the TPC environment
538+
with the `cloudSqlUniverseDomain` property. If not specified, defaults to the
539+
Google Default Universe (GDU): googleapis.com.
540+
541+
#### Example
542+
543+
```java
544+
Properties connProps = new Properties();
545+
connProps.setProperty("cloudSqlUniverseDomain", "test-universe.test");
546+
```
547+
535548
## Configuration Reference
536549

537550
- See [Configuration Reference](configuration.md)

‎r2dbc/core/src/main/java/com/google/cloud/sql/core/GcpConnectionFactoryProvider.java

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public abstract class GcpConnectionFactoryProvider implements ConnectionFactoryP
4848
public static final Option<String> ADMIN_ROOT_URL = Option.valueOf("ADMIN_ROOT_URL");
4949
public static final Option<String> ADMIN_SERVICE_PATH = Option.valueOf("ADMIN_SERVICE_PATH");
5050
public static final Option<String> ADMIN_QUOTA_PROJECT = Option.valueOf("ADMIN_QUOTA_PROJECT");
51+
public static final Option<String> UNIVERSE_DOMAIN = Option.valueOf("UNIVERSE_DOMAIN");
5152
public static final Option<String> GOOGLE_CREDENTIALS_PATH =
5253
Option.valueOf("GOOGLE_CREDENTIALS_PATH");
5354

@@ -114,6 +115,7 @@ public ConnectionFactory create(ConnectionFactoryOptions connectionFactoryOption
114115
(String) connectionFactoryOptions.getValue(ADMIN_QUOTA_PROJECT);
115116
final String googleCredentialsPath =
116117
(String) connectionFactoryOptions.getValue(GOOGLE_CREDENTIALS_PATH);
118+
final String universeDomain = (String) connectionFactoryOptions.getValue(UNIVERSE_DOMAIN);
117119

118120
Builder optionBuilder = createBuilder(connectionFactoryOptions);
119121
String cloudSqlInstance = (String) connectionFactoryOptions.getRequiredValue(HOST);
@@ -131,6 +133,7 @@ public ConnectionFactory create(ConnectionFactoryOptions connectionFactoryOption
131133
.withAdminServicePath(adminServicePath)
132134
.withAdminQuotaProject(adminQuotaProject)
133135
.withGoogleCredentialsPath(googleCredentialsPath)
136+
.withUniverseDomain(universeDomain)
134137
.build())
135138
.build();
136139
// Precompute SSL Data to trigger the initial refresh to happen immediately,

0 commit comments

Comments
 (0)
Please sign in to comment.