Skip to main content

How `toValues.js` works

This page explains what src/app/toValues.js does and how it is structured, so you can safely extend it when you add new UI fields or chart features.

At a high level, toValues(state) takes the React app state from App.jsx and converts it into the JavaScript object that eventually becomes values.yaml for the Helm chart. It is the single place where we:

  • Normalize UI shapes (strings, booleans, structured lists) into the values schema.
  • Apply defaults and omit empty values.
  • Keep the output clean and predictable.

Helper functions

The file starts with a few small helpers:

  • clean(v)
    Recursively removes:

    • null, undefined, "" (empty string)
    • Empty arrays []
    • Empty objects {}

    This keeps values.yaml minimal (no noisy empty fields).

  • parseYaml(str)
    Safely parses a YAML string into a JS value. Returns null if the string is empty or invalid. We use this for all YAML textareas in the UI.

  • asList(v)
    Normalizes to an array:

    • null/undefinednull
    • Single value → [value]
    • Array → unchanged

    This is used when a YAML field can be either a single object or a list, but the chart expects an array.

buildWorkload(wl)

buildWorkload(wl) takes one workload object from React state and turns it into the corresponding entry under:

  • values.deployments[]
  • values.statefulsets[]
  • values.cronjobs[]

The steps inside buildWorkload mirror the workload sections in baseline.js:

  1. Basic identity

    • w.namewl.name
    • w.image is built from wl.image.repository, tag, digest, pullPolicy (with defaults and clean).
    • replicaCount is set for non‑CronJobs when replicas !== 1.
  2. CronJob schedule

    For type === 'CronJob', scheduleYaml (YAML textarea) is parsed and split into:

    • schedule
    • concurrencyPolicy
    • successfulJobsHistoryLimit
    • failedJobsHistoryLimit
    • backoffLimit
    • restartPolicy
    • suspend
  3. Service

    Uses wl.service.enabled plus the YAML from wl.service.yaml to produce:

    • service.enabled: false when explicitly disabled.
    • service.enabled: true when enabled with or without a spec (older charts with serviceEnabledDefault: false always emit enabled: true).
    • Spec fields from YAML merged on top when provided.

    The headless field has version-aware behavior controlled by opts.statefulsetHeadlessDefault:

    ChartstatefulsetHeadlessDefaultHeadless checkedHeadless uncheckedNot touched
    v1.0.0, v1.1.0falseemits trueemits nothingemits nothing
    v1.2.0+(absent)emits nothing (chart default)emits false (opt-out)emits nothing
  4. Route (OpenShift)

    Only for non‑CronJobs and when route.enabled is true:

    • Starts from advanced route.yaml (parsed into spec).
    • Merges in:
      • host from the routeHost text field (if set).
      • TLS certificate and key from routeTlsCert / routeTlsKey (if set), merged into route.tls.

    This produces a route object compatible with templates/route.yaml.

  5. Ingress

    Similar pattern to Route but for standard Kubernetes Ingress, from ingress.enabled and ingress.yaml.

  6. Ports, env, resources

    • ports (YAML) → w.ports (array)
    • env (YAML) → w.env (array)
    • envFrom (YAML) → w.envFrom (array)
    • resources (YAML) → w.resources (object)
  7. Command / args

    YAML arrays for command and args are normalized with asList into w.command and w.args.

  8. Probes

    From the single probesYaml textarea we split into:

    • livenessProbe
    • readinessProbe
    • startupProbe
  9. StatefulSet‑specific fields

    For type === 'StatefulSet':

    • stsYaml yields serviceName and podManagementPolicy.
    • updateStrategy YAML → w.updateStrategy.
    • vct YAML → w.volumeClaimTemplates (array).
  10. Labels and advanced pod settings

    • labels and podLabels YAML → w.labels, w.podLabels.
    • podSec and secCtx YAML → w.podSecurityContext, w.securityContext.
  11. Volumes and mounts

    There are two sources:

    • Advanced YAML fields:
      • volMounts (YAML) → starting volumeMounts list.
      • extraVols (YAML) → starting extraVolumes list.
    • Structured UI lists:
      • volumeMounts (list of {name, mountPath, subPath}) → merged into volumeMounts.
      • extraVolumes (list of {name, type, resourceName}) → merged into extraVolumes as:
        • persistentVolumeClaim.claimName
        • configMap.name
        • secret.secretName

    Finally, volumeMounts and extraVolumes are attached to w via the adv object.

  12. Sidecars, init containers, scheduling

    From various YAML fields:

    • sidecars and initCtrsw.sidecars, w.initContainers.
    • nodeSelw.nodeSelector.
    • tolerations YAML → w.tolerations (array).
    • affinity YAML → w.affinity.
    • graceterminationGracePeriodSeconds (unless it’s the default 30s).
  13. Autoscaling, KEDA, PDB, SCC, certificate

    • HPA (hpa.enabled, hpa.yaml) → w.autoscaling.
    • KEDA ScaledObject (keda.soEnabled, keda.soYaml) → w.keda.scaledObject.
    • KEDA ScaledJob (keda.sjEnabled, keda.sjYaml) → w.keda.scaledJob.
    • PDB (pdb.enabled, pdb.yaml) → w.podDisruptionBudget.
    • SCC (scc.enabled, scc.name) → w.scc.
    • Certificate (cert.enabled, cert.yaml) → w.certificate.

At the end, we return clean(w) so empty parts are removed.

toValues(state)

toValues takes the full app state from App.jsx:

const values = toValues({
workloads,
configMaps,
secrets,
sealedSecrets,
pvcs,
global,
persistentVolumes,
extraManifests,
})

and builds the final values object in these steps:

  1. Split workloads by type

    const deployments  = workloads.filter(w => w.type === 'Deployment').map(buildWorkload)
    const statefulsets = workloads.filter(w => w.type === 'StatefulSet').map(buildWorkload)
    const cronjobs = workloads.filter(w => w.type === 'CronJob').map(buildWorkload)

    Non‑empty arrays are assigned to:

    • values.deployments
    • values.statefulsets
    • values.cronjobs
  2. Global settings

    • ServiceAccount (global.saCreate, global.saName) → values.serviceAccount with create and name.
    • RBAC (global.rbacCreate, global.rbacRules) → values.rbac with create and rules (array).
    • Image pull secrets (global.pullSecrets YAML) → values.imagePullSecrets (array).
  3. Shared resources

    These come from the shared ResourceList sections:

    • ConfigMaps: configMaps[]values.configMaps = [{ name, data }]
    • Secrets: secrets[]values.secrets = [{ name, ...specFromYaml }]
    • Sealed Secrets: sealedSecrets[]values.sealedSecrets = [{ name, encryptedData }]
    • PVCs: pvcs[]values.persistentVolumeClaims = [{ name, size, storageClassName, accessModes }]
  4. PersistentVolumes and extra manifests

    persistentVolumes and extraManifests are lists of entries with YAML inside. The helper flattenYamlItems:

    • Parses each .yaml string.
    • Normalizes single vs list using asList.
    • Flattens everything into:
      • values.persistentVolumes
      • values.extraManifests
  5. Final clean

    The function returns values as‑is (it has already been cleaned where needed). When Docusaurus shows the values.yaml preview, it comes from this object.

When you add or change fields

When you update the UI config (e.g. in baseline.js) you usually need to:

  1. Decide where the data lives in state

    • For per‑workload fields: add to newWorkload in App.jsx.
    • For shared resources: add a new ResourceList section and corresponding state in App.jsx.
  2. Teach buildWorkload or toValues how to emit it

    • For simple scalar/YAML fields, parse with parseYaml and assign to w.<field> or values.<field>.
    • For lists, normalize with asList.
    • Use clean when you want to drop empty values.
  3. Keep complexity here, not in JSX
    JSX components (WorkloadForm, SectionPanel, ResourceList) are generic. Business logic about how UI → values.yaml should live in toValues.js, so chart maintainers can reason about it in one place.