Dev Services

Quarkus supports the automatic provisioning of unconfigured services in dev and test mode. We refer to this capability as Dev Services. From a developer’s perspective this means that if you include an extension and don’t configure it then Quarkus will automatically start the relevant service (usually using Testcontainers behind the scenes) and wire up your application to use this service, even pre-configuring access credentials.

Dev Services will only be enabled in dev/test mode, so it will not affect the application running in production. If you want to disable all Dev Services you can use the quarkus.devservices.enabled=false config property, or you can simply configure the service in which case it will result in the Dev Service being disabled automatically.
If you are using a proprietary database such as DB2 or MSSQL you will need to accept the license agreement. To do this, create a src/main/resources/container-license-acceptance.txt file in your project and add a line with the image name and tag of the database.

More on zero config setup of datasources can be found here.

Replace Database Extensions

Instead of the built-in h2 database, we’re now going to use an external database. Swapping out from one database provider to another is fairly trivial with Quarkus. Let’s start with updating the configuration in the application.properties.

You technically don’t need to add the db-kind property since there is only one jdbc driver in our application. We added it for clarity’s sake.
# Configuration file
# key = value
greeting=Hello y'all!
quarkus.datasource.db-kind=postgresql
quarkus.hibernate-orm.database.generation=drop-and-create
Make sure the h2 db-kind and jdbc.url properties have been removed.

Now let’s swap the h2 extension out for the postgresql extension:

  • Maven

  • Quarkus CLI

./mvnw quarkus:remove-extension -Dextension=quarkus-jdbc-h2
./mvnw quarkus:add-extension -Dextension=quarkus-jdbc-postgresql
quarkus extension remove quarkus-jdbc-h2
quarkus extension add quarkus-jdbc-postgresql

Notice in the logs how Quarkus has reloaded and started up a Postgresql database dev service:

2023-03-16 08:06:56,882 INFO  [io.qua.dat.dep.dev.DevServicesDatasourceProcessor] (build-13) Dev Services for the default datasource (postgresql) started - container ID is c7c9a6ccf029

Verify Postgresql testcontainer is running

Let’s verify in Docker/Podman that the dev services container is running. You should see 2 containers, one testcontainers/ryuk container which orchestrates the dev services, and another postgres container which is running the database and which is automatically wired into our Quarkus dev mode:

docker ps
Dev Services Container

Call the fruit endpoint again. The data is now coming from the postgresql database container.

curl localhost:8080/fruit?season=Spring
[
  {
    "id": 1,
    "name": "Mango",
    "season": "Spring"
  },
  {
    "id": 2,
    "name": "Strawberry",
    "season": "Spring"
  }
]

Run Unit Tests with TestContainers

Let’s stop dev mode for now to verify that the Postgres container also stops when we’re done with dev mode by sending a CTRL+C in the terminal and checking again with docker ps. Notice that the postgres container has disappeared as expected.

docker ps
Dev Services Stopped

Run the tests:

  • Maven

  • Quarkus CLI

./mvnw test
quarkus test

Reminder: Once the tests have run, send CTRL+C since Quarkus CLI starts tests in continuous mode.

Notice in the logs that Quarkus and TestContainers work in unison to spin up the Postgres container again, run the tests, and then tear down the container again once done.

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.redhat.developers.GreetingResourceTest
2023-07-04 17:40:04,096 INFO  [org.tes.doc.DockerClientProviderStrategy] (build-23) Found Docker environment with Environment variables, system properties and defaults. Resolved dockerHost=unix:///run/user/1000/podman/podman.sock
2023-07-04 17:40:04,100 INFO  [org.tes.DockerClientFactory] (build-23) Docker host IP address is localhost
2023-07-04 17:40:04,235 INFO  [org.tes.DockerClientFactory] (build-23) Connected to docker:
  Server Version: 4.5.1
  API Version: 1.41
  Operating System: fedora
  Total Memory: 31787 MB
2023-07-04 17:40:04,254 INFO  [org.tes.uti.ImageNameSubstitutor] (build-23) Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2023-07-04 17:40:04,257 INFO  [org.tes.DockerClientFactory] (build-23) Checking the system...
2023-07-04 17:40:04,258 INFO  [org.tes.DockerClientFactory] (build-23) ✔︎ Docker server version should be at least 1.6.0
2023-07-04 17:40:05,038 INFO  [tc.doc.io/postgres:14] (build-23) Creating container for image: docker.io/postgres:14

[Optional] Re-deploy to Kubernetes

If you would like to redeploy to Kubernetes, this time you will need to provision a Postgresql database because as mentioned before, Dev Services are only enabled in dev/test mode.

We will also need to add credentials to connect to the external database. Make sure you have the following properties set:

%prod.quarkus.datasource.password=quarkus
%prod.quarkus.datasource.username=quarkus
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql/quarkus
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.sql-load-script=import.sql
We added a %prod. prefix to some of the properties. This prefix makes it so these properties will only be evaluated with the (default) prod profile. In dev mode these values will be ignored, thus triggering the Dev Services creation.

Create a postgresql database

There are several ways to deploy a Postgresql Database to Kubernetes. If you’re using Openshift, you could create one easily through the UI (Developer Perspective > +Add > Database > PostgreSQL). Make sure your database name, username and password match up with what you have configured in your application.properties or secrets.

Alternatively you can also create the following Kubernetes manifest for a simple ephemeral instance:

cat > postgres.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
spec:
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
        - name: postgresql
          image: quay.io/redhatdemo/openshift-pgsql12-primary:centos7
          imagePullPolicy: Always
          ports:
            - name: tcp
              containerPort: 5432
          env:
            - name: PG_USER_PASSWORD
              value: quarkus
            - name: PG_USER_NAME
              value: quarkus
            - name: PG_DATABASE
              value: quarkus
            - name: PG_NETWORK_MASK
              value: all
---
kind: Service
apiVersion: v1
metadata:
  name: postgresql
spec:
  ports:
    - name: pgsql
      protocol: TCP
      port: 5432
      targetPort: 5432
  type: ClusterIP
  selector:
    app: postgresql
EOF

And deploy it to Kubernetes:

kubectl apply -f postgres.yaml

Rebuild and deploy your application

Rebuild the application and container, and push to your registry again:

  • Maven

  • Quarkus CLI

./mvnw clean package -DskipTests -D"quarkus.container-image.push=true"
quarkus image push --also-build --no-tests

And finally, redeploy the application:

kubectl apply -f target/kubernetes/kubernetes.yml