The fabric8 Kubernetes client has been simplifying Java developers' use of Kubernetes for several years. Although the parent fabric8 project has ended, the Kubernetes client continues to be popular, and the recent 5.5.0 release includes many new features and bug fixes.
This article takes a look at new features in fabric8 Kubernetes client, focusing on:
- Dynamic clients
- Dynamic informers
- TLS certificate management
- HTTP retry options
- Red Hat OpenShift resources in the OpenShift client DSL.
Knowing about these changes will help you avoid problems when you upgrade to the latest version of fabric8's Java client for Kubernetes or Red Hat OpenShift.
Note: The fabric8 development team mostly consists of Java developers, so the client's design is heavily influenced by a Java developer's perspective. I will demonstrate just a few of fabric8's features for using Kubernetes APIs in a Java environment.
How to get the new fabric8 Java client
You can find the most current fabric8 Java client release on Maven Central. To start using the new client, add it as a dependency in your Maven pom.xml
file. For Kubernetes, the dependency is:
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>5.5.0</version>
</dependency>
For OpenShift, it's:
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-client</artifactId>
<version>5.5.0</version>
</dependency>
New features in fabric8 Kubernetes client 5.5.0
In many areas, we've been watching developments in Kubernetes and OpenShift and listening to the needs of developers. I will cover the most important new features in the following sections.
Dynamic clients: Unstructured and GenericKubernetesResource
The Kubernetes Go language client has the concept of a dynamic client and a generic Kubernetes type called Unstructured
. The fabric8 Kubernetes client already provided support for Unstructured
in its CustomResource
API using raw HashMap
s. In 5.5.0, we added a new type, GenericKubernetesResource
, which you can use for deserializing unknown types. Here is an example of using it to get a list of CronTab
resources (mentioned in the Kubernetes CustomResourceDefinition guide) in a specified namespace:
try (KubernetesClient client = new DefaultKubernetesClient()) {
CustomResourceDefinitionContext context = new CustomResourceDefinitionContext.Builder()
.withVersion("v1")
.withGroup("stable.example.com")
.withScope("Namespaced")
.withPlural("crontabs")
.build();
System.out.println("CronTab resources in default namespace: ");
client.genericKubernetesResources(context)
.inNamespace("default")
.list().getItems().stream()
.map(GenericKubernetesResource::getMetadata)
.map(ObjectMeta::getName)
.forEach(System.out::println);
} catch (KubernetesClientException e) {
System.out.println("Exception received: " + e.getMessage());
}
In the future, we will provide additional methods to make using GenericKubernetesResource
even easier. We also plan to add support for applying the list of Kubernetes resources (right now the class works only for primitive Kubernetes resource lists), which can also contain custom resources
Dynamic informers
With the introduction of GenericKubernetesResource
, you can use the SharedInformer API for a CustomResource
without providing any type. The earlier KubernetesClient
raw API was missing support for SharedInformer
s, but now you can use informers from both SharedInformerFactory
and the DSL inform()
method.
Here is an example of using dynamic informers from SharedInformerFactory
:
try (KubernetesClient client = new DefaultKubernetesClient()) {
SharedInformerFactory informerFactory = client.informers();
CustomResourceDefinitionContext context = new CustomResourceDefinitionContext.Builder()
.withGroup("stable.example.com")
.withVersion("v1")
.withPlural("crontabs")
.withScope("Namespaced")
.build();
SharedIndexInformer<GenericKubernetesResource> informer = informerFactory.sharedIndexInformerForCustomResource(context, 60 * 1000L);
informer.addEventHandler(new ResourceEventHandler<>() {
@Override
public void onAdd(GenericKubernetesResource genericKubernetesResource) {
System.out.printf("ADD %s\n", genericKubernetesResource.getMetadata().getName());
}
@Override
public void onUpdate(GenericKubernetesResource genericKubernetesResource, GenericKubernetesResource t1) {
System.out.printf("UPDATE %s\n", genericKubernetesResource.getMetadata().getName());
}
@Override
public void onDelete(GenericKubernetesResource genericKubernetesResource, boolean b) {
System.out.printf("DELETE %s\n", genericKubernetesResource.getMetadata().getName());
}
});
informerFactory.startAllRegisteredInformers();
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
We’ve also added a new method to the DSL:
inform(ResourceEventHandle eventHandler)
This method can be used from within the DSL without having to create a SharedInformerFactory
. A SharedIndexInformer
will be started automatically, so there is no need to invoke the informer's run()
method. Here is an example:
package io.fabric8.demos;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import java.util.concurrent.TimeUnit;
public class DSLInformMethod {
public static void main(String[] args) {
try (KubernetesClient client = new DefaultKubernetesClient()) {
client.pods().inNamespace("default").inform(new ResourceEventHandler<>() {
@Override
public void onAdd(Pod pod) {
System.out.println(pod.getMetadata().getName() + " ADD ");
}
@Override
public void onUpdate(Pod pod, Pod t1) {
System.out.println(pod.getMetadata().getName() + " UPDATE ");
}
@Override
public void onDelete(Pod pod, boolean b) {
System.out.println(pod.getMetadata().getName() + " DELETE ");
}
});
TimeUnit.SECONDS.sleep(10 * 1000);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
interruptedException.printStackTrace();
}
}
}
Certification management
A new extension was contributed to this release: The JetStack cert-manager
extension. With this extension, you can manage TLS web certificates through cert-manager
-based CRDs from the Kubernetes API server in Java, using the cert-manager
extension.
Add this dependency to use the extension in your projects:
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>certmanager-client</artifactId>
<version>5.5.0</version>
</dependency>
Here is a simple example of a CerificateRequest
using the extension:
package io.fabric8.demo;
import io.fabric8.certmanager.api.model.meta.v1.ObjectReferenceBuilder;
import io.fabric8.certmanager.api.model.v1.CertificateRequest;
import io.fabric8.certmanager.api.model.v1.CertificateRequestBuilder;
import io.fabric8.certmanager.client.CertManagerClient;
import io.fabric8.certmanager.client.DefaultCertManagerClient;
import io.fabric8.kubernetes.api.model.Duration;
import java.text.ParseException;
public class CertificateRequestExample {
public static void main(String[] args) {
try (CertManagerClient certManagerClient = new DefaultCertManagerClient()) {
CertificateRequest certificateRequest = new CertificateRequestBuilder()
.withNewMetadata().withName("my-ca-cr").endMetadata()
.withNewSpec()
.withRequest("base64encodedcert=")
.withIsCA(false)
.addToUsages("signing", "digital signature", "server auth")
.withDuration(Duration.parse("90d"))
.withIssuerRef(new ObjectReferenceBuilder()
.withName("ca-issuer")
.withKind("Issuer")
.withGroup("cert-manager.io")
.build())
.endSpec()
.build();
certManagerClient.v1().certificateRequests().inNamespace("default").create(certificateRequest);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
HTTP operation retry configuration options with exponential backoff
According to Kubernetes API conventions, when the HTTP status code is 500 (Status Internal Server Error), 503 (Status Service Unavailable), or 504 (Status Server Timeout), the suggested client recovery behavior is "retry with exponential backoff." In this release, we introduced additional configuration properties for this retry mechanism, shown in Table 1.
Property name |
Description |
---|---|
kubernetes.request.retry.backoffLimit |
Number of retry attempts on status codes >= 500. Defaults to 0. |
kubernetes.request.retry.backoffInterval |
Retry initial backoff interval, in milliseconds. Defaults to 1000. |
OpenShift client DSL improvements
The previous release provided support for all Kubernetes resources, as described in this issue in the fabric8 Kubernetes client GitHub repository. We also had good coverage for OpenShift 3.x resources. But in OpenShift 4, newer resources were introduced by the installation of additional operators. Now we’re covering most of the resources offered by OpenShift 4. You can find more details in this GitHub issue. Table 2 shows the newly added DSL methods.
OpenShift resource |
OpenShift client DSL |
---|---|
authorization.openshift.io/v1 ClusterRole |
openShiftClient.clusterRoles() |
authorization.openshift.io/v1 ResourceAccessReview |
openShiftClient.resourceAccessReviews() |
authorization.openshift.io/v1 LocalSubjectAccessReview |
openShiftClient.localSubjectAccessReviews() |
authorization.openshift.io/v1 LocalResourceAccessReview |
openShiftClient.localResourceAccessReviews() |
authorization.openshift.io/v1 SubjectRulesReview |
openShiftClient.subjectRulesReviews() |
authorization.openshift.io/v1 SelfSubjectRulesReview |
openShiftClient.selfSubjectRulesReviews() |
authorization.openshift.io/v1 RoleBindingRestrictions |
openShiftClient.roleBindingRestrictions() |
autoscaling.openshift.io/v1 ClusterAutoscaler |
openShiftClient.clusterAutoscaling().v1().clusterAutoscalers() |
autoscaling.openshift.io/v1beta1 MachineAutoscaler |
openShiftClient.clusterAutoscaling().v1beta1().machineAutoscalers() |
cloudcredential.openshift.io/v1 CredentialsRequest |
openShiftClient.credentialsRequests() |
config.openshift.io/v1 Authentication |
openShiftClient.config().authentications() |
config.openshift.io/v1 Console |
openShiftClient.config().consoles() |
config.openshift.io/v1 DNS |
openShiftClient.config().dnses() |
config.openshift.io/v1 Network |
openShiftClient.config().networks() |
console.openshift.io/v1alpha1 ConsolePlugin |
openShiftClient.console().consolePlugins() |
console.openshift.io/v1 ConsoleQuickStart |
openShiftClient.console().consoleQuickStarts() |
controlplane.operator.openshift.io/v1alpha1 PodNetworkConnectivityCheck |
openShiftClient.operator().podNetworkConnectivityChecks() |
helm.openshift.io/v1beta1 HelmChartRepository |
openShiftClient.helmChartRepositories() |
image.openshift.io/v1 ImageSignature |
openShiftClient.imageSignatures() |
image.openshift.io/v1 ImageStreamImage |
openShiftClient.imageStreamImages() |
image.openshift.io/v1 ImageStreamImport |
openShiftClient.imageStreamImports() |
image.openshift.io/v1 ImageStreamMapping |
openShiftClient.imageStreamMappings() |
machine.openshift.io/v1beta1 MachineHealthCheck |
openShiftClient.machine().machineHealthChecks() |
machine.openshift.io/v1beta1 MachineSet |
openShiftClient.machine().machineSets() |
machineconfiguration.openshift.io/v1 ContainerRuntimeConfig |
openShiftClient.machineConfigurations().containerRuntimeConfigs() |
machineconfiguration.openshift.io/v1 ControllerConfig |
openShiftClient.machineConfigurations().controllerConfigs() |
machineconfiguration.openshift.io/v1 KubeletConfig |
openShiftClient.machineConfigurations().kubeletConfigs() |
machineconfiguration.openshift.io/v1 MachineConfigPool |
openShiftClient.machineConfigurations().machineConfigPools() |
machineconfiguration.openshift.io/v1 MachineConfig |
openShiftClient.machineConfigurations().machineConfigs() |
metal3.io/v1alpha1 BareMetalHost |
openShiftClient.bareMetalHosts() |
monitoring.coreos.com/v1alpha1 AlertmanagerConfig |
openShiftClient.monitoring().alertmanagerConfigs() |
monitoring.coreos.com/v1 Probe |
openShiftClient.monitoring().probes() |
monitoring.coreos.com/v1 ThanosRuler |
openShiftClient.monitoring().thanosRulers() |
network.openshift.io/v1 HostSubnet |
openShiftClient.hostSubnets() |
network.operator.openshift.io/v1 OperatorPKI |
openShiftClient.operatorPKIs() |
oauth.openshift.io/v1 OAuthClientAuthorization |
openShiftClient.oAuthClientAuthorizations() |
oauth.openshift.io/v1 UserOAuthAccessToken |
openShiftClient.userOAuthAccessTokens() |
operator.openshift.io/v1 CloudCredential |
openShiftClient.operator().cloudCredentials() |
operator.openshift.io/v1 ClusterCSIDriver |
openShiftClient.operator().clusterCSIDrivers() |
operator.openshift.io/v1 Storage |
openShiftClient.operator().storages() |
operators.coreos.com/v1 OperatorCondition |
openShiftClient.operatorHub().operatorConditions() |
operators.coreos.com/v1 Operator |
openShiftClient.operatorHub().operators() |
packages.operators.coreos.com/v1 PackageManifest |
openShiftClient.operatorHub().packageManifests() |
security.openshift.io/v1 PodSecurityPolicyReview |
openShiftClient.podSecurityPolicyReviews() |
security.openshift.io/v1 PodSecurityPolicySelfSubjectReview |
openShiftClient.podSecurityPolicySelfSubjectReviews() |
security.openshift.io/v1 PodSecurityPolicySubjectReview |
openShiftClient.podSecurityPolicySubjectReviews() |
template.openshift.io/v1 BrokerTemplateInstance |
openShiftClient.brokerTemplateInstances() |
template.openshift.io/v1 TemplateInstance |
openShiftClient.templateInstances() |
tuned.openshift.io/v1 Profile |
openShiftClient.tuned().profiles() |
tuned.openshift.io/v1 Tuned |
openShiftClient.tuned().tuneds() |
user.openshift.io/v1 Identity |
openShiftClient.identities() |
user.openshift.io/v1 UserIdentityMapping |
openShiftClient.userIdentityMappings() |
Other improvements
Other notable improvements to the Kubernetes client in this release include:
- fabric8 Knative extension: Knative model updated to v0.23.0.
- fabric8 Tekton extension: Tekton pipeline model updated to v0.24.1.
- fabric8 Tekton extension: Tekton triggers model updated to v0.13.0.
- fabric8 Kubernetes mock server: Bug fixes related to ignoring the local kubeconfig, CRUD-mode fixes such as status subresource handling, apiVersion awareness, etc.
- Introduction of
withNewFilter()
in theKubernetesClient
DSL, offering better options for Kubernetes resource filtering.
Learn more about fabric8
This article has demonstrated just a few of fabric8's features for using Kubernetes APIs in a Java environment. For more examples, see the Kubernetes Java client examples repository. For a deep dive into using fabric8, visit the fabric8 Kubernetes Java Client Cheat Sheet.
Last updated: September 19, 2023