Skip to content

Commit 243bee0

Browse files
authored
feat: Metadata Exchange support for the Java connector. (#2200)
This is the client-side implementation of the metadata exchange protocol for Cloud SQL instances. This will allow connectors to pass metadata about the connection to the Cloud SQL instance while the connection is being established. In particular, this is a part of resolving the issues with the Auth Proxy connecting to MySQL instances that use the caching_sha2_password extension. (See GoogleCloudPlatform/cloud-sql-proxy#2317) This does not update the default MDX behavior yet. That will be added in #2207
1 parent 0a0c338 commit 243bee0

28 files changed

+1489
-158
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,4 @@ artman-genfiles
7373
*.zip
7474
*.tar.gz
7575
*.rar
76+
.tools

.kokoro/check_coverage.sh

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,23 @@
1616
# `-e` enables the script to automatically fail when a command fails
1717
set -e
1818

19-
export CUR_COVER=$(cat core/target/site/jacoco/index.html | grep -o 'Total[^%]*' | sed 's/<.*>//; s/Total//')
20-
echo "Current Coverage is $CUR_COVER%"
19+
# Calculate total coverage, skipping the generated protobuf file.
20+
# The CSV file generated by Jacoco has the test coverage for each. We use AWK to process this CSV file.
21+
# here's what the AWK program means:
22+
#
23+
# NR > 1 && Skip the first line, it only has field names
24+
# $3 != "com.google.cloud.sql.core.mdx" Skip any line for class in the protobuf generated package 'mdx'
25+
# { covered += $6; total += $6+$5; } For each line add a running sum of covered lines and total lines
26+
# END { printf "%2.f", covered/total * 100}' At the end, print out the coverage percentage, no decimals
27+
#
28+
29+
CUR_COVER=$( awk -F, \
30+
'NR > 1 && $3 != "com.google.cloud.sql.core.mdx" { covered += $6; total += $6+$5; } END { printf "%2.f", covered/total * 100}' \
31+
< "core/target/site/jacoco/jacoco.csv")
32+
2133
if [ "$CUR_COVER" -lt 75 ]; then
34+
echo "FAIL: Current Coverage is $CUR_COVER%, less than the required 75%"
2235
exit 1;
36+
else
37+
echo "PASS: Current Coverage is $CUR_COVER%"
2338
fi

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function test() {
4242
sudo ifconfig lo0 alias 127.0.0.2 up
4343
sudo ifconfig lo0 alias 127.0.0.3 up
4444
fi
45-
mvn install
45+
mvn -P coverage test
4646
}
4747

4848
## e2e - Runs end-to-end integration tests.

core/pom.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,53 @@
210210
<artifactId>logback-classic</artifactId>
211211
<scope>test</scope>
212212
</dependency>
213+
<dependency>
214+
<groupId>com.google.protobuf</groupId>
215+
<artifactId>protobuf-java</artifactId>
216+
</dependency>
213217

214218
</dependencies>
215219

216220
<build>
217221
<plugins>
222+
<plugin>
223+
<groupId>org.codehaus.mojo</groupId>
224+
<artifactId>build-helper-maven-plugin</artifactId>
225+
<version>3.3.0</version>
226+
<executions>
227+
<execution>
228+
<id>test</id>
229+
<phase>generate-sources</phase>
230+
<goals>
231+
<goal>add-source</goal>
232+
</goals>
233+
<configuration>
234+
<sources>
235+
<source>${project.basedir}/target/generated-sources</source>
236+
</sources>
237+
</configuration>
238+
</execution>
239+
</executions>
240+
</plugin>
241+
<plugin>
242+
<groupId>com.github.os72</groupId>
243+
<artifactId>protoc-jar-maven-plugin</artifactId>
244+
<version>3.11.4</version>
245+
<executions>
246+
<execution>
247+
<phase>generate-sources</phase>
248+
<goals>
249+
<goal>run</goal>
250+
</goals>
251+
<configuration>
252+
<optimizeCodegen>false</optimizeCodegen>
253+
<protocVersion>3.23.0</protocVersion>
254+
<includeStdTypes>true</includeStdTypes>
255+
<outputOptions></outputOptions>
256+
</configuration>
257+
</execution>
258+
</executions>
259+
</plugin>
218260
<plugin>
219261
<groupId>org.apache.maven.plugins</groupId>
220262
<artifactId>maven-jar-plugin</artifactId>

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

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class ConnectionConfig {
5454
public static final List<IpType> DEFAULT_IP_TYPE_LIST =
5555
Arrays.asList(IpType.PUBLIC, IpType.PRIVATE);
5656
public static final String CLOUD_SQL_GOOGLE_CREDENTIALS_PATH = "cloudSqlGoogleCredentialsPath";
57+
public static final String MDX_CLIENT_PROTOCOL_TYPE = "mdxClientProtocolType";
5758

5859
private final ConnectorConfig connectorConfig;
5960
private final String cloudSqlInstance;
@@ -64,6 +65,7 @@ public class ConnectionConfig {
6465
private final AuthType authType;
6566
private final String unixSocketPathSuffix;
6667
private final String domainName;
68+
private final String mdxClientProtocolType;
6769

6870
/** Create a new ConnectionConfig from the well known JDBC Connection properties. */
6971
public static ConnectionConfig fromConnectionProperties(Properties props) {
@@ -116,6 +118,9 @@ public static ConnectionConfig fromConnectionProperties(Properties props, String
116118
? RefreshStrategy.LAZY
117119
: RefreshStrategy.BACKGROUND;
118120

121+
final String mdxClientProtocolType =
122+
props.getProperty(ConnectionConfig.MDX_CLIENT_PROTOCOL_TYPE);
123+
119124
return new ConnectionConfig(
120125
csqlInstanceName,
121126
namedConnection,
@@ -133,7 +138,8 @@ public static ConnectionConfig fromConnectionProperties(Properties props, String
133138
.withAdminQuotaProject(adminQuotaProject)
134139
.withUniverseDomain(universeDomain)
135140
.withRefreshStrategy(refreshStrategy)
136-
.build());
141+
.build(),
142+
mdxClientProtocolType);
137143
}
138144

139145
/**
@@ -197,7 +203,8 @@ private ConnectionConfig(
197203
AuthType authType,
198204
String unixSocketPathSuffix,
199205
String domainName,
200-
ConnectorConfig connectorConfig) {
206+
ConnectorConfig connectorConfig,
207+
String mdxClientProtocolType) {
201208
this.cloudSqlInstance = cloudSqlInstance;
202209
this.namedConnector = namedConnector;
203210
this.unixSocketPath = unixSocketPath;
@@ -206,6 +213,7 @@ private ConnectionConfig(
206213
this.connectorConfig = connectorConfig;
207214
this.authType = authType;
208215
this.domainName = domainName;
216+
this.mdxClientProtocolType = mdxClientProtocolType;
209217
}
210218

211219
/** Creates a new instance of the ConnectionConfig with an updated connectorConfig. */
@@ -218,7 +226,8 @@ public ConnectionConfig withConnectorConfig(ConnectorConfig config) {
218226
authType,
219227
unixSocketPathSuffix,
220228
domainName,
221-
config);
229+
config,
230+
mdxClientProtocolType);
222231
}
223232

224233
/** Creates a new instance of the ConnectionConfig with an updated cloudSqlInstance. */
@@ -231,7 +240,8 @@ public ConnectionConfig withCloudSqlInstance(String newCloudSqlInstance) {
231240
authType,
232241
unixSocketPathSuffix,
233242
domainName,
234-
connectorConfig);
243+
connectorConfig,
244+
mdxClientProtocolType);
235245
}
236246

237247
/** Creates a new instance of the ConnectionConfig with an updated cloudSqlInstance. */
@@ -244,7 +254,22 @@ public ConnectionConfig withDomainName(String domainName) {
244254
authType,
245255
unixSocketPathSuffix,
246256
domainName,
247-
connectorConfig);
257+
connectorConfig,
258+
mdxClientProtocolType);
259+
}
260+
261+
/** Creates a new instance of the ConnectionConfig with an updated clientProtocolType. */
262+
public ConnectionConfig withMdxClientProtocolType(String mdxClientProtocolType) {
263+
return new ConnectionConfig(
264+
cloudSqlInstance,
265+
namedConnector,
266+
unixSocketPath,
267+
ipTypes,
268+
authType,
269+
unixSocketPathSuffix,
270+
domainName,
271+
connectorConfig,
272+
mdxClientProtocolType);
248273
}
249274

250275
public String getNamedConnector() {
@@ -279,6 +304,10 @@ public String getDomainName() {
279304
return domainName;
280305
}
281306

307+
public String getMdxClientProtocolType() {
308+
return mdxClientProtocolType;
309+
}
310+
282311
/** The builder for the ConnectionConfig. */
283312
public static class Builder {
284313

@@ -290,6 +319,7 @@ public static class Builder {
290319
private ConnectorConfig connectorConfig = new ConnectorConfig.Builder().build();
291320
private AuthType authType = DEFAULT_AUTH_TYPE;
292321
private String domainName;
322+
private String mdxClientProtocolType;
293323

294324
/** Chained setter for CloudSqlInstance field. */
295325
public Builder withCloudSqlInstance(String cloudSqlInstance) {
@@ -345,6 +375,12 @@ public Builder withUnixSocketPathSuffix(String unixSocketPathSuffix) {
345375
return this;
346376
}
347377

378+
/** Chained setter for MdxClientProtocolType field. */
379+
public Builder withMdxClientProtocolType(String mdxClientProtocolType) {
380+
this.mdxClientProtocolType = mdxClientProtocolType;
381+
return this;
382+
}
383+
348384
/** Builds a new instance of {@code ConnectionConfig}. */
349385
public ConnectionConfig build() {
350386
return new ConnectionConfig(
@@ -355,7 +391,8 @@ public ConnectionConfig build() {
355391
authType,
356392
unixSocketPathSuffix,
357393
domainName,
358-
connectorConfig);
394+
connectorConfig,
395+
mdxClientProtocolType);
359396
}
360397
}
361398
}

core/src/main/java/com/google/cloud/sql/core/ConnectionInfo.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ ConnectionMetadata toConnectionMetadata(
7575
preferredIp,
7676
sslData.getKeyManagerFactory(),
7777
sslData.getTrustManagerFactory(),
78-
sslData.getSslContext());
78+
sslData.getSslContext(),
79+
instanceMetadata.getMdxProtocolSupport());
7980
}
8081
}

core/src/main/java/com/google/cloud/sql/core/ConnectionInfoRepositoryFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@
2323
interface ConnectionInfoRepositoryFactory {
2424

2525
ConnectionInfoRepository create(HttpRequestInitializer credentials, ConnectorConfig config);
26+
27+
String getUserAgents();
2628
}

core/src/main/java/com/google/cloud/sql/core/ConnectionMetadata.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.cloud.sql.core;
1818

19+
import java.util.List;
1920
import javax.net.ssl.KeyManagerFactory;
2021
import javax.net.ssl.SSLContext;
2122
import javax.net.ssl.TrustManagerFactory;
@@ -29,18 +30,21 @@ public class ConnectionMetadata {
2930
private final KeyManagerFactory keyManagerFactory;
3031
private final TrustManagerFactory trustManagerFactory;
3132
private final SSLContext sslContext;
33+
private final List<String> mdxProtocolSupport;
3234

3335
/** Construct an immutable ConnectionMetadata. */
3436
public ConnectionMetadata(
3537
String preferredIpAddress,
3638
KeyManagerFactory keyManagerFactory,
3739
TrustManagerFactory trustManagerFactory,
38-
SSLContext sslContext) {
40+
SSLContext sslContext,
41+
List<String> mdxProtocolSupport) {
3942

4043
this.preferredIpAddress = preferredIpAddress;
4144
this.keyManagerFactory = keyManagerFactory;
4245
this.trustManagerFactory = trustManagerFactory;
4346
this.sslContext = sslContext;
47+
this.mdxProtocolSupport = mdxProtocolSupport;
4448
}
4549

4650
public String getPreferredIpAddress() {
@@ -58,4 +62,12 @@ public TrustManagerFactory getTrustManagerFactory() {
5862
public SSLContext getSslContext() {
5963
return sslContext;
6064
}
65+
66+
public List<String> getMdxProtocolSupport() {
67+
return mdxProtocolSupport;
68+
}
69+
70+
public boolean isMdxClientProtocolTypeSupport() {
71+
return mdxProtocolSupport != null && mdxProtocolSupport.contains("CLIENT_PROTOCOL_TYPE");
72+
}
6173
}

core/src/main/java/com/google/cloud/sql/core/Connector.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class Connector {
5353

5454
private final InstanceConnectionNameResolver instanceNameResolver;
5555
private final Timer instanceNameResolverTimer;
56+
private final ProtocolHandler mdxProtocolHandler;
5657

5758
Connector(
5859
ConnectorConfig config,
@@ -63,7 +64,8 @@ class Connector {
6364
long minRefreshDelayMs,
6465
long refreshTimeoutMs,
6566
int serverProxyPort,
66-
InstanceConnectionNameResolver instanceNameResolver) {
67+
InstanceConnectionNameResolver instanceNameResolver,
68+
ProtocolHandler mdxProtocolHandler) {
6769
this.config = config;
6870

6971
this.adminApi =
@@ -75,6 +77,7 @@ class Connector {
7577
this.serverProxyPort = serverProxyPort;
7678
this.instanceNameResolver = instanceNameResolver;
7779
this.instanceNameResolverTimer = new Timer("InstanceNameResolverTimer", true);
80+
this.mdxProtocolHandler = mdxProtocolHandler;
7881
}
7982

8083
public ConnectorConfig getConfig() {
@@ -137,6 +140,11 @@ Socket connect(ConnectionConfig config, long timeoutMs) throws IOException {
137140
throw e;
138141
}
139142

143+
if (metadata.isMdxClientProtocolTypeSupport()
144+
&& !Strings.isNullOrEmpty(config.getMdxClientProtocolType())) {
145+
socket = mdxProtocolHandler.connect(socket, config.getMdxClientProtocolType());
146+
}
147+
140148
logger.debug(String.format("[%s] Connected to instance successfully.", instanceIp));
141149
instance.addSocket(socket);
142150

core/src/main/java/com/google/cloud/sql/core/DefaultConnectionInfoRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ private InstanceMetadata fetchMetadata(CloudSqlInstanceName instanceName, AuthTy
348348
instanceCaCertificates,
349349
isCasManagedCertificate(instanceMetadata.getServerCaMode()),
350350
serverName,
351-
pscEnabled);
351+
pscEnabled,
352+
instanceMetadata.getMdxProtocolSupport());
352353
} catch (CertificateException ex) {
353354
throw new RuntimeException(
354355
String.format(

0 commit comments

Comments
 (0)