From 707b9d14717572d5a8bb3ffd0d46e3108f7e7ee6 Mon Sep 17 00:00:00 2001 From: Mihai B Date: Mon, 20 Jan 2020 10:46:42 +0200 Subject: [PATCH 1/8] Use of quarkus-agroal with GCP CloudSQL #5901 --- .../asciidoc/deploying-to-google-cloud.adoc | 110 ++++++++++++++++++ .../agroal/test/GcpDataSourceConfigTest.java | 66 +++++++++++ .../application-gcp-datasource.properties | 18 +++ .../runtime/AbstractDataSourceProducer.java | 19 +++ .../runtime/DataSourceRuntimeConfig.java | 14 +++ 5 files changed, 227 insertions(+) create mode 100644 docs/src/main/asciidoc/deploying-to-google-cloud.adoc create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java create mode 100644 extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc new file mode 100644 index 0000000000000..039e7b462c5bd --- /dev/null +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -0,0 +1,110 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc +//// += Quarkus - Deploying on Google cloud platform + +include::./attributes.adoc[] + +This guide covers: + +* Connecting to a CloudSQL postgresql instance with quarkus-agroal and service account using CloudRun +== Prerequisites + +For this guide you need: + +* roughly 1 hour +* having access to an Google cloud platform project with owner rights. + +This guide will take as input an application developed in the link:datasource.adoc[datasource guide]. + +Make sure you have the application at hand working locally. + + +== Solution + +We recommend to follow the instructions in the next sections and build the application step by step. + +== Connecting to CloudSQL + +Google CloudSQL managed service allows 4 kinds of connection : + +. Using public IP +. Using private IP +. Using Cloud SQL Proxy +. Using service account + +The first two don't need anymore details, simply connect to you database following the link:datasource.adoc[datasource guide]. +The third allows you to connect as if locally, but is not available for AppEngine or CloudRun where you only deploy one artefact. +Please check link:https://cloud.google.com/sql/docs/postgres/external-connection-methods?hl=en[Connection options for external applications]. + +This guide will help you through the fourth possibility : connecting using service account. + +== Adding necessary dependencies to your application. + +You will need to add the _Cloud SQL Postgres Socket Factory_ and postgresql driver dependencies. + +With maven : +[source,xml, subs="attributes"] +---- + + + com.google.cloud.sql + postgres-socket-factory + 1.0.15 + + + + org.postgresql + postgresql + 42.2.8 + +---- + +With Gradle : +[source,groovy, subs="attributes"] +---- +// https://mvnrepository.com/artifact/com.google.cloud.sql/postgres-socket-factory +compile group: 'com.google.cloud.sql', name: 'postgres-socket-factory', version: '1.0.15' +// https://mvnrepository.com/artifact/org.postgresql/postgresql +compile group: 'org.postgresql', name: 'postgresql', version: '42.2.8' +---- + +== Configuring the CloudSQL instance + +If you haven't already please follow the link:https://cloud.google.com/sql/docs/postgres/create-instance[Creating instances guide]. + +Ensure you have a service account with _Cloud SQL Client_ role (minimum necessary role), and get your instance connection name (`PROJECT_ID:REGION:INSTANCE_ID`). + +== Configuring the application + +Configure your quarkus application as follows : + +[source,properties] +-- +quarkus.datasource.url=jdbc:postgresql:///${DB_NAME} +quarkus.datasource.driver=org.postgresql.Driver +quarkus.datasource.username = ${DB_USER} +quarkus.datasource.password = ${DB_PASSWORD} +quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory +quarkus.datasource.additional-jdbc-url-properties=cloud-sql-instance=project:zone:db-name +-- + +Prefere the use of environment variables that will allow you to connect to all your env depending on your runtime values + +WARNING: Don't forget to add your service account key to the deployed image, and to accept requests on the expected port + +[source,docker] +-- +FROM gcr.io/distroless/java:11 +COPY target/*.jar /app/application-runner.jar +COPY .json /app +WORKDIR /app +CMD ["application-runner.jar","-Dquarkus.http.host=0.0.0.0", "-Dquarkus.http.port=$PORT"] +-- + +NOTE: `$PORT` value will be injected automatically by CloudRun and is to be used for HealthCheck + + +You should now be able to connect to your instance. \ No newline at end of file diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java new file mode 100644 index 0000000000000..fe11b7c630600 --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java @@ -0,0 +1,66 @@ +package io.quarkus.agroal.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.SQLException; +import java.time.Duration; + +import javax.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.agroal.api.AgroalDataSource; +import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; +import io.agroal.narayana.NarayanaTransactionIntegration; +import io.quarkus.test.QuarkusUnitTest; + +public class GcpDataSourceConfigTest { + + //tag::injection[] + @Inject + AgroalDataSource defaultDataSource; + //end::injection[] + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withConfigurationResource("application-gcp-datasource.properties"); + + @Test + public void testGCPDataSourceInjection() throws SQLException { + testDataSource(defaultDataSource, "username-default", 3, 13, 7, Duration.ofSeconds(53), Duration.ofSeconds(54), + Duration.ofSeconds(55), Duration.ofSeconds(56), Duration.ofSeconds(57), + "create schema if not exists schema_default"); + } + + private static void testDataSource(AgroalDataSource dataSource, String username, int minSize, int maxSize, + int initialSize, Duration backgroundValidationInterval, Duration acquisitionTimeout, Duration leakDetectionInterval, + Duration idleRemovalInterval, Duration maxLifetime, String newConnectionSql) { + AgroalConnectionPoolConfiguration configuration = dataSource.getConfiguration().connectionPoolConfiguration(); + AgroalConnectionFactoryConfiguration agroalConnectionFactoryConfiguration = configuration + .connectionFactoryConfiguration(); + + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("socketFactory")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("socketFactory"), + "com.google.cloud.sql.postgres.SocketFactory"); + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("randomProperty")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("randomProperty"), "myProperty"); + assertEquals(username, agroalConnectionFactoryConfiguration.principal().getName()); + assertEquals(minSize, configuration.minSize()); + assertEquals(maxSize, configuration.maxSize()); + assertEquals(initialSize, configuration.initialSize()); + assertEquals(backgroundValidationInterval, configuration.validationTimeout()); + assertEquals(acquisitionTimeout, configuration.acquisitionTimeout()); + assertEquals(leakDetectionInterval, configuration.leakTimeout()); + assertEquals(idleRemovalInterval, configuration.reapTimeout()); + assertEquals(maxLifetime, configuration.maxLifetime()); + assertTrue(configuration.transactionIntegration() instanceof NarayanaTransactionIntegration); + assertEquals(AgroalConnectionFactoryConfiguration.TransactionIsolation.SERIALIZABLE, + agroalConnectionFactoryConfiguration.jdbcTransactionIsolation()); + assertTrue(agroalConnectionFactoryConfiguration.trackJdbcResources()); + assertTrue(dataSource.getConfiguration().metricsEnabled()); + assertEquals(newConnectionSql, agroalConnectionFactoryConfiguration.initialSql()); + } +} diff --git a/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties b/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties new file mode 100644 index 0000000000000..44f2cdc4335b3 --- /dev/null +++ b/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties @@ -0,0 +1,18 @@ +#tag::basic[] +quarkus.datasource.url=jdbc:h2:tcp://localhost/mem:default +quarkus.datasource.driver=org.h2.Driver +quarkus.datasource.username=username-default +quarkus.datasource.min-size=3 +quarkus.datasource.max-size=13 +quarkus.datasource.enable-metrics=true +#end::basic[] +quarkus.datasource.initial-size=7 +quarkus.datasource.background-validation-interval=53 +quarkus.datasource.acquisition-timeout=54 +quarkus.datasource.leak-detection-interval=55 +quarkus.datasource.idle-removal-interval=56 +quarkus.datasource.max-lifetime=57 +quarkus.datasource.transaction-isolation-level=serializable +quarkus.datasource.new-connection-sql=create schema if not exists schema_default +quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory +quarkus.datasource.additional-jdbc-properties=cloud-sql-instance=project:zone:db-name,randomProperty=myProperty diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java index 81b043460ea92..cd303a2bc48a6 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java @@ -4,6 +4,7 @@ import java.sql.Driver; import java.sql.Statement; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -201,6 +202,24 @@ public boolean isValid(Connection connection) { poolConfiguration.maxLifetime(dataSourceRuntimeConfig.maxLifetime.get()); } + if (dataSourceRuntimeConfig.socketFactory.isPresent()) { + agroalConnectionFactoryConfigurationSupplier.jdbcProperty("socketFactory", + dataSourceRuntimeConfig.socketFactory.get()); + } + + if (dataSourceRuntimeConfig.additionalJdbcProperties.isPresent()) { + String generalProperties = dataSourceRuntimeConfig.additionalJdbcProperties.get(); + List properties = Arrays.asList(generalProperties.split(",")); + for (String property : properties) { + String[] propertyData = property.split("="); + if (propertyData.length == 2) { + agroalConnectionFactoryConfigurationSupplier.jdbcProperty(propertyData[0], propertyData[1]); + } else { + log.warnv("Property {0} is not correctly formatted", property); + } + } + } + // SSL support: we should push the driver specific code to the driver extensions but it will have to do for now if (disableSslSupport) { switch (driverName) { diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java index 11585516146c5..5ff918fc704f6 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java @@ -124,4 +124,18 @@ public class DataSourceRuntimeConfig { */ @ConfigItem public Optional validationQuerySql; + + /** + * The socket factory driver that should be appended as a datasource url property + */ + @ConfigItem + public Optional socketFactory; + + /** + * General JDBC properties that can configured for a specific JDBC connection + * + * Should be comma separated, example: =,= + */ + @ConfigItem + public Optional additionalJdbcProperties; } From d4a8325a710bc0f5852ca4f1e07d7767c192e80d Mon Sep 17 00:00:00 2001 From: Mihai B Date: Mon, 20 Jan 2020 10:50:29 +0200 Subject: [PATCH 2/8] Use of quarkus-agroal with GCP CloudSQL #5901 --- docs/src/main/asciidoc/deploying-to-google-cloud.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index 039e7b462c5bd..2015446ed4b43 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -88,7 +88,7 @@ quarkus.datasource.driver=org.postgresql.Driver quarkus.datasource.username = ${DB_USER} quarkus.datasource.password = ${DB_PASSWORD} quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory -quarkus.datasource.additional-jdbc-url-properties=cloud-sql-instance=project:zone:db-name +quarkus.datasource.additional-jdbc-properties=cloud-sql-instance=project:zone:db-name -- Prefere the use of environment variables that will allow you to connect to all your env depending on your runtime values From 1f9f77bdefcdad992c1cfeb6b354afff775450cd Mon Sep 17 00:00:00 2001 From: Mihai B Date: Mon, 20 Jan 2020 13:33:56 +0200 Subject: [PATCH 3/8] modified docs: updated dependencies --- docs/src/main/asciidoc/deploying-to-google-cloud.adoc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index 2015446ed4b43..40c9f7f9de1e3 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -54,11 +54,10 @@ With maven : postgres-socket-factory 1.0.15 - + - org.postgresql - postgresql - 42.2.8 + io.quarkus + quarkus-jdbc-postgresql ---- @@ -67,8 +66,8 @@ With Gradle : ---- // https://mvnrepository.com/artifact/com.google.cloud.sql/postgres-socket-factory compile group: 'com.google.cloud.sql', name: 'postgres-socket-factory', version: '1.0.15' -// https://mvnrepository.com/artifact/org.postgresql/postgresql -compile group: 'org.postgresql', name: 'postgresql', version: '42.2.8' +// https://mvnrepository.com/artifact/io.quarkus/quarkus-jdbc-postgresql +compile group: 'io.quarkus', name: 'quarkus-jdbc-postgresql', version: '1.1.1.Final' ---- == Configuring the CloudSQL instance From 87c55f30d5890dc5f158920eb6dc05a5d32898b7 Mon Sep 17 00:00:00 2001 From: Mihai B Date: Mon, 20 Jan 2020 14:06:21 +0200 Subject: [PATCH 4/8] fixed documentation typos --- docs/src/main/asciidoc/deploying-to-google-cloud.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index 40c9f7f9de1e3..e0fdf4d4d8ed3 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -35,8 +35,8 @@ Google CloudSQL managed service allows 4 kinds of connection : . Using Cloud SQL Proxy . Using service account -The first two don't need anymore details, simply connect to you database following the link:datasource.adoc[datasource guide]. -The third allows you to connect as if locally, but is not available for AppEngine or CloudRun where you only deploy one artefact. +The first two don't need anymore details, simply connect to your database following the link:datasource.adoc[datasource guide]. +The third allows you to connect as if locally, but is not available for AppEngine or CloudRun where you only deploy one artifact. Please check link:https://cloud.google.com/sql/docs/postgres/external-connection-methods?hl=en[Connection options for external applications]. This guide will help you through the fourth possibility : connecting using service account. @@ -90,7 +90,7 @@ quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory quarkus.datasource.additional-jdbc-properties=cloud-sql-instance=project:zone:db-name -- -Prefere the use of environment variables that will allow you to connect to all your env depending on your runtime values +NOTE: To set the environment variables(DB_NAME, DB_USER, DB_PASSWORD) when deploying to CloudRun there is an option `ADD VARIABLE` WARNING: Don't forget to add your service account key to the deployed image, and to accept requests on the expected port From c7b811cf5ec45fe3bc3485f57ed1717133e1e7f3 Mon Sep 17 00:00:00 2001 From: Mihai B Date: Tue, 21 Jan 2020 10:28:41 +0200 Subject: [PATCH 5/8] Use quarkus with Google Cloud Run And CloudSQL --- .../asciidoc/deploying-to-google-cloud.adoc | 24 ++++++------------- .../agroal/test/GcpDataSourceConfigTest.java | 3 +++ .../application-gcp-datasource.properties | 3 ++- .../runtime/AbstractDataSourceProducer.java | 16 ++++--------- .../runtime/DataSourceRuntimeConfig.java | 5 ++-- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index e0fdf4d4d8ed3..782a365a20818 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -9,7 +9,8 @@ include::./attributes.adoc[] This guide covers: -* Connecting to a CloudSQL postgresql instance with quarkus-agroal and service account using CloudRun +Connecting to a CloudSQL postgresql instance with quarkus-agroal and service account using CloudRun + == Prerequisites For this guide you need: @@ -17,7 +18,7 @@ For this guide you need: * roughly 1 hour * having access to an Google cloud platform project with owner rights. -This guide will take as input an application developed in the link:datasource.adoc[datasource guide]. +This guide will take as input an application developed in the link:datasource[datasource guide]. Make sure you have the application at hand working locally. @@ -35,7 +36,7 @@ Google CloudSQL managed service allows 4 kinds of connection : . Using Cloud SQL Proxy . Using service account -The first two don't need anymore details, simply connect to your database following the link:datasource.adoc[datasource guide]. +The first two don't need more details, simply connect to your database following the link:datasource[datasource guide]. The third allows you to connect as if locally, but is not available for AppEngine or CloudRun where you only deploy one artifact. Please check link:https://cloud.google.com/sql/docs/postgres/external-connection-methods?hl=en[Connection options for external applications]. @@ -43,33 +44,22 @@ This guide will help you through the fourth possibility : connecting using servi == Adding necessary dependencies to your application. -You will need to add the _Cloud SQL Postgres Socket Factory_ and postgresql driver dependencies. +You will need to add the _Cloud SQL Postgres Socket Factory_ and the PostgreSQL JDBC driver extension. With maven : [source,xml, subs="attributes"] ---- - com.google.cloud.sql postgres-socket-factory 1.0.15 - io.quarkus quarkus-jdbc-postgresql ---- -With Gradle : -[source,groovy, subs="attributes"] ----- -// https://mvnrepository.com/artifact/com.google.cloud.sql/postgres-socket-factory -compile group: 'com.google.cloud.sql', name: 'postgres-socket-factory', version: '1.0.15' -// https://mvnrepository.com/artifact/io.quarkus/quarkus-jdbc-postgresql -compile group: 'io.quarkus', name: 'quarkus-jdbc-postgresql', version: '1.1.1.Final' ----- - == Configuring the CloudSQL instance If you haven't already please follow the link:https://cloud.google.com/sql/docs/postgres/create-instance[Creating instances guide]. @@ -87,10 +77,10 @@ quarkus.datasource.driver=org.postgresql.Driver quarkus.datasource.username = ${DB_USER} quarkus.datasource.password = ${DB_PASSWORD} quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory -quarkus.datasource.additional-jdbc-properties=cloud-sql-instance=project:zone:db-name +quarkus.datasource.additional-jdbc-properties.cloudSqlInstance=project:zone:instance-name -- -NOTE: To set the environment variables(DB_NAME, DB_USER, DB_PASSWORD) when deploying to CloudRun there is an option `ADD VARIABLE` +NOTE: To set the environment variables (DB_NAME, DB_USER, DB_PASSWORD) when deploying to CloudRun there is an option `ADD VARIABLE` WARNING: Don't forget to add your service account key to the deployed image, and to accept requests on the expected port diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java index fe11b7c630600..822b4b08e1fff 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java @@ -47,6 +47,9 @@ private static void testDataSource(AgroalDataSource dataSource, String username, "com.google.cloud.sql.postgres.SocketFactory"); assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("randomProperty")); assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("randomProperty"), "myProperty"); + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("cloudSqlInstance")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("cloudSqlInstance"), + "project:zone:db-name"); assertEquals(username, agroalConnectionFactoryConfiguration.principal().getName()); assertEquals(minSize, configuration.minSize()); assertEquals(maxSize, configuration.maxSize()); diff --git a/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties b/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties index 44f2cdc4335b3..f9d6bfe36081b 100644 --- a/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties +++ b/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties @@ -15,4 +15,5 @@ quarkus.datasource.max-lifetime=57 quarkus.datasource.transaction-isolation-level=serializable quarkus.datasource.new-connection-sql=create schema if not exists schema_default quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory -quarkus.datasource.additional-jdbc-properties=cloud-sql-instance=project:zone:db-name,randomProperty=myProperty +quarkus.datasource.additional-jdbc-properties.cloudSqlInstance=project:zone:instance-name +quarkus.datasource.additional-jdbc-properties.randomProperty=myProperty diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java index cd303a2bc48a6..46f5be70be266 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AbstractDataSourceProducer.java @@ -4,8 +4,8 @@ import java.sql.Driver; import java.sql.Statement; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import javax.annotation.PreDestroy; @@ -207,16 +207,10 @@ public boolean isValid(Connection connection) { dataSourceRuntimeConfig.socketFactory.get()); } - if (dataSourceRuntimeConfig.additionalJdbcProperties.isPresent()) { - String generalProperties = dataSourceRuntimeConfig.additionalJdbcProperties.get(); - List properties = Arrays.asList(generalProperties.split(",")); - for (String property : properties) { - String[] propertyData = property.split("="); - if (propertyData.length == 2) { - agroalConnectionFactoryConfigurationSupplier.jdbcProperty(propertyData[0], propertyData[1]); - } else { - log.warnv("Property {0} is not correctly formatted", property); - } + if (!dataSourceRuntimeConfig.additionalJdbcProperties.isEmpty()) { + Map generalProperties = dataSourceRuntimeConfig.additionalJdbcProperties; + for (String propertyName : dataSourceRuntimeConfig.additionalJdbcProperties.keySet()) { + agroalConnectionFactoryConfigurationSupplier.jdbcProperty(propertyName, generalProperties.get(propertyName)); } } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java index 5ff918fc704f6..ffc4595e47703 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java @@ -1,6 +1,7 @@ package io.quarkus.agroal.runtime; import java.time.Duration; +import java.util.Map; import java.util.Optional; import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration; @@ -133,9 +134,7 @@ public class DataSourceRuntimeConfig { /** * General JDBC properties that can configured for a specific JDBC connection - * - * Should be comma separated, example: =,= */ @ConfigItem - public Optional additionalJdbcProperties; + public Map additionalJdbcProperties; } From 655d3fdf7471c3587d0663e20afd3c7f800ed0e0 Mon Sep 17 00:00:00 2001 From: Mihai B Date: Tue, 21 Jan 2020 10:39:35 +0200 Subject: [PATCH 6/8] Use quarkus with Google Cloud Run And CloudSQL --- docs/src/main/asciidoc/deploying-to-google-cloud.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index 782a365a20818..3c1ef4f978faa 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -46,7 +46,7 @@ This guide will help you through the fourth possibility : connecting using servi You will need to add the _Cloud SQL Postgres Socket Factory_ and the PostgreSQL JDBC driver extension. -With maven : +.Example dependencies using Maven [source,xml, subs="attributes"] ---- From 708b1135bb1e08720ed00e1c9ebed18fbd89f5ab Mon Sep 17 00:00:00 2001 From: Mihai B Date: Tue, 21 Jan 2020 11:05:32 +0200 Subject: [PATCH 7/8] Updated junit tests --- .../test/DefaultDataSourceConfigTest.java | 9 +++ .../agroal/test/GcpDataSourceConfigTest.java | 69 ------------------- .../application-default-datasource.properties | 5 +- .../application-gcp-datasource.properties | 19 ----- .../runtime/DataSourceRuntimeConfig.java | 2 +- 5 files changed, 14 insertions(+), 90 deletions(-) delete mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java delete mode 100644 extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java index ad3495062885c..1b07b35887cd0 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java @@ -59,6 +59,15 @@ private static void testDataSource(AgroalDataSource dataSource, String username, assertTrue(agroalConnectionFactoryConfiguration.trackJdbcResources()); assertTrue(dataSource.getConfiguration().metricsEnabled()); assertEquals(newConnectionSql, agroalConnectionFactoryConfiguration.initialSql()); + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("socketFactory")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("socketFactory"), + "pass socket factory provider as jdbc property"); + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("extraProperty1")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("extraProperty1"), + "extraProperty1Value"); + assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("extraProperty2")); + assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("extraProperty2"), + "extraProperty2Value"); try (Connection connection = dataSource.getConnection()) { } } diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java deleted file mode 100644 index 822b4b08e1fff..0000000000000 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/GcpDataSourceConfigTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.quarkus.agroal.test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.sql.SQLException; -import java.time.Duration; - -import javax.inject.Inject; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.agroal.api.AgroalDataSource; -import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration; -import io.agroal.api.configuration.AgroalConnectionPoolConfiguration; -import io.agroal.narayana.NarayanaTransactionIntegration; -import io.quarkus.test.QuarkusUnitTest; - -public class GcpDataSourceConfigTest { - - //tag::injection[] - @Inject - AgroalDataSource defaultDataSource; - //end::injection[] - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("application-gcp-datasource.properties"); - - @Test - public void testGCPDataSourceInjection() throws SQLException { - testDataSource(defaultDataSource, "username-default", 3, 13, 7, Duration.ofSeconds(53), Duration.ofSeconds(54), - Duration.ofSeconds(55), Duration.ofSeconds(56), Duration.ofSeconds(57), - "create schema if not exists schema_default"); - } - - private static void testDataSource(AgroalDataSource dataSource, String username, int minSize, int maxSize, - int initialSize, Duration backgroundValidationInterval, Duration acquisitionTimeout, Duration leakDetectionInterval, - Duration idleRemovalInterval, Duration maxLifetime, String newConnectionSql) { - AgroalConnectionPoolConfiguration configuration = dataSource.getConfiguration().connectionPoolConfiguration(); - AgroalConnectionFactoryConfiguration agroalConnectionFactoryConfiguration = configuration - .connectionFactoryConfiguration(); - - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("socketFactory")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("socketFactory"), - "com.google.cloud.sql.postgres.SocketFactory"); - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("randomProperty")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("randomProperty"), "myProperty"); - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("cloudSqlInstance")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("cloudSqlInstance"), - "project:zone:db-name"); - assertEquals(username, agroalConnectionFactoryConfiguration.principal().getName()); - assertEquals(minSize, configuration.minSize()); - assertEquals(maxSize, configuration.maxSize()); - assertEquals(initialSize, configuration.initialSize()); - assertEquals(backgroundValidationInterval, configuration.validationTimeout()); - assertEquals(acquisitionTimeout, configuration.acquisitionTimeout()); - assertEquals(leakDetectionInterval, configuration.leakTimeout()); - assertEquals(idleRemovalInterval, configuration.reapTimeout()); - assertEquals(maxLifetime, configuration.maxLifetime()); - assertTrue(configuration.transactionIntegration() instanceof NarayanaTransactionIntegration); - assertEquals(AgroalConnectionFactoryConfiguration.TransactionIsolation.SERIALIZABLE, - agroalConnectionFactoryConfiguration.jdbcTransactionIsolation()); - assertTrue(agroalConnectionFactoryConfiguration.trackJdbcResources()); - assertTrue(dataSource.getConfiguration().metricsEnabled()); - assertEquals(newConnectionSql, agroalConnectionFactoryConfiguration.initialSql()); - } -} diff --git a/extensions/agroal/deployment/src/test/resources/application-default-datasource.properties b/extensions/agroal/deployment/src/test/resources/application-default-datasource.properties index 74d72ce5e6dfb..164489a2daba8 100644 --- a/extensions/agroal/deployment/src/test/resources/application-default-datasource.properties +++ b/extensions/agroal/deployment/src/test/resources/application-default-datasource.properties @@ -13,4 +13,7 @@ quarkus.datasource.leak-detection-interval=55 quarkus.datasource.idle-removal-interval=56 quarkus.datasource.max-lifetime=57 quarkus.datasource.transaction-isolation-level=serializable -quarkus.datasource.new-connection-sql=create schema if not exists schema_default \ No newline at end of file +quarkus.datasource.new-connection-sql=create schema if not exists schema_default +quarkus.datasource.socket-factory=pass socket factory provider as jdbc property +quarkus.datasource.additional-jdbc-properties.extraProperty1=extraProperty1Value +quarkus.datasource.additional-jdbc-properties.extraProperty2=extraProperty2Value diff --git a/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties b/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties deleted file mode 100644 index f9d6bfe36081b..0000000000000 --- a/extensions/agroal/deployment/src/test/resources/application-gcp-datasource.properties +++ /dev/null @@ -1,19 +0,0 @@ -#tag::basic[] -quarkus.datasource.url=jdbc:h2:tcp://localhost/mem:default -quarkus.datasource.driver=org.h2.Driver -quarkus.datasource.username=username-default -quarkus.datasource.min-size=3 -quarkus.datasource.max-size=13 -quarkus.datasource.enable-metrics=true -#end::basic[] -quarkus.datasource.initial-size=7 -quarkus.datasource.background-validation-interval=53 -quarkus.datasource.acquisition-timeout=54 -quarkus.datasource.leak-detection-interval=55 -quarkus.datasource.idle-removal-interval=56 -quarkus.datasource.max-lifetime=57 -quarkus.datasource.transaction-isolation-level=serializable -quarkus.datasource.new-connection-sql=create schema if not exists schema_default -quarkus.datasource.socket-factory=com.google.cloud.sql.postgres.SocketFactory -quarkus.datasource.additional-jdbc-properties.cloudSqlInstance=project:zone:instance-name -quarkus.datasource.additional-jdbc-properties.randomProperty=myProperty diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java index ffc4595e47703..4c25512bac7a1 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceRuntimeConfig.java @@ -133,7 +133,7 @@ public class DataSourceRuntimeConfig { public Optional socketFactory; /** - * General JDBC properties that can configured for a specific JDBC connection + * General properties that should be applied for a JDBC */ @ConfigItem public Map additionalJdbcProperties; From 32def54d5d2efc95672f34ec9c38b390147bbe5b Mon Sep 17 00:00:00 2001 From: Mihai B Date: Wed, 29 Jan 2020 18:10:24 +0200 Subject: [PATCH 8/8] updated tests --- extensions/agroal/deployment/pom.xml | 5 +++++ .../test/DefaultDataSourceConfigTest.java | 17 ++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/extensions/agroal/deployment/pom.xml b/extensions/agroal/deployment/pom.xml index 7449ad4ad97e1..e0ff095c47c17 100644 --- a/extensions/agroal/deployment/pom.xml +++ b/extensions/agroal/deployment/pom.xml @@ -59,6 +59,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + io.quarkus quarkus-test-h2 diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java index 1b07b35887cd0..b74a44d94de0d 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DefaultDataSourceConfigTest.java @@ -1,5 +1,8 @@ package io.quarkus.agroal.test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -59,15 +62,11 @@ private static void testDataSource(AgroalDataSource dataSource, String username, assertTrue(agroalConnectionFactoryConfiguration.trackJdbcResources()); assertTrue(dataSource.getConfiguration().metricsEnabled()); assertEquals(newConnectionSql, agroalConnectionFactoryConfiguration.initialSql()); - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("socketFactory")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("socketFactory"), - "pass socket factory provider as jdbc property"); - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("extraProperty1")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("extraProperty1"), - "extraProperty1Value"); - assertTrue(agroalConnectionFactoryConfiguration.jdbcProperties().containsKey("extraProperty2")); - assertEquals(agroalConnectionFactoryConfiguration.jdbcProperties().getProperty("extraProperty2"), - "extraProperty2Value"); + assertThat(agroalConnectionFactoryConfiguration.jdbcProperties()) + .contains(entry("socketFactory", "pass socket factory provider as jdbc property"), + entry("extraProperty1", "extraProperty1Value"), + entry("extraProperty2", "extraProperty2Value")); + assertThatCode(() -> dataSource.getConnection().close()).doesNotThrowAnyException(); try (Connection connection = dataSource.getConnection()) { } }