Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use quarkus with Google Cloud Run And CloudSQL #6634

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions docs/src/main/asciidoc/deploying-to-google-cloud.adoc
Original file line number Diff line number Diff line change
@@ -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"]
----
<dependency>
<groupId>com.google.cloud.sql</groupId>
<artifactId>postgres-socket-factory</artifactId>
<version>1.0.15</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
----

== 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 <your_service_account_key>.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.
5 changes: 5 additions & 0 deletions extensions/agroal/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-h2</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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()) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -201,6 +202,18 @@ public boolean isValid(Connection connection) {
poolConfiguration.maxLifetime(dataSourceRuntimeConfig.maxLifetime.get());
}

if (dataSourceRuntimeConfig.socketFactory.isPresent()) {
agroalConnectionFactoryConfigurationSupplier.jdbcProperty("socketFactory",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since socketFactory is a JDBC property, the best is to have it declared in the additionalJdbcProperties, like in:

quarkus.datasource.additional-jdbc-properties.socketFactory=foobar

dataSourceRuntimeConfig.socketFactory.get());
}

if (!dataSourceRuntimeConfig.additionalJdbcProperties.isEmpty()) {
Map<String, String> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -124,4 +125,16 @@ public class DataSourceRuntimeConfig {
*/
@ConfigItem
public Optional<String> validationQuerySql;

/**
* The socket factory driver that should be appended as a datasource url property
*/
@ConfigItem
public Optional<String> socketFactory;
Comment on lines +128 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed since the configuration is set as a JDBC property and could live in the additionalJdbcProperties Map below


/**
* General properties that should be applied for a JDBC
*/
@ConfigItem
public Map<String, String> additionalJdbcProperties;
}