Cloud Native 8 min read

Using Helm Capabilities to Build Charts Compatible with Multiple Kubernetes Versions

This article explains how to use Helm's Capabilities object and custom template functions to make Helm charts, especially Ingress resources, compatible with multiple Kubernetes versions by dynamically selecting the appropriate API version and fields.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Using Helm Capabilities to Build Charts Compatible with Multiple Kubernetes Versions

As Kubernetes versions are continuously released, many Helm chart packages cannot keep up, causing incompatibility with newer Kubernetes versions; therefore when developing Helm charts, it is necessary to consider compatibility with different Kubernetes versions.

The core of achieving cross‑version compatibility is to use the built‑in object Capabilities provided by Helm chart templates, which supplies information about the Kubernetes cluster’s supported features, including the following:

Capabilities.APIVersions – retrieves the set of API versions supported by the cluster.

Capabilities.APIVersions.Has $version – checks whether a specific version (e.g., batch/v1 ) or resource (e.g., apps/v1/Deployment ) is available.

Capabilities.KubeVersion and Capabilities.KubeVersion.Version – obtain the Kubernetes version string.

Capabilities.KubeVersion.Major – get the major version number.

Capabilities.KubeVersion.Minor – get the minor version number.

Capabilities.HelmVersion – object containing detailed Helm version information, matching the output of helm version .

Capabilities.HelmVersion.Version – semantic version of the current Helm.

Capabilities.HelmVersion.GitCommit – Git SHA‑1 of Helm.

Capabilities.HelmVersion.GitTreeState – state of the Helm Git tree.

Capabilities.HelmVersion.GoVersion – Go compiler version used to build Helm.

Using these objects we can decide which API version or attributes a resource should use; the following example demonstrates this with the Ingress resource.

Kubernetes 1.19 introduced a new API for Ingress: networking.k8s.io/v1 . It is similar to the previous beta version networking.k8s.io/v1beta1 but differs significantly from the older extensions/v1beta1 version; the resource’s fields change, so to support multiple versions the Ingress template must handle compatibility.

The new resource format is:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

The old resource format is:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

Which format to use depends on the cluster version. First, add several named templates to the chart’s _helpers.tpl file to determine the cluster version or API:

{{/* Allow KubeVersion to be overridden. */}}
{{- define "ydzs.kubeVersion" -}}
  {{- default .Capabilities.KubeVersion.Version .Values.kubeVersionOverride -}}
{{- end -}}

{{/* Get Ingress API Version */}}
{{- define "ydzs.ingress.apiVersion" -}}
  {{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" (include "ydzs.kubeVersion" .)) -}}
      {{- print "networking.k8s.io/v1" -}}
  {{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
    {{- print "networking.k8s.io/v1beta1" -}}
  {{- else -}}
    {{- print "extensions/v1beta1" -}}
  {{- end -}}
{{- end -}}

{{/* Check Ingress stability */}}
{{- define "ydzs.ingress.isStable" -}}
  {{- eq (include "ydzs.ingress.apiVersion" .) "networking.k8s.io/v1" -}}
{{- end -}}

{{/* Check Ingress supports pathType */}}
{{/* pathType was added to networking.k8s.io/v1beta1 in Kubernetes 1.18 */}}
{{- define "ydzs.ingress.supportsPathType" -}}
  {{- or (eq (include "ydzs.ingress.isStable" .) "true") (and (eq (include "ydzs.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" (include "ydzs.kubeVersion" .))) -}}
{{- end -}}

Above we use .Capabilities.APIVersions.Has to decide which apiVersion to use; if the version is networking.k8s.io/v1 we set isStable . We also check whether pathType is supported based on the version. Then in the Ingress object template we can use these named templates to decide which fields to include, as shown in the ingress.yaml file:

{{- $apiIsStable := eq (include "ydzs.ingress.isStable" .) "true" -}}
{{- $ingressSupportsPathType := eq (include "ydzs.ingress.supportsPathType" .) "true" -}}
{{- $ingressClass := index .Values "ingress-nginx" "controller" "ingressClass" }}
apiVersion: {{ include "ydzs.ingress.apiVersion" . }}
kind: Ingress
metadata:
  name: portal-ingress
  annotations:
    {{- if $ingressClass }}
    kubernetes.io/ingress.class: {{ $ingressClass }}
    {{- end }}
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "120"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  labels:
    {{- include "ydzs.labels" . | nindent 4 }}
spec:
  rules:
  {{- if eq .Values.endpoint.type "FQDN" }}
  - host: {{ required ".Values.endpoint.FQDN is required for FQDN" .Values.endpoint.FQDN }}
    http:
  {{- else }}
  - http:
  {{- end }}
      paths:
      - path: /
        {{- if $ingressSupportsPathType }}
        pathType: Prefix
        {{- end }}
        backend:
          {{- if $apiIsStable }}
          service:
            name: portal
            port:
              number: 80
          {{- else }}
          serviceName: portal
          servicePort: 80
          {{- end }}

By using the named templates in the Ingress manifest, the chart becomes compatible with different Kubernetes versions; the same technique can be applied to other resources such as Deployments.

Cloud NativeKubernetesIngressCompatibilityHelmTemplatingchart
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.