Quarkus - Kubernetes Client

    Having a Kubernetes Client extension in Quarkus is very useful in order to unlock the power of Kubernetes Operators.Kubernetes Operators are quickly emerging as a new class of Cloud Native applications.These applications essentially watch the Kubernetes API and react to changes on various resources and can be used to manage the lifecycle of all kinds of complex systems like databases, messaging systems and much much more.Being able to write such operators in Java with the very low footprint that native images provide is a great match.

    Once you have your Quarkus project configured you can add the kubernetes-client extensionto your project by running the following command in your project base directory.

    /mvnw quarkus:add-extension -Dextensions="kubernetes-client"

    1. <dependency>
    2. <groupId>io.quarkus</groupId>
    3. <artifactId>quarkus-kubernetes-client</artifactId>
    4. </dependency>

    Usage

    Quarkus configures a Bean of type KubernetesClient which can be injected into application code using the well known CDI methods.This client can be configured using various properties as can be seen in the following example:

    1. quarkus.kubernetes-client.trust-certs=false
    2. quarkus.kubernetes-client.namespace=default

    Note that the full list of properties is available in the class.

    An example of this can be seen in the following snippet:

    1. @ApplicationScoped
    2. public class KubernetesClientProducer {
    3. @Produces
    4. public KubernetesClient kubernetesClient() {
    5. // here you would create a custom client
    6. return new DefaultKubernetesClient();
    7. }
    8. }

    To make testing against a mock Kubernetes API extremely simple, Quarkus provides the KubernetesMockServerTestResource which automatically launchesa mock of the Kubernetes API server and sets the proper environment variables needed so that the Kubernetes Client configures itself to use said mock.Tests can inject the mock and set it up in any way necessary for the particular testing using the @MockServer annotation.

    Let’s assume we have a REST endpoint defined like so:

    We could write a test for this endpoint very easily like so:

    1. @QuarkusTestResource(KubernetesMockServerTestResource.class)
    2. @QuarkusTest
    3. public class KubernetesClientTest {
    4. @MockServer
    5. KubernetesMockServer mockServer;
    6. @BeforeEach
    7. public void before() {
    8. final Pod pod1 = new PodBuilder().withNewMetadata().withName("pod1").withNamespace("test").and().build();
    9. final Pod pod2 = new PodBuilder().withNewMetadata().withName("pod2").withNamespace("test").and().build();
    10. mockServer.expect().get().withPath("/api/v1/namespaces/test/pods")
    11. .andReturn(200,
    12. new PodListBuilder().withNewMetadata().withResourceVersion("1").endMetadata().withItems(pod1, pod2)
    13. .build())
    14. .always();
    15. @Test
    16. public void testInteractionWithAPIServer() {
    17. RestAssured.when().get("/pod/test").then()
    18. .body("size()", is(2));
    19. }
    20. }

    Note that to take advantage of these features, the quarkus-test-kubernetes-client dependency needs to be added, for example like so:

    1. <dependency>
    2. <groupId>io.quarkus</groupId>
    3. <artifactId>quarkus-test-kubernetes-client</artifactId>
    4. <scope>test</scope>
    5. </dependency>

    Note on implementing the Watcher interface

    1. client.pods().watch(new Watcher<Pod>() {
    2. @Override
    3. public void eventReceived(Action action, Pod pod) {
    4. // do something
    5. }
    6. @Override
    7. public void onClose(KubernetesClientException e) {
    8. // do something
    9. }
    10. });

    or

    Note that defining the generic type via a class hierarchy similar to the following example will also work correctly:

    1. public abstract class MyWatcher<S> implements Watcher<S> {
    2. }
    3. ...
    4. client.pods().watch(new MyWatcher<Pod>() {
    5. @Override
    6. public void eventReceived(Action action, Pod pod) {
    7. // do something
    8. }
    9. });
    1. public class ResourceWatcher<T extends HasMetadata> implements Watcher<T> {
    2. @Override
    3. public void eventReceived(Action action, T resource) {
    4. }
    5. @Override
    6. public void onClose(KubernetesClientException e) {
    7. // do something
    8. }
    9. }
    10. client.pods().watch(new ResourceWatcher<Pod>());

    In many cases in order to access the Kubernetes API server a ServiceAccount, Role and RoleBinding will be necessary.An example that allows listing all pods could look something like this:

    1. ---
    2. apiVersion: v1
    3. kind: ServiceAccount
    4. metadata:
    5. name: <applicationName>
    6. namespace: <namespace>
    7. ---
    8. apiVersion: rbac.authorization.k8s.io/v1
    9. kind: Role
    10. metadata:
    11. namespace: <namespace>
    12. rules:
    13. - apiGroups: [""]
    14. resources: ["pods"]
    15. verbs: ["list"]
    16. ---
    17. apiVersion: rbac.authorization.k8s.io/v1
    18. kind: RoleBinding
    19. metadata:
    20. name: <applicationName>
    21. namespace: <namespace>
    22. roleRef:
    23. kind: Role
    24. name: <applicationName>
    25. apiGroup: rbac.authorization.k8s.io
    26. subjects:
    27. - kind: ServiceAccount
    28. name: <applicationName>
    29. namespace: <namespace>

    Replace <applicationName> and <namespace> with your values.Have a look at to get further information.

    Configuration Reference

    Configuration property fixed at build time - ️ Configuration property overridable at runtime