Cloud Native 11 min read

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.

FunTester
FunTester
FunTester
Using the Kubernetes Java Client: Metrics, Resource API, CRDs, Typed and Untyped Operations

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.

JavaCloudNativeKubernetesMetricsClientCRD
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.