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..3c1ef4f978faa --- /dev/null +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -0,0 +1,99 @@ +//// +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[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 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]. + +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 the PostgreSQL JDBC driver extension. + +.Example dependencies using Maven +[source,xml, subs="attributes"] +---- + + com.google.cloud.sql + postgres-socket-factory + 1.0.15 + + + io.quarkus + quarkus-jdbc-postgresql + +---- + +== 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-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` + +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/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 ad3495062885c..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,6 +62,11 @@ private static void testDataSource(AgroalDataSource dataSource, String username, assertTrue(agroalConnectionFactoryConfiguration.trackJdbcResources()); assertTrue(dataSource.getConfiguration().metricsEnabled()); assertEquals(newConnectionSql, agroalConnectionFactoryConfiguration.initialSql()); + 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()) { } } 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/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..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 @@ -5,6 +5,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import javax.annotation.PreDestroy; @@ -201,6 +202,18 @@ public boolean isValid(Connection connection) { poolConfiguration.maxLifetime(dataSourceRuntimeConfig.maxLifetime.get()); } + if (dataSourceRuntimeConfig.socketFactory.isPresent()) { + agroalConnectionFactoryConfigurationSupplier.jdbcProperty("socketFactory", + dataSourceRuntimeConfig.socketFactory.get()); + } + + if (!dataSourceRuntimeConfig.additionalJdbcProperties.isEmpty()) { + Map generalProperties = dataSourceRuntimeConfig.additionalJdbcProperties; + for (String propertyName : dataSourceRuntimeConfig.additionalJdbcProperties.keySet()) { + agroalConnectionFactoryConfigurationSupplier.jdbcProperty(propertyName, generalProperties.get(propertyName)); + } + } + // 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..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 @@ -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; @@ -124,4 +125,16 @@ 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 properties that should be applied for a JDBC + */ + @ConfigItem + public Map additionalJdbcProperties; }