The Java Cafe

The Java Cafe is a community of 3,665 amazing users

Java and Cloud content, by the community for the community

Create new account Log in
loading...

Java Operator SDK: An Introduction

summerspittman profile image Hoyt Summers Pittman ・3 min read

Red Hat launched Red Hat OpenShift Application Services at Summit 2021, and a part of that is the Application Services Operator which manages the bindings between remote service instances, service accounts, and applications inside of a Kubernetes cluster. I was excited to try writing this operator using the Java Operator SDK in the Java programming language, and I wanted to write this post to introduce you to some of the basics of writing an operator with that SDK.

Before we talk about a Java operator, let's take a moment to review Kubernetes, Kubernetes resources, and operators.

First, Kubernetes is a platform for managing containerized applications. k8s represents these applications as Kubernetes resources; these are built in resources such as Deployments, Pods, Secrets, StatefulSets, ReplicaSets, etc. An application is run on k8s by creating a these resources. It is k8s' responsibility to manage these resources, and the actions k8s takes based on these resources control the application's installation. These resources are edited as yaml files using text editors and can be used to swap databases, trigger backups, move applications to larger nodes, etc. With custom resources, we can extend the k8s API to not just manage the installation and upkeep of our application’s servers, but also the application itself.

Kubernetes custom resources are resources that extend the k8s API. These resources are processed by an operator instead of k8s. Operators a made up of custom resource definitions (CRDs) and custom controllers. CRDs contain the group, version, kind, and validation information necessary for k8s to store and serve custom resources. k8s is also responsible for communicating custom resources' events to operators' custom controllers. Controllers are responsible for responding to these events and updating the underlying custom resource. For instance, a k8s resource can be used to deploy PostgreSQL, but with a PostgreSQL operator you can add and managed users with its custom resource “User”. This "User" resource is a yaml file that is written and deployed just like any other k8s resource despite not being part of the stock k8s API.

While you can write an operator from scratch, most operators are written using an operator sdk. Operator SDKs provide tooling for creating CRDs, controllers, interacting with Kubernetes from your controller, testing, validating, etc.; the GoLang based operator-sdk is an early and popular example. There are also several operator SDKs available including the Java Operator SDK. The RHOAS operator was written using this SDK’s Quarkus extension.

The Java Operator SDK connects to your Kubernetes cluster and routes events on your custom resources to your Controller classes. Controllers are annotated POJOs that implement responses to creation, modification, and deletion of custom resources. The SDK makes writing an operator straightforward; one just has to write their custom resource definitions, annotate a Controller class with the CRD information, and then implement the ResourceController interface.

Here is a Controller and CustomResource implementation. Note the annotations; they provide the information necessary for the SDK to connect your controller and to resource watches.



import io.javaoperatorsdk.operator.api.*;

@Controller
public class MyCustomResourceController  implements ResourceController<MyCustomResource> {

  @Override
  void createOrUpdateResource(MyCustomResource resource, Context<MyCustomResource> context) {
      var template = "Hello, %s!";
      var name = resource.getSpec().getName();
      var status = new MyCustomResourceStatus();
      var stats.setHelloWorldMessage(String.format(template, name));

      resource.setStatus(status)

      return UpdateControl.updateStatusSubResource(resource);
    }

    @Override
  public DeleteControl deleteResource(MyCustomResource resource, Context<MyCustomResource> context) {
    return DeleteControl.DEFAULT_DELETE;
  }

}

Enter fullscreen mode Exit fullscreen mode

This controller operates on the following Custom Resource:

import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Plural;
import io.fabric8.kubernetes.model.annotation.Version;
import io.fabric8.kubernetes.api.model.Namespaced;

@Plural("mycustomresources")
@Group("secondsun.dev")
@Version("v1alpha1")
public class MyCustomResource extends CustomResource<MyCustomResourceSpec, MyCustomResourceStatus>
    implements Namespaced {

public static class MyCustomResourceSpec {
  private String name; 
  /* snipping getters/setters*/
}
public static class MyCustomResourceStatus {
  private String helloWorldMessage;
  /* snipping getters/setters*/
 }


}
Enter fullscreen mode Exit fullscreen mode

Once this controller is deployed in an cluster, users will be able to create "mycustomresource" resources with yaml in their Kubernetes cluster, and the operator SDK would receive those events, pass them to the controller's createOrUpdateResource method, and then update the status of the custom resource based on the processing in the controller. Before we can deploy the operator, we need to generate a CRD and apply that to our Kubernetes cluster. Currently, this is done by hand, but CRD generation is a feature coming to the Java operator SDK.

If you're curious about a full, deployable example, the Java Operator SDK samples project includes several. Hopefully you have a better understanding of Kubernetes Operators and how one would write them in Java.

Discussion (0)

pic
Editor guide