chart2kro

Background: Helm, kro and ResourceGraphDefinitions

What kro and ResourceGraphDefinitions are, how they differ from Helm charts, why you would convert a chart, and the terms chart2kro uses.

Helm in one paragraph

A Helm chart is a set of Go templates plus a values.yaml. helm install (or helm template) merges your values into the defaults, renders the templates into Kubernetes manifests on the client, and applies them as a release. The cluster never sees the chart, only the rendered objects.

kro in one paragraph

kro (Kube Resource Orchestrator, developed in kubernetes-sigs/kro) is a controller that runs in the cluster. You give it a ResourceGraphDefinition (RGD), and it creates a new custom resource kind from it. Each object of that kind, an instance, makes kro create and continuously reconcile the resources the RGD describes.

Anatomy of an RGD

apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: webapp
spec:
  schema:                       # the new API: group, version, kind and fields
    apiVersion: v1alpha1
    kind: WebApp
    spec:
      replicas: integer | default=1
      image: string | required=true
  resources:                    # what each instance creates
    - id: deployment
      readyWhen:
        - ${deployment.status.?availableReplicas.orValue(0) == deployment.spec.?replicas.orValue(1)}
      template:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: ${schema.metadata.name}
        spec:
          replicas: ${schema.spec.replicas}
          # …
    - id: ingress
      includeWhen:
        - ${schema.spec.ingress.enabled}
      template: { … }
PartMeaning
schema.specThe instance's fields in SimpleSchema: type | marker=value …, e.g. string | default="nginx" immutable=true
${…}A CEL expression, evaluated per instance. schema.spec.x reads an instance field; deployment.status… reads another resource
includeWhenCreate the resource only when the expressions are true
readyWhenWhen kro considers the resource ready

Helm and kro compared

Helmkro
RunsOn the client, at install timeIn the cluster, continuously
Inputvalues.yaml, untypedThe instance's spec, typed and validated by the API server
Unit of deploymentReleaseInstance of a custom kind
DriftFixed on the next helm upgradeReconciled by the controller
LogicGo templates and sprig functionsCEL expressions
Lifecycle hookspre-install, post-upgrade, …None: everything is reconciled

Why convert a chart

  • Platform APIs. Offer kind: WebApp with five fields instead of a chart with two hundred values. RBAC, admission policies and GitOps tools work on it like on any other kind.
  • No client-side tooling. Delivery needs only kubectl apply; the cluster does the rendering.
  • Reconciliation. kro corrects drift and rolls out spec changes on its own.

What you give up: template logic that CEL cannot express, and Helm hooks. chart2kro tells you exactly which values are affected; see Limitations.

Why not convert by hand

Templates hide which values end up where. A value can land in several fields, inside a string, behind a condition, or pass through a function like upper. A hand-written RGD that gets one of these wrong deploys something different from the chart, and nothing tells you. chart2kro finds each mapping by experiment and checks it with kro's own runtime; see How it works.

Glossary

TermMeaning
ExposedThe value is an input of the new API (spec.<path>), verified against Helm.
FrozenThe value cannot be a verified input. It keeps its default in every instance, and the report gives a reason code.
FailedThe chart does not render with a probe value for it, so its effect is unknown. It keeps its default.
UnusedChanging the value changes nothing in the RGD's resources, with the current defaults.
ProbeA synthetic, type-preserving value (a distinct number, a distinct string, a flipped boolean) used to see which fields a value controls.
WitnessOne combination of values at which kro's output is compared with Helm's: the defaults, each value at its probes, and combinations. The report counts them.
CollectionA resource with forEach: kro creates one object per entry of a list input. chart2kro emits one for a range loop that creates objects
PrerequisitesObjects that cannot belong to one instance, such as the chart's CRDs. They go to prerequisites.yaml and are applied once.
Instance labelapp.kubernetes.io/instance: ${schema.metadata.name}, added to selectors that would otherwise match the pods of every instance.

Further reading