Motivations

At IBM, I work on a product called Cloud Pak for AIOps. It is a self-hosted IT operations management tool that runs on Kubernetes. Because it is self-hosted and is composed of dozens of microservices, one of the key challenges we face is keeping installation and upgrade simple.


We use a handful of operators (each with one or more custom resources and controllers) to deploy and manage all the Kubernetes resources within the application. These controllers manage a wide array of resources, both ones native in Kubernetes as well as custom resources.


Of the many resources managed by our Go-based controllers, some provide Go types while others are managed through unstructured (a map[string]interface{} in Go with Kubernetes type meta). Across all of these objects, there are often common actions we wish to take — applying labels, setting replica counts, configuring owner references, etc.


With github.com/griffindvs/stencil, I started putting together some tools to assist with these types of operations. This library is built on many of the same core functions as Manifestival, but with an extension to Go typed Kubernetes objects which can take advantage of controller-runtime caching. After spending some time with the library, I did not end up completely satisfied with the user experience, but I find some of the tools and ideas useful in other areas.


Generic transformations

As provided in Manifestival, a useful way for conducting generic transformations on Kubernetes resources is to manage them with unstructured. A basic transformer could apply an owner reference to an object:



A more complex transformation might take into account the current state of the object in the cluster. In stencil, I refer to these as context-aware transformers:



A context-aware transformation can be used for things like preventing inadvertent reductions in scale that could lead to failures. For example, perhaps the volume capacity in the claim template of a StatefulSet is configurable, but you want to prevent attempts to reduce the capacity. Before you apply the updated StatefulSet, you could transform the resource to retain the larger capacity from the object on the cluster.


Converting between representations

To transform objects as unstructured while retaining some of the benefits of Go typed objects, we need a mechanism to switch between the representations. Moving from typed to unstructured is fairly simple:



The one catch is that Go typed objects do not include type meta, since that information is inherent in the Go type. To address this, we can set the GVK of the unstructured after the conversion:



Moving the other direction is a bit more challenging. To do this generically, we need to create a pointer to whatever the type is that we determine at runtime:



We can then switch from unstructured to that type:


To then treat that type as a Kubernetes object, we need to assert that it implements client.Object: