A Quick Dive into Kubernetes Operators - Part 4

Before diving in, make sure you have finished part 3 of the series. →
Table of Contents
In the previous parts of this series, we built the foundation for a Kubernetes operator, explored how to expose and manage custom resources and implememnted a custom search API. Now, let’s take the next step: implementing fully custom APIs inside Kubernetes. This allows you to present complex, aggregated, or domain-specific custom logic directly through the Kubernetes API.
Implement a Fully Customized API
Sometimes, the built-in API and operator capabilities aren’t enough, especially when dealing with complex data relationships or when you need fine-grained control over the data retrieval process. In these cases, you can implement fully customized services using an aggregation API.
When Simple Solutions Fall Short
Traditional solutions like eager loading work well for one-to-one or one-to-many relationships, but they can become inefficient or unwieldy when you have:
- Many-to-many relationships
- Complex data transformations
- Deeply nested relationships
By using an aggregation API, you decouple the frontend from the complex backend logic. This approach gives you full control over the data retrieval process, allowing you to optimize performance, reduce network chatter, and tailor the data precisely for each client’s specific requirements.
In this example you’ll create a cluster scope ClusterTask
and a namespaced scope CustomTask
service. Bring your own implementation.
|
|
Re-deploy the application.
|
|
|
|
|
|
Extending Built-in Resources with Aggregated Endpoints
The next service extends the Kubernetes API by creating a new combinedtasks endpoint. This endpoint behaves like any other Kubernetes resource (get, list, and watch), but instead of returning a plain Task, it provides a richer view that includes:
- The Task itself — with its metadata and spec fields (priority, deadline, details, etc.).
- All related Events — collected by label selector and attached under the status.events field.
In other words, a CombinedTask is a merged resource that combines the task object and its lifecycle history.
This design is useful because developers don’t need to manually query both tasks and events to understand what’s happening. By fetching combinedtasks, you immediately see the business object (Task) together with the operational signals (Events) in one unified response.
First you have to add new permissions to fetch events at internal/apiserver/task_search.go
file header.
|
|
You’ll continue by defining a CombinedTask resource that aggregates a Task with its related Events.
|
|
Update function signature, because new endpoint has to reach Kubernetes API.
|
|
Pass Kubernetes client to kaf.ServerConfig
.
|
|
Add service implementation to kaf.ServerConfig.APIKInds
.
|
|
Change API initialization at cmd/main.go
file.
|
|
To associate events with tasks and avoiding N+1 query problem, the controller must label them with the task UID. Update your controller to set index label on the created events at internal/controller/task_controller.go
.
|
|
Re-deploy the application.
|
|
Create your Task
object.
|
|
Validate business logic via kubectl
for simplicity, fetch the combinedtasks
in all namespaces.
|
|
NAMESPACE NAME AGE
default task-sample-1 45h
default task-sample-2 2d5h
default task-sample-3 45h
default task-sample-4 45h
default task-sample-5 3s
Fetch the combinedtasks
in default namespace.
|
|
NAMESPACE NAME AGE
default task-sample-1 45h
default task-sample-2 2d5h
default task-sample-3 45h
default task-sample-4 45h
default task-sample-5 5s
Fetch a combinedtask
by name in default namespace.
|
|
NAME AGE
task-sample-5 8s
Validate combinedtask
contains events.
|
|
apiVersion: v1
items:
- apiVersion: search.task.example.example.com/v1
kind: CombinedTask
metadata:
...
name: task-sample-5
namespace: default
spec:
deadline: "2025-08-19T16:52:15Z"
details: Sample task details for task-sample-4
priority: 3
taskState: Pending
status:
events:
- eventTime: null
firstTimestamp: null
involvedObject:
apiVersion: example.example.com/v1
kind: Task
name: task-sample-5
namespace: default
resourceVersion: "128025"
uid: 21e694cb-4735-45a5-91c4-198b0cf01311
lastTimestamp: null
message: Task has been updated
...
reason: TaskUpdated
reportingComponent: ""
reportingInstance: ""
source: {}
type: Normal
- eventTime: null
firstTimestamp: null
involvedObject:
apiVersion: example.example.com/v1
kind: Task
name: task-sample-5
namespace: default
resourceVersion: "128025"
uid: 21e694cb-4735-45a5-91c4-198b0cf01311
lastTimestamp: null
message: Task has been created
metadata:
...
reason: TaskCreated
reportingComponent: ""
reportingInstance: ""
source: {}
type: Normal
kind: List
metadata:
resourceVersion: "128025"
Kubernetes is a powerful platform, but its native API has limitations for complex, domain-specific logic. By using the API Aggregation Layer, you can create fully customized APIs that extend Kubernetes’ functionality and address these challenges. This approach allows you to implement specialized endpoints.
Ready to run your application in production? Learn how to prepare your Kubernetes to use HariKube as underlaying storage. →
That’s it! Imagine your own data topology and enhance your Kubernetes experience. Enjoy lower latency, higher throughput, data isolation, virtually unlimited storage, and simplified development. HariKube supports both flat and hierarchical topologies, allowing you to organize your databases like leaves on a tree.
Thank you for reading, and feel free to share your thoughts.