Finding out the Kubernetes Version in Operators

Kubernetes operators allow the automatic deployment and management of software. Since different Kubernetes distributions have different capabilities, operators need to handle these differences.

In the best case operators are able to operate software on different Kubernetes based platforms. As an example let’s take the self-deployed open source Kubernetes version and the managed AWS OpenShift version (EKS). While core functionality like deployments and service are identical, OpenShift adds convenience functionality like routes for easier networking. Another example are lightweight Kubernetes distributions, for example for edge devices, which often have been reduced in terms of functionality. Yet another example are Kubernetes versions that use different or no Ingress resources. Additionally obviously newer Kubernetes verions support functionality which is not available in older versions.

That’s why the operators need to know which capabilities are available. If certain capabilities are missing, e.g. a route, it might be able to use alternate mechanisms. Note that I talk about capabilities, not versions, since this approach is more reliable and more generic which leads to less code. Read the article Why not couple an Operator’s logic to a specific Kubernetes platform? for more details.

I’m working on a sample that describes different patterns and best practices to build operators with Golang. Let’s take a look how you can find out whether or not certain capabilities are supported.

In a nutshell there is an API ‘DiscoveryClient.ServerVersion’ and more importantly another API ‘DiscoveryClient.ServerGroupsAndResources’ to get a list of all available APIs (= capabilities).

The snippet shows how to check whether OpenShift routes are available.

var kubernetesServerVersion string
var runsOnOpenShift bool = false
...
func (reconciler *ApplicationReconciler) checkPrerequisites() bool {
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(managerConfig)
	if err == nil {
		serverVersion, err := discoveryClient.ServerVersion()
		if err == nil {
			kubernetesServerVersion = serverVersion.String()
			apiGroup, _, err := discoveryClient.ServerGroupsAndResources()
			if err == nil {
				for i := 0; i < len(apiGroup); i++ {
					if apiGroup[i].Name == "route.openshift.io" {
						runsOnOpenShift = true
					}
				}
			}
		}
	}
	return true
}

Note that you also define the required Kubernetes versions in the manifest when you use the Operator Lifecycle Manager.

Check out the repo and keep an eye on my blog. I’ll write more about other operator patterns soon.