Using the Kubernetes Java Client: Metrics, Resource API, CRDs, Typed and Untyped Operations
This article demonstrates how to use the Kubernetes Java client to retrieve metrics, manage resources, work with custom resource definitions, and operate both typed and untyped APIs, providing code examples for creating, listing, updating, deleting, and watching Kubernetes objects.
Metrics
The Kubernetes Java client provides a rich API to retrieve metrics from a metrics‑enabled API server using client.top() .
Get all NodeMetrics
NodeMetricsList nodeMetricList = client.top().nodes().metrics();Get NodeMetrics for a specific node
NodeMetrics nodeMetric = client.top().nodes().withName("minikube").metric();Get all PodMetrics in all namespaces
PodMetricsList podMetricsList = client.top().pods().metrics();Get all PodMetrics in a specific namespace
PodMetricsList podMetricsList = client.top().pods().inNamespace("default").metrics();Get PodMetrics for a specific pod
PodMetrics podMetrics = client.top().pods().metrics("default", "nginx-pod");Resource API
Resource is an abstraction of compute resources (CPU, memory) in a Kubernetes cluster, used for pod requests and limits.
The client offers a generic API for handling most Kubernetes resources, which extend the HasMetadata class.
Retrieve a resource from the API server
Pod pod = client.resource(pod1).inNamespace("default").get();Apply a resource to the cluster (server‑side apply)
Pod pod1 = new PodBuilder()
.withNewMetadata().withName("resource-pod-" + RandomStringUtils.randomAlphanumeric(6).toLowerCase(Locale.ROOT)).endMetadata()
.withNewSpec()
.addNewContainer().withName("nginx").withImage("nginx").endContainer()
.endSpec()
.build();
client.resource(pod1).inNamespace("default").serverSideApply();Apply a resource and wait until it is ready
pod1 = client.resource(pod1).serverSideApply();
Pod p = client.pods().resource(pod1).waitUntilReady(10, TimeUnit.SECONDS);Delete a resource
client.resource(pod1).inNamespace("default").delete();Resource List
The client also provides a generic API for handling lists of resources.
Apply a list of resources
Service service = new ServiceBuilder()
.withNewMetadata().withName("my-service").endMetadata()
.withNewSpec()
.addToSelector("app", "Myapp")
.addNewPort().withProtocol("TCP").withPort(80).withTargetPort(new IntOrString(9376)).endPort()
.endSpec()
.build();
ConfigMap configMap = new ConfigMapBuilder()
.withNewMetadata().withName("my-configmap").endMetadata()
.addToData(Collections.singletonMap("app", "Myapp"))
.build();
KubernetesList list = new KubernetesListBuilder().withItems(deployment, service, configMap).build();
client.resourceList(list).inNamespace("default").serverSideApply();Delete a resource list
client.resourceList(new PodListBuilder().withItems(pod1, pod2, pod3).build()).inNamespace("default").delete();Custom Resource Definition (CRD)
CRDs allow users to extend the Kubernetes API with custom resources to meet specific application needs.
Load a CRD from a YAML file
CustomResourceDefinition crd = client.apiextensions().v1().customResourceDefinitions()
.load(new FileInputStream("/sparkapplication-crd.yml")).item();Get a CRD from the API server
CustomResourceDefinition crd = client.apiextensions().v1().customResourceDefinitions()
.withName("sparkclusters.radanalytics.io").get();Create a CRD
CustomResourceDefinition crd = new CustomResourceDefinitionBuilder()
.withApiVersion("apiextensions.k8s.io/v1")
.withNewMetadata().withName("sparkclusters.radanalytics.io").endMetadata()
.withNewSpec()
.withNewNames().withKind("SparkCluster").withPlural("sparkclusters").endNames()
.withGroup("radanalytics.io")
.withVersion("v1")
.withScope("Namespaced")
.withNewValidation()
.withNewOpenAPIV3SchemaLike(readSchema()).endOpenAPIV3Schema()
.endValidation()
.endSpec()
.build();
client.apiextensions().v1().customResourceDefinitions().resource(crd).create();Apply a CRD (server‑side apply)
client.apiextensions().v1().customResourceDefinitions().resource(crd).serverSideApply();List CRDs
CustomResourceDefinitionList crdList = client.apiextensions().v1().customResourceDefinitions().list();Delete a CRD
client.apiextensions().v1().customResourceDefinitions().withName("sparkclusters.radanalytics.io").delete();Typed Resource API
Any resource can be accessed via client.resources(Class) , returning a strongly‑typed client.
Obtain a client for a custom resource
MixedOperation
, Resource
> cronTabClient =
client.resources(CronTab.class);Get a custom resource
CronTab ct = cronTabClient.inNamespace("default").withName("my-second-cron-object").get();Create a custom resource
cronTabClient.inNamespace("default").create(cronTab1);List custom resources
CronTabList list = cronTabClient.inNamespace("default").list();Delete a custom resource
cronTabClient.inNamespace("default").withName("my-third-cron-object").delete();Update the status of a custom resource
cronTabClient.inNamespace("default").resource(updatedCronTab).updateStatus();Patch the status of a custom resource
cronTabClient.inNamespace("default").resource(updatedCronTab).patchStatus();Edit the status of a custom resource
cronTabClient.inNamespace("default").resource(cronTab1).editStatus(cronTab -> updatedCronTab);Watch a custom resource
cronTabClient.inNamespace("default").watch(new Watcher<>() {
@Override
public void eventReceived(Action action, CronTab resource) {
// handle event
}
@Override
public void onClose(WatcherException cause) {
// handle close
}
});Untyped (Raw) Resource API
If you prefer not to use a typed client, the raw API can handle resources generically.
Create a ResourceDefinitionContext
ResourceDefinitionContext ctx = new ResourceDefinitionContext.Builder()
.withGroup("jungle.example.com")
.withVersion("v1")
.withPlural("animals")
.withNamespaced(true)
.build();Load a resource from a YAML file
GenericKubernetesResource custom = client.genericKubernetesResources(ctx)
.load(new FileInputStream("cr.yaml")).item();Get a resource from the API server
GenericKubernetesResource obj = client.genericKubernetesResources(ctx)
.inNamespace(currentNamespace).withName("otter").get();Create a resource
GenericKubernetesResource created = client.genericKubernetesResources(ctx)
.inNamespace(currentNamespace).load(new FileInputStream("test-rawcustomresource.yml")).create();List resources
GenericKubernetesResourceList list = client.genericKubernetesResources(ctx)
.inNamespace(currentNamespace).list();Update a resource
GenericKubernetesResource walrus = client.genericKubernetesResources(ctx)
.inNamespace(currentNamespace).withName("walrus").get();
Map
spec = (Map
) walrus.getAdditionalProperties().get("spec");
spec.put("image", "my-updated-awesome-walrus-image");
walrus.getAdditionalProperties().put("spec", spec);
client.genericKubernetesResources(ctx).inNamespace(currentNamespace).resource(walrus).update();Delete a resource
client.genericKubernetesResources(ctx).inNamespace(currentNamespace).withName("otter").delete();Watch a raw custom resource
final CountDownLatch closeLatch = new CountDownLatch(1);
client.genericKubernetesResources(crdContext).inNamespace(namespace).watch(new Watcher<>() {
@Override
public void eventReceived(Action action, GenericKubernetesResource resource) {
logger.info("{}: {}", action, resource);
}
@Override
public void onClose(WatcherException e) {
logger.debug("Watcher onClose");
closeLatch.countDown();
if (e != null) {
logger.error(e.getMessage(), e);
}
}
});
closeLatch.await(10, TimeUnit.MINUTES);Spark Operator
The following examples show two ways to define the same context for a custom resource using the Spark Operator.
Typed resource API
@Group("sparkoperator.k8s.io")
@Plural("sparkapps")
@Version("v1beta2")
@Kind("SparkApplication")
public class SparkOperatorResource extends GenericKubernetesResource implements Namespaced { ... }Usage:
kubernetesClient.resources(SparkOperatorResource.class)
.inNamespace("myNamespace")...Untyped resource API
public static ResourceDefinitionContext getResourceDefinitionContext() {
return new ResourceDefinitionContext.Builder()
.withGroup("sparkoperator.k8s.io")
.withPlural("sparkapps")
.withVersion("v1beta2")
.withKind("SparkApplication")
.withNamespaced(true)
.build();
}Usage:
kubernetesClient.genericKubernetesResources(getResourceDefinitionContext())
.inNamespace("myNamespace")...These APIs allow flexible management of both built‑in and custom resources in a Kubernetes cluster.
FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.