////
This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
= Deploying on OpenShift

include::./attributes.adoc[]

This guide covers generating and deploying OpenShift resources based on sane default and user supplied configuration.


== Prerequisites

:prerequisites-no-graalvm:
include::includes/devtools/prerequisites.adoc[]
* Access to an OpenShift cluster (Minishift is a viable option)
* OpenShift CLI (Optional, only required for manual deployment)

== Bootstrapping the project

First, we need a new project that contains the OpenShift extension. This can be done using the following command:

:create-app-artifact-id: openshift-quickstart
:create-app-extensions: resteasy,openshift
:create-app-code:
include::includes/devtools/create-app.adoc[]

Quarkus offers the ability to automatically generate OpenShift resources based on sane defaults and user supplied configuration.
The OpenShift extension is actually a wrapper extension that brings together the xref:deploying-to-kubernetes.adoc[kubernetes] and xref:container-image.adoc#s2i[container-image-s2i]
extensions with sensible defaults so that it's easier for the user to get started with Quarkus on OpenShift.

When we added the OpenShift extension to the command line invocation above, the following dependency was added to the `pom.xml`

[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-openshift</artifactId>
</dependency>
----

[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
----
implementation("io.quarkus:quarkus-openshift")
----

== Log Into the OpenShift Cluster

Before we build and deploy our application we need to log into an OpenShift cluster.
You can log in via the https://docs.openshift.com/container-platform/4.9/cli_reference/openshift_cli/getting-started-cli.html[OpenShift CLI]:

.Log In - OpenShift CLI Example
[source,bash]
----
oc login -u myUsername <1>
----
<1> You'll be prompted for the required information such as server URL, password, etc.

Alternatively, you may log in using the API token:

.Log In - OpenShift CLI With API Token Example
[source,bash]
----
oc login --token=myToken --server=myServerUrl
----

TIP: You can request the token via the _Copy Login Command_ link in the OpenShift web console.

Finally, you don't need to use the OpenShift CLI at all.
Instead, set the `quarkus.kubernetes-client.master-url` config property and authenticate with the `quarkus.kubernetes-client.token`, or `quarkus.kubernetes-client.username` and `quarkus.kubernetes-client.password` respectively:

:build-additional-parameters: -Dquarkus.kubernetes-client.master-url=myServerUrl -Dquarkus.kubernetes-client.token=myToken
include::includes/devtools/build.adoc[]
:!build-additional-parameters:

== Build and Deployment

You can trigger a build and deployment in a single step or build the container image first and then configure the OpenShift application manually if you need <<control_application_config,more control over the deployment configuration>>.

To trigger a build and deployment in a single step:

:build-additional-parameters: -Dquarkus.kubernetes.deploy=true
include::includes/devtools/build.adoc[]
:!build-additional-parameters:

TIP: If you want to test your application immediately then set the `quarkus.openshift.route.expose` config property to `true` to <<exposing_routes,expose the service automatically>>, e.g. add `-Dquarkus.openshift.route.expose=true` to the command above.

This command will build your application locally, then trigger a container image build and finally apply the generated OpenShift resources automatically.
The generated resources use OpenShift's `DeploymentConfig` that is configured to automatically trigger a redeployment when a change in the `ImageStream` is noticed.
In other words, any container image build after the initial deployment will automatically trigger redeployment, without the need to delete, update or re-apply the generated resources.

You can use the OpenShift web console to verify that the above command has created an image stream, a service resource and has deployed the application.
Alternatively, you can run the following OpenShift CLI commands:
[source,bash,subs=attributes+]
----
oc get is <1>
oc get pods <2>
oc get svc <3>
----
<1> Lists the image streams created.
<2> Get the list of pods.
<3> Get the list of Kubernetes services.

Note that the service is not exposed to the outside world by default.
So unless you've used the `quarkus.openshift.route.expose` config property to expose the created service automatically you'll need to expose the service manually.

.Expose The Service - OpenShift CLI Example
[source,bash,subs=attributes+]
----
oc expose svc/greeting <1>
oc get routes <2>
curl http://<route>/greeting <3>
----
<1> Expose the service.
<2> Get the list of exposed routes.
<3> Access your application.

[[control_application_config]]
=== Configure the OpenShift Application Manually 

If you need more control over the deployment configuration you can build the container image first and then configure the OpenShift application manually.

To trigger a container image build:

[source,bash,subs=attributes+]
----
./mvnw clean package -Dquarkus.container-image.build=true
----

The build that will be performed is an _s2i binary_ build. 
The input of the build is the jar that has been built locally and the output of the build is an `ImageStream` that is configured to automatically trigger a deployment.

[NOTE]
====
During the build you may find the `Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed` exception due to self-signed certificate. To solve this, just add the following line to your `application.properties`:
```properties
quarkus.kubernetes-client.trust-certs=true
```
For more information, see link:https://quarkus.io/guides/deploying-to-kubernetes#client-connection-configuration[deploying to kubernetes].
====

Once the build is done we can create a new application from the relevant `ImageStream`.

[source,bash,subs=attributes+]
----
oc get is <1>
oc new-app --name=greeting <project>/openshift-quickstart:1.0.0-SNAPSHOT <2>
oc get svc
oc expose svc/greeting <3>
oc get routes <4>
curl http://<route>/greeting <5>
----
<1> Lists the image streams created. The image stream of our application should be tagged as <project>/openshift-quickstart:1.0.0-SNAPSHOT.
<2> Create a new application from the image source.
<3> Expose the service to the outside world.
<4> Get the list of exposed routes.
<5> Access your application.

After this setup the next time the container image is built a deployment to OpenShift is triggered automatically.
In other words, you don't need to repeat the above steps.

=== Non-S2I Builds

Out of the box the OpenShift extension is configured to use xref:container-image.adoc#s2i[container-image-s2i]. However, it's still possible to use other container image extensions like:

- xref:container-image.adoc#docker[container-image-docker]
- xref:container-image.adoc#jib[container-image-jib]

When a non-s2i container image extension is used, an `ImageStream` is created that is pointing to an external `dockerImageRepository`. The image is built and pushed to the registry and the `ImageStream` populates the tags that are available in the `dockerImageRepository`. 

To select which extension will be used for building the image:

[source,properties]
----
quarkus.container-image.builder=docker
----

or

[source,properties]
----
quarkus.container-image.builder=jib
----

== Customizing

All available customization options are available in the xref:deploying-to-kubernetes.adoc#openshift[OpenShift configuration options].

Some examples are provided in the sections below:

[[exposing_routes]]
=== Exposing Routes

To expose a `Route` for the Quarkus application:

[source,properties]
----
quarkus.openshift.route.expose=true
----

[TIP]
====
You don't necessarily need to add this property in the `application.properties`. You can pass it as a command line argument:

[source,bash,subs=attributes+]
----
./mvnw clean package -Dquarkus.openshift.route.expose=true
----

The same applies to all properties listed below.
====

=== Labels

To add a label in the generated resources:

[source,properties]
----
quarkus.openshift.labels.foo=bar
----

=== Annotations

To add an annotation in the generated resources:

[source,properties]
----
quarkus.openshift.annotations.foo=bar
----

[#env-vars]
=== Environment variables

OpenShift provides multiple ways of defining environment variables:

- key/value pairs
- import all values from a Secret or ConfigMap
- interpolate a single value identified by a given field in a Secret or ConfigMap
- interpolate a value from a field within the same resource

==== Environment variables from key/value pairs

To add a key/value pair as an environment variable in the generated resources:

[source,properties]
----
quarkus.openshift.env.vars.my-env-var=foobar
----

The command above will add `MY_ENV_VAR=foobar` as an environment variable.
Please note that the key `my-env-var` will be converted to uppercase and dashes will be replaced by underscores resulting in `MY_ENV_VAR`.

==== Environment variables from Secret

To add all key/value pairs of `Secret` as environment variables just apply the following configuration, separating each `Secret`
to be used as source by a comma (`,`):

[source,properties]
----
quarkus.openshift.env.secrets=my-secret,my-other-secret
----

which would generate the following in the container definition:

[source,yaml]
----
envFrom:
  - secretRef:
      name: my-secret
      optional: false
  - secretRef:
      name: my-other-secret
      optional: false
----

The following extracts a value identified by the `keyName` field from the `my-secret` Secret into a `foo` environment variable:

[source,properties]
----
quarkus.openshift.env.mapping.foo.from-secret=my-secret
quarkus.openshift.env.mapping.foo.with-key=keyName
----

This would generate the following in the `env` section of your container:

[source,yaml]
----
- env:
  - name: FOO
    valueFrom:
      secretKeyRef:
        key: keyName
        name: my-secret
        optional: false
----

==== Environment variables from ConfigMap

To add all key/value pairs from `ConfigMap` as environment variables just apply the following configuration, separating each
`ConfigMap` to be used as source by a comma (`,`):

[source,properties]
----
quarkus.openshift.env.configmaps=my-config-map,another-config-map
----

which would generate the following in the container definition:

[source,yaml]
----
envFrom:
  - configMapRef:
      name: my-config-map
      optional: false
  - configMapRef:
      name: another-config-map
      optional: false
----

The following extracts a value identified by the `keyName` field from the `my-config-map` ConfigMap into a `foo`
environment variable:

[source,properties]
----
quarkus.openshift.env.mapping.foo.from-configmap=my-configmap
quarkus.openshift.env.mapping.foo.with-key=keyName
----

This would generate the following in the `env` section of your container:

[source,yaml]
----
- env:
  - name: FOO
    valueFrom:
      configMapRefKey:
        key: keyName
        name: my-configmap
        optional: false
----

==== Environment variables from fields

It's also possible to use the value from another field to add a new environment variable by specifying the path of the field to be used as a source, as follows:

[source,properties]
----
quarkus.openshift.env.fields.foo=metadata.name
----

==== Using Deployment instead of DeploymentConfig
Out of the box the extension will generate a `DeploymentConfig` resource. Often users, prefer to use `Deployment` as the main deployment resource, but still make use of OpenShift specific resources like `Route`, `BuildConfig` etc.
This feature is enabled by setting `quarkus.openshift.deployment-kind` to `Deployment`.

[source,properties]
----
quarkus.openshift.deployment-kind=Deployment
----

Since `Deployment` is a Kubernetes resource and not OpenShift specific, it can't possibly leverage `ImageStream` resources, as is the case with `DeploymentConfig`. This means that the image references need to include the container image registry that hosts the image.
When the image is built, using OpenShift builds (s2i binary and docker strategy) the OpenShift internal image registry `image-registry.openshift-image-registry.svc:5000` will be used, unless an other registry has been explicitly specified by the user. Please note, that in the internal registry the project/namespace name is added as part of the image repository: `image-registry.openshift-image-registry.svc:5000/<project name>/<name>:<tag>`, so users will need to make sure that the target project/namespace name is aligned with the `quarkus.container-image.group`.

[source,properties]
----
quarkus.container-image.group=<project/namespace name>
----

==== Validation

A conflict between two definitions, e.g. mistakenly assigning both a value and specifying that a variable is derived from a field, will result in an error being thrown at build time so that you get the opportunity to fix the issue before you deploy your application to your cluster where it might be more difficult to diagnose the source of the issue.

Similarly, two redundant definitions, e.g. defining an injection from the same secret twice, will not cause an issue but will indeed report a warning to let you know that you might not have intended to duplicate that definition.

[#env-vars-backwards]
===== Backwards compatibility

Previous versions of the OpenShift extension supported a different syntax to add environment variables. The older syntax is still supported but is deprecated, and it's advised that you migrate to the new syntax.

.Old vs. new syntax
|====
|                               |Old                                                    | New                                                 |
| Plain variable                |`quarkus.openshift.env-vars.my-env-var.value=foobar`  | `quarkus.openshift.env.vars.my-env-var=foobar`     |
| From field                    |`quarkus.openshift.env-vars.my-env-var.field=foobar`  | `quarkus.openshift.env.fields.my-env-var=foobar`   |
| All from `ConfigMap`          |`quarkus.openshift.env-vars.xxx.configmap=foobar`     | `quarkus.openshift.env.configmaps=foobar`          |
| All from `Secret`             |`quarkus.openshift.env-vars.xxx.secret=foobar`        | `quarkus.openshift.env.secrets=foobar`             |
| From one `Secret` field       |`quarkus.openshift.env-vars.foo.secret=foobar`        | `quarkus.openshift.env.mapping.foo.from-secret=foobar` |
|                               |`quarkus.openshift.env-vars.foo.value=field`          | `quarkus.openshift.env.mapping.foo.with-key=field` |
| From one `ConfigMap` field    |`quarkus.openshift.env-vars.foo.configmap=foobar`     | `quarkus.openshift.env.mapping.foo.from-configmap=foobar` |
|                               |`quarkus.openshift.env-vars.foo.value=field`          | `quarkus.openshift.env.mapping.foo.with-key=field` |
|====

NOTE: If you redefine the same variable using the new syntax while keeping the old syntax, **ONLY** the new version will be kept, and a warning will be issued to alert you of the problem. For example, if you define both
`quarkus.openshift.env-vars.my-env-var.value=foobar` and `quarkus.openshift.env.vars.my-env-var=newValue`, the extension will only generate an environment variable `MY_ENV_VAR=newValue` and issue a warning.

=== Mounting volumes

The OpenShift extension allows the user to configure both volumes and mounts for the application.

Any volume can be mounted with a simple configuration:

[source,properties]
----
quarkus.openshift.mounts.my-volume.path=/where/to/mount
----

This will add a mount to my pod for volume `my-volume` to path `/where/to/mount`

The volumes themselves can be configured as shown in the sections below:

==== Secret volumes

[source,properties]
----
quarkus.openshift.secret-volumes.my-volume.secret-name=my-secret
----

==== ConfigMap volumes

[source,properties]
----
quarkus.openshift.config-map-volumes.my-volume.config-map-name=my-secret
----

==== Persistent Volume Claims

[source,properties]
----
quarkus.openshift.pvc-volumes.my-pvc.claim-name=my-pvc
----

== Knative - OpenShift Serverless

OpenShift also provides the ability to use Knative via the link:https://www.openshift.com/learn/topics/serverless[OpenShift Serverless] functionality.

The first order of business is to instruct Quarkus to generate Knative resources by setting:

[source,properties]
----
quarkus.kubernetes.deployment-target=knative
----

In order to leverage OpenShift S2I to build the container image on the cluster and use the resulting container image for the Knative application,
we need to set a couple of configuration properties:

[source,properties]
----
# set the Kubernetes namespace which will be used to run the application
quarkus.container-image.group=geoand
# set the container image registry - this is the standard URL used to refer to the internal OpenShift registry
quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000
----

The application can then be deployed to OpenShift Serverless by enabling the standard `quarkus.kubernetes.deploy=true` property.

== Configuration Reference

include::{generated-dir}/config/quarkus-openshift-openshift-config.adoc[opts=optional, leveloffset=+1]
