Integration

Extending the generated Openshift artifacts in Quarkus

There are lot of properties in Quarkus that configure the generated Openshift (or Kubernetes) deployment artifacts. And when I mean a lot, I mean a lot: https://quarkus.io/guides/all-config. You can define almost all possible Kubernetes settings: from volumes and labels to environmental variables and init containers. But that is the central point here, you can define almost all of them. But what about the others? For some other components like empty volumes and post start lifecycle commands there are no properties available. Fortunately, that doesn’t mean there is nothing you can do. Quarkus provides a way to extend the generated artifacts and I will show you how to do it.

1. Generating the artifacts.

The first thing I recommend is to look at the generated YAML file. A JSON file is also generated , but for this Blog I will use the one with YAML format. I had to add the quarkus-openshift dependency to my pom file since I am working with openshift.

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-openshift</artifactId>
</dependency>

You can execute the following command once you have added the dependency:

./mvnw clean package -Dquarkus.container-image.build=true -Dquarkus.kubernetes.deployment-target=openshift

After it finished you will see that the openshift.yml file was generated inside the target/kubernetes directory. It will have at least 5 definitions: 1 Service, 2 ImageStreams (one for the jdk base image and one for the new image itself), 1 BuildConfig and 1 DeploymentConfig. You can basically change any of those definitions, but we are just focusing on DeploymentConfig in this blog entry.

2. Extend the generated openshift.yml file

To extend the generated the openshift.yml you have the define a new openshift.yml file under src/main/kubernetes. On its must simple version, you just have to define the metadata and the container. In this example I will additionally overwrite the number of replicas.

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: esentri-quarkus-overwrite
spec:
  replicas: 2
  template:
    spec:
      containers:
        - image: user/esentri-quarkus-overwrite:1.0.0-SNAPSHOT
          name: esentri-quarkus-overwrite

After executing the command, you can see that the default number of replicas has been set to 2.

...
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  annotations:
    app.openshift.io/vcs-url: <<unknown>>
    app.quarkus.io/build-timestamp: 2021-08-26 - 11:44:25 +0000
  labels:
    app.kubernetes.io/name: esentri-quarkus-overwrite
    app.openshift.io/runtime: quarkus
    app.kubernetes.io/version: 1.0.0-SNAPSHOT
  name: esentri-quarkus-overwrite
spec:
  replicas: 2
  selector:
    app.kubernetes.io/version: 1.0.0-SNAPSHOT
    app.kubernetes.io/name: esentri-quarkus-overwrite
...

3. Example 1: define an empty volume.

As mentioned above, empty volumes cannot be generated using the application.properties settings. Instead, you will have to define the openshift.yml with the empty volume in it.

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: esentri-quarkus-overwrite
spec:
  template:
    spec:
      containers:
        - image: user/esentri-quarkus-overwrite:1.0.0-SNAPSHOT
          name: esentri-quarkus-overwrite
          volumeMounts:
            - mountPath: /etc/keystore-volume
              name: keystore-volume
              readOnly: false
      volumes:
        - name: keystore-volume
          emptyDir: {}

One of the most important aspects of this technique is that this new definition extends the one that was defined using the application.properties. So if you defined a volume in the application.propeties file, you will get now two volumes instead, like this:

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
...
    spec:
      containers:
...
       
          volumeMounts:
            - mountPath: /etc/keystore-volume
              name: keystore-volume
              readOnly: false
            - mountPath: /etc/main-secret-volume
              name: main-secret-volume
              readOnly: false
      volumes:
        - emptyDir: {}
          name: keystore-volume
        - name: main-secret-volume
          secret:
            defaultMode: 511
            optional: false
            secretName: main-keystore
  ...

4. Example 2: define a post start lifecycle command

Like with the empty volume, you need to create the openshift.yml file with the lifecycle fragment on it:

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: esentri-quarkus-overwrite
spec:
  replicas: 1
  template:
    spec:
      containers:
        - image: user/esentri-quarkus-overwrite:1.0.0-SNAPSHOT
          name: esentri-quarkus-overwrite
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - '-c'
                  - >-
                    echo 'This is a test' > /etc/keystore-volume/test.txt;

          volumeMounts:
            - mountPath: /etc/keystore-volume
              name: keystore-volume
              readOnly: false
      volumes:
        - name: keystore-volume
          emptyDir: {}

5. Conclusion

The Openshift (or Kubernetes) extension file is a very powerful feature when it comes to defining additional settings not available as configuration properties. It is also interesting option in cases where some of these artifacts are being generated manually or with another tool.  There might be some use cases where you prefer only to maintain the business configuration properties in the application.properties file and the infrastructure aspects separated.

6. References

https://quarkus.io/guides/deploying-to-kubernetes#openshift

https://quarkus.io/guides/all-config