Leveraging third party Operators in Kubernetes Operators

This article summarizes how to use third party operators in your own operators in order to provide an easier user experience.

Let me first explain why multiple layers of operators make sense using a concrete scenario. My little sample operator shows how to deploy a microservice which uses a managed database in the IBM Cloud. The microservice is deployed via my custom operator. The managed database is deployed via the IBM Cloud Operator.

From a user experience standpoint I’d like to have one single yaml which is as short as possible.

apiVersion: "ecommercesample.com/v1alpha1"
kind: ECommerceSample
metadata:
  name: ecommercesample1
  namespace: tenant1
spec:
  sqlUrl: https://raw.githubusercontent.com/IBM/multi-tenancy/main/installapp/postgres-config/create-populate-tenant-a.sql
  appTitle: Movies

The following yaml shows to how deploy a Postgres database on the IBM Cloud. Service creates the instance, Binding stores the Postgres credentials in a Kubernetes secret after the service has be created and initialized. This yaml should not be visible to users. Instead it should be generated by the custom operator.

apiVersion: ibmcloud.ibm.com/v1
kind: Service
metadata:
  name: ecommercesample1
  namespace: tenant1
spec:
  plan: standard
  serviceClass: databases-for-postgresql
---
apiVersion: ibmcloud.ibm.com/v1
kind: Binding
metadata:
  name: binding-ecommercesample1
  namespace: tenant1
spec:
  serviceName: ecommercesample1
  secretName: ecommercesample1-secret

The implementation is actually pretty straight forward. Let’s take a look how to trigger the creation of the Postgres resources in the reconcile loop using Quarkus and the Java Operator SDK.

The trick is to generate the Postgres Service and Binding definition with Java in JSON format and then to use the GenericKubernetesResource API.

Since the creation can take a good amount of time and since you should use this pattern anyway in the reconcile loop, the deployment code of the microservice only continues in a future loop run once the secret with Postgres credentials is available in Kubernetes.

secret = client.secrets().inNamespace(namespace).withName(ibmOperatorBindingSecretName).get();        
if (secret == null) {
    System.out.println("Secret " + ibmOperatorBindingSecretName + " does not exist");
    return UpdateControl.updateCustomResource(resource);
}

Keep an eye on this blog to find out more about how to build operators.