I have developed an operator which monitors a CR that I developed, let's say its kind is MyCustomResource1. It then basically does what any operator does, in the reconcile loop it brings the current cluster state closer to the desired state.
Now, in the spec for my CR, there is a field like so
elastic_configuration:
cpu: 2
memory: 4Gi
The CR, as part of its reconciliation loop ensures that an Elasticsearch cluster with N pods comes up, the memory and CPU fields are determined by the fields elastic_configuration.cpu and elastic_configuration.memory respectively.
Now the actual Elasticsearch cluster in K8S is brought up by the ECK operator (which monitors its own CR of kind Elasticsearch) BUT I want the values for memory and CPU to be part of MyCustomResource1's spec
Whenever a user wants to increase the CPU and memory of the Elasticsearch pods, they should edit the values of the fields elastic_configuration.cpu and elastic_configuration.memory in my CR. The operator that I've written, in its reconciliation loop will identify that these values have changed and it will update the respective fields of the Elasticsearch resource's spec, which is being monitored by the ECK operator.
Question is what is the best way for my operator to update a CR ?
The most popular solution online suggests using unstructured object BUT I find it a bit tedious to repeatedly convert everything to a map[string]interface{}, then convert it to some desired type.
The other solution that i thought of was, since I'm using ECK version 2.13.0, I could just clone the entire repo or maybe copy the relevant struct (from 2.13.0 of course) that represents the Elasticsearch resource, update the fields of the struct in my operator's reconcile loop and then somehow use this struct through some K8S client to update the CR, I know I'm low on details here but it is a high level idea, one I thought of but I'm not sure is implemntable)
TLDR; What is the correct way to update a Custom Resource, say of kind Elasticsearch in K8S in Go, programatically ?
The ECK operator is an ordinary Go Kubebuilder operator. That means that it's possible to import its API types. So long as they're registered with your Scheme, you can interact with them normally.
In your main.go
file, put this with the other Scheme initialization
import elasticsearchv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1"
func init() {
utilruntime.Must(elasticsearchv1.AddToScheme(scheme))
utilruntime.Must(yourgroupv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
Then in your reconciler, you can use this type normally with the (controller-runtime-flavored) Kubernetes operator.
func (r *YourReconciler) updateElasticSearch(ctx context.Context, obj *yourgroupv1.Object) error {
es := &elasticsearchv1.Elasticsearch{}
name := types.NamespacedName{
Namespace: obj.GetNamespace(),
Name: obj.GetName() // this will be the name of the Elasticsearch object, change if needed
}
err := r.Get(ctx, name, es)
if err != nil {
return err
}
...
err = r.Update(ctx, es)
if err != nil {
return err
}
}
This is the same way you'd interact with any core Kubernetes API objects, but you need to import them into the controller-runtime environment first.
There is one obligatory legal note here. The ECK operator license is a non-standard bespoke license specific to Elasticsearch; it is not a "standard" open-source license. Incorporating it as a library into your operator will come with some obligations, and the first paragraph under "limitations" might apply to you. Consult your local legal team. (Many things are Apache- or BSD-licensed and these licenses are much better understood.)