= Cloud Native
:experimental:

In this step we will package the application as a Linux Container image, and deploy it to Kubernetes, and add a few features common to cloud native apps that you as a developer will need to handle. We'll use OpenShift 4 as our deployment target, which is a distribution of Kubernetes from Red Hat.

== Health Probes

Quarkus application developers can utilize the MicroProfile Health specification to write HTTP health probes for their applications. These endpoints by default provide basic data about the service however they all provide a way to customize the health data and add more meaningful information (e.g. database connection health, backoffice system availability, etc).

[NOTE]
====
There are of course a category of issues that can't be resolved by restarting the container. In those scenarios, the container never recovers and traffic will no longer be sent to it (which can have cascading effects on the rest of the system, possibly requiring human intervention, which is why monitoring is crucial to availability).
====

=== Add Extension

Let's build a simple REST application endpoint exposes https://microprofile.io[MicroProfile,window=_blank] Health checks at the `/health` endpoint according to the specification. It will also provide several other REST endpoints to allow us to dynamically query the health of our Quarkus application.

We'll need to add a https://quarkus.io/extensions[Quarkus Extension,window=_blank] to enable this feature in our app. Fortunately, adding a Quarkus extension is super easy. We'll cover extensions in more depth in other sections of this workshop but for now, open a Terminal and execute the following command to add the extension to our project's `pom.xml`:

[source, sh, role="copypaste"]
----
mvn quarkus:add-extension -Dextensions="health"
----

This will add the extension below to your `pom.xml`:

[source, xml]
----
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-health</artifactId>
</dependency>
----

With no code, Quarkus still provides a default health check which may be enough for you if all you need is to know the app started. Try to access the `/health/ready` endpoint on the Terminal:

[source, sh, role="copypaste"]
----
curl http://localhost:8080/health/ready
----

You'll see:

[source,json]
----
{
    "status": "UP",
    "checks": [
    ]
}
----

This default health check will return success as long as the app is running - if it crashes, the health check will of course fail.

=== Add a probe

We can now implement a better Health Check using the MicroProfile APIs. Create a new Java class - `org.acme.people.health.SimpleHealthCheck` (hint: right-click on the `org.acme.people.health` package and select _New > Java Class_ and name it `SimpleHealthCheck`). In this file, implement the health check (you can copy/paste this code):

[source, java, role="copypaste"]
----
package org.acme.people.health;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
@Readiness
public class SimpleHealthCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named("Simple health check").up().build();
    }
}
----

As you can see health check procedures are defined as implementations of the `HealthCheck` interface which are defined as CDI beans with the either the `@Readiness` or `@Liveness` annotation. `HealthCheck` is a functional interface whose single method `call` returns a `HealthCheckResponse` object which can be easily constructed by the fluent builder API shown above. This simple example will serve as our _Readiness_ probe.

[NOTE]
====
There are two types of probes in Quarkus apps (and Kubernetes):

* Liveness Probe - Many applications running for long periods of time eventually transition to broken states, and cannot recover except by being restarted. Kubernetes provides liveness probes to detect and remedy such situations. Restarting a container in such a state can help to make the application more available despite bugs.
* Readiness Probe - Sometimes, applications are temporarily unable to serve traffic. For example, an application might need to load large data or configuration files during startup, or depend on external services after startup. In such cases, you don’t want to kill the application, but you don’t want to send it requests either. Kubernetes provides readiness probes to detect and mitigate these situations. A pod with containers reporting that they are not ready does not receive traffic through Kubernetes Services.

Readiness and liveness probes can be used in parallel for the same container. Using both can ensure that traffic does not reach a container that is not ready for it, and that containers are restarted when they fail. There are various https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/[Configuration Paramters, window=_blank] you can set, such as the timeout period, frequency, and other parameters that can be tuned to expected application behavior.
====

Thanks to Live Coding mode, simply open a Terminal window and run:

[source, sh, role="copypaste"]
----
curl http://localhost:8080/health/ready
----

The new health check procedure is now present in the `checks` array:

[source,json]
----
{
    "status": "UP",
    "checks": [
        {
            "name": "Simple health check",
            "status": "UP"
        }
    ]
}
----


Congratulations! You’ve created your first Quarkus health check procedure. Let’s continue by exploring what else can be done with the MicroProfile Health specification.

== Custom data in health checks

In the previous step we created a simple health check with only the minimal attributes, namely, the health check name and its state (`UP` or `DOWN`). However, MicroProfile also provides a way for the applications to supply arbitrary data in the form of key/value pairs sent in the health check response. This can be done by using the `withData(key, value)`` method of the health check response builder API. This is useful to provide additional info about passing or failing health checks, to give some indication of the problem when failures are investigated.

Let’s create our second health check procedure, a _Liveness_ probe. Create another Java class `org.acme.people.health.DataHealthCheck` with the following code:

[source, java, role="copypaste"]
----
package org.acme.people.health;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
@Liveness
public class DataHealthCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named("Health check with data")
        .up()
        .withData("foo", "fooValue")
        .withData("bar", "barValue")
        .build();

    }
}
----

Access the liveness health checks:

[source, sh, role="copypaste"]
----
curl http://localhost:8080/health/live
----

You can see that the new health check with data is present in the `checks` array. This check contains a new attribute called `data` which is a JSON object consisting of the properties we have defined in our health check procedure above.

== Negative Health Checks

In this section we create another health check which simulates a connection to an external service provider such as a database. For simplicity reasons, we'll use an `application.properties` setting to toggle the health check from `DOWN` to `UP`.

Create another Java class in the same package called `DatabaseConnectionHealthCheck` with the following code:

[source, java, role="copypaste"]
----
package org.acme.people.health;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipse.microprofile.health.Liveness;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
@Liveness
public class DatabaseConnectionHealthCheck implements HealthCheck {

    @ConfigProperty(name = "database.up", defaultValue = "false")
    private boolean databaseUp;

    @Override
    public HealthCheckResponse call() {

        HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("Database connection health check");

        try {
            simulateDatabaseConnectionVerification();
            responseBuilder.up();
        } catch (IllegalStateException e) {
            // cannot access the database
            responseBuilder.down()
                    .withData("error", e.getMessage());
        }

        return responseBuilder.build();
    }

    private void simulateDatabaseConnectionVerification() {
        if (!databaseUp) {
            throw new IllegalStateException("Cannot contact database");
        }
    }
}
----

Re-run the health check test:

[source, sh, role="copypaste"]
----
curl -i http://localhost:8080/health/live
----

You should see at the beginning the HTTP response:

[source,none]
----
HTTP/1.1 503 Service Unavailable
----

And the returned content should begin with `"status": "DOWN"` and you should see in the `checks` array the newly added Database connection health check which is down and the error message explaining why it failed:

[source,json]
----
        {
            "name": "Database connection health check",
            "status": "DOWN",
            "data": {
                "error": "Cannot contact database"
            }
        },
----

== Fix Health Check
We shouldn’t leave this application with a health check in DOWN state. Because we are running Quarkus dev mode, add this to to the end of the `src/main/resources/application.properties` file:

[source,json]
----
database.up=true
----

And access again using the same `curl` command — it should be `UP`!

== Accessing liveness and readiness separately

Quarkus apps can access the two different types using two different endpoints (`/health/live` and `/health/ready`). This is useful when configuring Kubernetes with probes which we'll do later, as it can access each separately (and configure each with different timeouts, periods, failure thresholds, etc). For example, You may want your Readiness probe to wait 30 seconds before starting, but Liveness should wait 2 minutes and only wait 10 seconds between retries.

Access the two endpoints. Each endpoint will only report on its specific type of probe:

[source, sh, role="copypaste"]
----
curl http://localhost:8080/health/live
----

You should only see the two Liveness probes.

[source, sh, role="copypaste"]
----
curl http://localhost:8080/health/ready
----

You should only see our single readiness probes.

Later, when we deploy this to our Kubernetes cluster, we'll configure it to use these endpoints.

== Externalized Configuration

Hardcoded values in your code is a no go (even if we all did it at some point ;-)). In this step, we learn how to configure your application to externalize configuration.

Quarkus uses https://microprofile.io/project/eclipse/microprofile-config[MicroProfile Config,window=_blank] to inject the configuration into the application. The injection uses the `@ConfigProperty` annotation, for example:

[source, java]
----
@ConfigProperty(name = "greeting.message")
String message;
----

[NOTE]
====
When injecting a configured value, you can use `@Inject @ConfigProperty` or just `@ConfigProperty`. The `@Inject` annotation is not necessary for members annotated with `@ConfigProperty`, a behavior which differs from https://microprofile.io/project/eclipse/microprofile-config[MicroProfile Config,window=_blank].
====

=== Add some external config

In the `org.acme.people.rest.GreetingResource` class, add the following fields to the class definition:

[source, java, role="copypaste"]
----
@ConfigProperty(name = "greeting.message")
String message;

@ConfigProperty(name = "greeting.suffix", defaultValue="!")
String suffix;

@ConfigProperty(name = "greeting.name")
Optional<String> name;
----

Remember to _Assistant > Organize Imports_ to import the `org.eclipse.microprofile.config.inject.ConfigProperty`.

[NOTE]
====
. If you do not provide a value for the first property (`greeting.message`), the application startup will fail with `DeploymentException: No config value of type [class java.lang.String] exists for: greeting.message`
. The default value for `greeting.suffix` is injected if the configuration does not provide a value for `greeting.suffix`.
. The `greeting.name` property is optional - an empty `Optional` is injected if the configuration does not provide a value for `greeting.name`.
====

Now, modify the `hello()` method to use the injected properties:

[source, java, role="copypaste"]
----
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return message + " " + name.orElse("world") + suffix;
}
----

=== Create the configuration

By default, Quarkus reads `application.properties`. Add the following properties to the `src/main/resources/application.properties` file:

[source, java, role="copypaste"]
----
greeting.message = hello
greeting.name = quarkus
----

Open up a Terminal window and run a `curl` command to test the changes:

[source, sh, role="copypaste"]
----
curl http://localhost:8080/hello
----

You should get `hello quarkus!`.

[NOTE]
====
If the application requires configuration values and these values are not set, an error is thrown. So you can quickly know when your configuration is complete.
====

=== Update the test

We also need to update the functional test to reflect the changes made to endpoint. Edit the `src/test/java/org/acme/people/GreetingResourceTest.java` file and change the content of the `testHelloEndpoint` method to:

[source,java,role="copypaste"]
----
    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/greeting")
          .then()
             .statusCode(200)
             .body(is("hello quarkus!")); // Modified line
    }
----

Since our applcation is still running from before, thanks to Quarkus Live Reload we should immediately see changes. Update `application.properties`, by changing the `greeting.message`, `greeting.name`, or adding `greeting.suffix` and running the same `curl http://localhost:8080/hello` after each change.

== Quarkus Configuration options

Quarkus itself is configured via the same mechanism as your application. Quarkus reserves the `quarkus.` namespace for its own configuration.

It is also possible to generate an example `application.properties` with _all known_ configuration properties, to make it easy to see what Quarkus configuration options are available. To do this, open a Terminal and run:

[source,sh,role=copypaste]
----
mvn quarkus:generate-config
----

This will create a `src/main/resources/application.properties.example` file that contains all the config options exposed via the extensions you currently have installed. These options are commented out, and have their default value when applicable.

[NOTE]
====
If the `application.properties.example` file does not appear in the project tree, right-click on the `resources` folder and select _Refresh 'resources'_.
====

[NOTE]
====
**Overriding properties at runtime**

As you have seen, in _dev_ mode, properties can be changed at will and reflected in the running app, however once you are ready to package your app for deployment, you'll not be running in _dev_ mode anymore, but rather building and packaging (e.g. into fat JAR or native executable.) Quarkus will do much of its configuration and bootstrap at build time. Most properties will then be read and set during the _build time_ step. To change them, you have to stop the application, re-package it, and restart.

Extensions _do_ define some properties as overridable at runtime. A canonical example is the database URL, username and password which is only known specifically in your target environment. **This is a tradeoff** as the more runtime properties are available, the less build time pre-work Quarkus can do. The list of runtime properties is therefore lean.

You can override these runtime properties with the following mechanisms (in decreasing priority):

* using system properties:
. for a runner jar: `java -Dquarkus.datasource.password=youshallnotpass -jar target/myapp-runner.jar`
. for a native executable: ``./target/myapp-runner -Dquarkus.datasource.password=youshallnotpass`

* using environment variables:
. for a runner jar: `QUARKUS_DATASOURCE_PASSWORD=youshallnotpass java -jar target/myapp-runner.jar`
. for a native executable: `QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ./target/myapp-runner`

Environment variables names are following the conversion rules of https://github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc#default-configsources[Eclipse MicroProfile Config sources,window=_blank]
====

=== Configuration Profiles

Quarkus supports the notion of configuration _profiles_. These allow you to have multiple configuration values in `application.properties` and select between then via a profile name.

The syntax for this is `%{profile}.config.key=value`. For example if I have the following: (do not copy this code!):

[source,java]
----
quarkus.http.port=9090
%dev.quarkus.http.port=8181
----

The Quarkus HTTP port will be `9090`, unless the `dev` profile is active, in which case it will be `8181`.

By default Quarkus has three profiles, although it is possible to use as many as you like (just use your custom profile names in `application.properties` and when running the app, and things will match up). The default profiles are:

. `dev` - Activated when in development mode (i.e. `mvn quarkus:dev`)
. `test` - Activated when running tests (i.e. `mvn verify`)
. `prod` - The default profile when not running in `dev` or `test` mode

=== Exercise Configuration Profile

Let's give this a go. In your `application.properties`, add a different `message.prefix` for the `prod` profile. To do this, change the content of the `greeting.` properties in `application.properties` to be:

[source,none,role="copypaste"]
----
greeting.message = hello
greeting.name = quarkus
%prod.greeting.name = production quarkus
----

Verify that in _dev_ mode (which you're currently running in) that:

[source,sh,role="copypaste"]
----
curl http://localhost:8080/hello
----

produces `hello quarkus!`.

Next, let's re-build the app as an executable JAR (which will run with the `prod` profile active).

Build an executable JAR just as before using the command palette and choosing **Create Executable JAR**.

image:createexec.png[create,600]

Next, open a new Terminal window and run the the app:

[source,sh,role="copypaste"]
----
java -Dquarkus.http.port=8081 -jar target/*-runner.jar
----

Notice we did not specify any Quarkus profile. When not running in dev mode (`mvn quarkus:dev`), and not running in test mode (`mvn verify`), then the default profile is `prod`.

While the app is running, open a separate Terminal window and test it by running:

[source,sh,role="copypaste"]
----
curl http://localhost:8081/hello
----

What did you get? You should get `hello production quarkus!` indicating that the `prod` profile was active by default. In other sections in this workshop we'll use this feature to overrride important variables like database credentials.

[NOTE]
====
In this example we read configuration properties from `application.properties`. You can also introduce custom configuration sources in the standard MicroProfile Config manner. To do this, you must provide a class which implements either `org.eclipse.microprofile.config.spi.ConfigSource` or `org.eclipse.microprofile.config.spi.ConfigSourceProvider`. Create a service file for the class and it will be detected and installed at application startup. https://microprofile.io/project/eclipse/microprofile-config[More Info,window=_blank]. This would be useful, for example, to read directly from **Kubernetes ConfigMap**s.
====

== Cleanup

Stop the app that you ran with `java -jar` by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs. Make sure to leave the `Start Live Coding` terminal window open!

== Congratulations

Cloud native encompasses much more than health probes and externalized config. With Quarkus' _container and Kubernetes-first philosophy_, excellent performance, support for many cloud native frameworks, it's a great place to build your next cloud native app.