TT Lab
Get started
Learn Learning paths Courses

Authoring and Shipping Helm Charts

Does This Cluster Have That API — Capabilities and crds

Continue in TT Lab

Goal

You read the cluster's version and API list with .Capabilities to branch, and compare for yourself where those values come from in each command. You confirm through install and upgrade that the crds/ directory lives outside the release.

Why it matters

The moment you deploy one chart to several clusters, the question "does this cluster have that API?" arises. Helm brings the answer into templates with .Capabilities, but where this value comes from differs by command. helm template does not look at the cluster and uses built-in defaults, while helm install asks the real cluster even in dry-run. So "it doesn't show up locally but it does when deployed" happens, and conversely, if you trust only the CI render check, an object you have never seen before may pop out in production. crds/ adds one more layer to this — it is not a template, it is not in the release manifest, it goes in first at install, it is not touched on upgrade, and it remains even if you delete the release. If you check these five things once each, it becomes clear what a person has to do when operating a chart that contains CRDs.

Steps

  1. Create a /root/hc-cap/sensor chart (name sensor, version 0.1.0, with widgetSize: large in values) and put a <릴리스이름>-cap ConfigMap (release name in the placeholder) in templates/cap.yaml. Its data has six lines — kubeVersion, major, and minor (all from .Capabilities.KubeVersion), helmVersion, hasIngress (whether networking.k8s.io/v1/Ingress exists), and hasWidget (whether demo.labhub.io/v1/Widget exists). Wrap all six values in quotes. Save the result of helm template sense /root/hc-cap/sensor to /root/hc-cap/out/offline.yaml.
  2. Render the same chart as if it were Kubernetes 1.21.0 and save it to /root/hc-cap/out/kube121.yaml. In the result, kubeVersion must be v1.21.0 and minor must be 21.
  3. Render as if demo.labhub.io/v1/Widget exists and save it to /root/hc-cap/out/apiversions.yaml. In the result, hasWidget must be true and hasIngress must still be false — judge from that result whether this option replaces or adds to the default list.
  4. Create /root/hc-cap/sensor/templates/widget.yaml. Only when demo.labhub.io/v1/Widget exists, it emits an object with apiVersion: demo.labhub.io/v1, kind: Widget, the name <릴리스이름>-widget (release name), and spec.size set to .Values.widgetSize. Save the result rendered with no options to /root/hc-cap/out/branch-off.yaml, and the result rendered as if that API exists to /root/hc-cap/out/branch-on.yaml.
  5. Put the widgets.demo.labhub.io CRD in /root/hc-cap/sensor/crds/widget.yaml (group demo.labhub.io, kind Widget, plural widgets, namespaced scope, a single version v1, and a schema in which spec.size is a string). Then save the result rendered with no options to /root/hc-cap/out/tpl-no-crd.yaml, and the result given the option that also exports the CRD to /root/hc-cap/out/tpl-with-crd.yaml.
  6. Actually install it with helm install sense /root/hc-cap/sensor. Then save three things — the release manifest to /root/hc-cap/out/manifest.yaml, the CRD created in the cluster to /root/hc-cap/out/crd-live.yaml, and the data of the installed cap ConfigMap as JSON to /root/hc-cap/out/cap-live.json. Check whether the manifest contains the CRD and whether it contains the Widget object.
  7. Add a color (string) field to the schema in /root/hc-cap/sensor/crds/widget.yaml and run helm upgrade sense /root/hc-cap/sensor. Then save the property names under spec of the CRD that is currently in the cluster, joined with commas, on a single line to /root/hc-cap/out/crd-after-upgrade.txt. The chart has two fields; how many the cluster has is the answer to this step.
  8. Save the result of helm template sense /root/hc-cap/sensor --validate to /root/hc-cap/out/validate.yaml and the result of helm install probe /root/hc-cap/sensor --dry-run=server to /root/hc-cap/out/server-dryrun.yaml. Then write the three keys offline_has_widget, validate_has_widget, and dryrun_has_widget as booleans in /root/hc-cap/out/cap-compare.json. You read the values from hasWidget in step 1 and in the two files you just made.

Notes

What do you see when you render without a cluster?

Create a /root/hc-cap/sensor chart (name sensor, version 0.1.0, with widgetSize: large in values) and put a <릴리스이름>-cap ConfigMap (release name in the placeholder) in templates/cap.yaml. Its data has six lines — kubeVersion, major, and minor (all from .Capabilities.KubeVersion), helmVersion, hasIngress (whether networking.k8s.io/v1/Ingress exists), and hasWidget (whether demo.labhub.io/v1/Widget exists). Wrap all six values in quotes. Save the result of helm template sense /root/hc-cap/sensor to /root/hc-cap/out/offline.yaml.

helm template does not ask the cluster. It uses the default capability list built into Helm, so even an Ingress that actually exists shows up as missing here. If you check this fact with your own eyes first, the reason the values change in later steps becomes clear. The release name is sense throughout this lab.

Render pretending to be a different version without a cluster

Render the same chart as if it were Kubernetes 1.21.0 and save it to /root/hc-cap/out/kube121.yaml. In the result, kubeVersion must be v1.21.0 and minor must be 21.

helm template has an option to give the version directly (the one starting with kube in helm template --help). The value of this option is that when you have customers still using an old cluster, you can test whether the branches work properly without creating that cluster.

Render pretending a nonexistent API exists

Render as if demo.labhub.io/v1/Widget exists and save it to /root/hc-cap/out/apiversions.yaml. In the result, hasWidget must be true and hasIngress must still be false — judge from that result whether this option replaces or adds to the default list.

There is a separate option for giving the API list directly. To give several, use the option several times or join them with commas. The format is <그룹>/<버전>/<종류> (group, version, and kind). It is the method to use when testing a chart that depends on resources that come in as CRDs without a cluster.

Put objects in or leave them out depending on capabilities

Create /root/hc-cap/sensor/templates/widget.yaml. Only when demo.labhub.io/v1/Widget exists, it emits an object with apiVersion: demo.labhub.io/v1, kind: Widget, the name <릴리스이름>-widget (release name), and spec.size set to .Values.widgetSize. Save the result rendered with no options to /root/hc-cap/out/branch-off.yaml, and the result rendered as if that API exists to /root/hc-cap/out/branch-on.yaml.

If you wrap the whole if block with {{- ... }}, not even a blank line is left when the condition is false. If the condition is false, this file emits nothing, so one document vanishes entirely from the render result. It is the standard way to support both clusters that have the CRD and clusters that do not with one chart.

The crds directory is not templates

Put the widgets.demo.labhub.io CRD in /root/hc-cap/sensor/crds/widget.yaml (group demo.labhub.io, kind Widget, plural widgets, namespaced scope, a single version v1, and a schema in which spec.size is a string). Then save the result rendered with no options to /root/hc-cap/out/tpl-no-crd.yaml, and the result given the option that also exports the CRD to /root/hc-cap/out/tpl-with-crd.yaml.

Files inside crds/ do not go through the template engine — braces are just characters even if you use them. That is why they do not appear in the default render result either. To export them you have to give an option separately (the option containing crds in helm template --help). The reason this directory gets special treatment is that the CRD must go in before the objects that use it.

When you put it in a real cluster, the capabilities change

Actually install it with helm install sense /root/hc-cap/sensor. Then save three things — the release manifest to /root/hc-cap/out/manifest.yaml, the CRD created in the cluster to /root/hc-cap/out/crd-live.yaml, and the data of the installed cap ConfigMap as JSON to /root/hc-cap/out/cap-live.json. Check whether the manifest contains the CRD and whether it contains the Widget object.

helm get manifest <릴리스> (release in the placeholder) prints the manifest recorded in that release. You can pull the data out as JSON with kubectl get cm sense-cap -o jsonpath='{.data}'. Installation asks the cluster, so hasIngress differs from the local render. Because the CRD goes in before the templates, the Widget condition becomes true even on the first install — check it yourself.

Even if you fix crds and upgrade, the cluster stays as it is

Add a color (string) field to the schema in /root/hc-cap/sensor/crds/widget.yaml and run helm upgrade sense /root/hc-cap/sensor. Then save the property names under spec of the CRD that is currently in the cluster, joined with commas, on a single line to /root/hc-cap/out/crd-after-upgrade.txt. The chart has two fields; how many the cluster has is the answer to this step.

Helm puts crds/ in only at install and does not touch it on upgrade. The official documentation states this as a limitation and tells you to update CRDs by hand with kubectl apply. You can pull out the property names as the keys of .spec.versions[0].schema.openAPIV3Schema.properties.spec.properties from kubectl get crd <이름> -o json (CRD name in the placeholder).

The same chart, three answers — sort them out

Save the result of helm template sense /root/hc-cap/sensor --validate to /root/hc-cap/out/validate.yaml and the result of helm install probe /root/hc-cap/sensor --dry-run=server to /root/hc-cap/out/server-dryrun.yaml. Then write the three keys offline_has_widget, validate_has_widget, and dryrun_has_widget as booleans in /root/hc-cap/out/cap-compare.json. You read the values from hasWidget in step 1 and in the two files you just made.

--validate sends the render result to the API server for validation, so it also takes the capabilities from the cluster. --dry-run=server is the same. The only one that does not look at the cluster is helm template without options. They must be true and false without quotes — you must not write them as strings.