Files
devops-infra-helm-charts-gcp/docs/global/coding-guidelines/helm-values.md
T
2026-08-26 03:39:42 +05:30

10 KiB

Coding Guidelines — Helm values authoring

The hard stops live in ../SANCTITY_RULES.md; the layer/scope rules in ../AGENT_BOUNDARIES.md. This file is "how to write the values YAML well" — style, conventions, and the recurring footguns the linter doesn't catch.


File-level conventions

Where files go

Path Meaning
helm-overrides/<cluster>/<app>/custom-values.yaml The primary Helm values file Argo's valueFiles references. Default name. Do not invent alternatives.
helm-overrides/<cluster>/<app>/<extra>.yaml Sidecar raw manifests applied alongside the Helm release. Common shapes: computeclass/<x>-cc.yaml, external-dns-services/<svc>.yaml, elastic-cluster/argo-launch.yaml, mimir-distributed/alertmanager_config.yaml.
helm-templates/<chart>/Chart.yaml Chart manifest; usually a thin wrapper declaring an upstream dep.
helm-templates/<chart>/values.yaml The chart's own defaults — rarely edited; treat as upstream.
helm-templates/<chart>/templates/ Manifest templates — vanilla upstream unless intentionally forked. Don't edit casually.

Cluster directory naming

The cluster directory's name is a contract — it must match the Kubernetes cluster name registered in Argo CD. Conventions:

Pattern Use
k8s-<bu>-prd-ase1 Standard GKE prod cluster, BU-owned (AWS-style naming retained).
k8s-<bu>-prd-ase1c GCP zone-c twin.
k8s-shared-int-ase1 Shared int (pre-prod) cluster. The only non-prod cluster.
k8s-aurva-prd-ase1 Aurva integration.
db-<numeric-id>-... Auto-named dataplane / data-tier clusters. Minimal override sets (typically kube-state-metrics + victoria-metrics-agent).
k8s-supply-dev-ase1 The lone dev/sandbox cluster.

App directory naming

Inside helm-overrides/<cluster>/, each subdirectory is one Argo Application = one Helm release.

  • One Helm release per directory.
  • Multiple Contour instances per cluster is the norm — contour-external, contour-internal-0, contour-internal-1, contour-internal-intra-{0,1}. Each maps to a different node pool / dedicated taint or compute class. They are separate releases — don't merge them.
  • Versioned siblings (argo-cdargo-cd-green, kedakeda-2.17.1) live as parallel directories under helm-templates/, but their per-cluster overrides typically live under one directory until the migration cuts over. See blue-green-chart-migration.

Field-level conventions (custom-values.yaml)

Image references

  • Always pin images to Meesho's Artifact Registry mirror for production:
    image:
      registry: asia-southeast1-docker.pkg.dev
      repository: meesho-devops-admin-0622/admin/sre/<image>
      tag: <semver-or-sha>
    
  • Never use :latest or unpinned tags in production overrides.
  • Never reference Docker Hub, Quay, GCR upstream, or ECR directly in a production override. Mirror it via the platform team's image-pull workflow first.

fullnameOverride

  • Set it once, never change it. Service DNS, PVC binding, ConfigMap references, and downstream Argo Application names depend on stability. (SANCTITY_RULES R9)
  • For dataplane (db-*) clusters, the convention is fullnameOverride: <kind>-dbc-<bu>-prd (e.g. kube-state-metrics-dbc-dsci-prd).
  • For BU clusters, omit fullnameOverride unless the chart's default name collides with another release in the same namespace.

nameOverride

Almost never needed. Helm's <release>-<chart> naming is usually fine.

Replica counts and HPA bounds

  • Read the chart's defaults before specifying replicas. Some charts have HPA-managed replicas that conflict with replicaCount.
  • For HPA-managed releases, set minReplicas and maxReplicas and leave replicaCount unset (or set to null).
  • Don't lower minReplicas to zero unless the workload genuinely scales-from-zero.

Resource requests and limits

  • Always set resources.requests for production releases. Without requests, the scheduler treats the pod as best-effort.
  • Set resources.limits unless the chart documentation explicitly recommends omitting them (some sidecars deliberately go limit-less).
  • Don't copy resources from another cluster. Workload sizing is per-traffic-tier; the supply prd cluster's Contour requests are not the demand prd cluster's.

nodeSelector, tolerations, affinity, topologySpreadConstraints

The single biggest source of silent mis-deploys. Per SANCTITY_RULES R5:

Cluster type Key style Example
GKE Autopilot (k8s-central-prd-ase1, k8s-dsgpu-prd-ase1, k8s-shared-int-ase1) cloud.google.com/compute-class nodeSelector: {cloud.google.com/compute-class: contour-internal-0-cc}
Standard GKE dedicated: nodeSelector: {dedicated: contour-internal-0}
Most others dedicated: same

Cross-reference contour-nodeselector-tolerations-summary.md for the per-cluster Contour matrix. For non-Contour apps, copy from a sibling app on the same cluster, not from the same app on a different cluster.

Probes

  • Always set livenessProbe and readinessProbe for any long-running container.
  • For workloads that take >30 s to warm (Jenkins, JFrog, ClickHouse), bump initialDelaySeconds accordingly — not the timeout, not the period.
  • A startupProbe is the right tool for slow-warm containers; don't fight it with a 600s initialDelaySeconds on livenessProbe.

Persistence

  • StorageClass references go through the cluster-wide singletons in manifests/storageclass/: pd-standard-retain-dr, sc-filestore-standard, sc-pd-ssd, sc-pd-standard. Never reference a StorageClass that doesn't exist in manifests/storageclass/.
  • For stateful releases, set persistence.size explicitly. Default sizes are rarely right.
  • Never enable persistence.enabled: true without confirming the StorageClass and its retention/reclaim policy.

Secrets

  • Never inline secret values in custom-values.yaml. (SANCTITY_RULES R4)
  • Reference secrets by name: existingSecret: <secret-name>, where the secret is materialised by the per-cluster external-secrets app from GCP Secret Manager / Vault.
  • Most clusters have an external-secrets/ override directory; if your release needs a secret, the corresponding ExternalSecret lives there.

Annotations and labels

  • Add labels conservatively. Most charts already emit sensible label sets (app.kubernetes.io/name, etc.).
  • For ingress (Ingress, HTTPProxy, Contour Service), external-dns annotations and AWS/GCP load-balancer annotations are normal — copy from a sibling on the same cluster.
  • Don't invent label keys. If you find yourself adding meesho.com/<something>, double-check whether the project already has a convention for it.

Field-level conventions (raw sidecar manifests)

For helm-overrides/<cluster>/<app>/<extra>.yaml files (no Helm templating, applied as-is):

  • One Kubernetes resource per file unless they are tightly coupled.
  • Use apiVersion: v1 etc. — pin the API version explicitly.
  • Set metadata.namespace (don't rely on the Argo Application's destination.namespace for these).
  • For ComputeClass / NodeClass / BackendConfig / GKE-specific resources, sample a sibling cluster's existing file before authoring.
  • For external-dns-services/*.yaml, the Service resource carries external-dns.alpha.kubernetes.io/hostname annotations — match the cluster's existing DNS pattern.

YAML style

  • 2-space indent. No tabs.
  • Use single quotes for '*' and other glob-like strings; bare strings elsewhere where unambiguous.
  • Trailing newline at EOF.
  • No --- document separators unless you genuinely need multi-document YAML (rare in this repo).
  • Don't comment out fields; remove them. The repo doesn't use commented-out scaffolding.
  • Preserve key order from siblings. A reordered file is a noisy diff that drowns the real change.
  • Don't reformat unrelated YAML in passing. (SANCTITY_RULES R14)

Diff hygiene

When opening a PR:

  • One change-type per PR. Adding a service should not also "normalise labels on three other apps."
  • Keep diffs minimal. Don't reformat surrounding YAML.
  • Cite the procedure followed (link to one of docs/platform/procedures/*.md) in the PR description.
  • Show the validation you ranhelm template, yamllint, sibling-file diff, the kubectl context you ran a helm diff against.
  • Pair the sister-repo PR (devops-infra-argo-config) when adding a new app or cluster — link both.

Common mistakes the hooks do not catch

These are the recurring footguns that pre-commit hooks won't flag:

  1. nodeSelector / tolerations / computeClass copied from the wrong cluster. Pods stay Pending, or schedule on the wrong node pool.
  2. fullnameOverride modified. Downstream Service DNS resolves to nothing.
  3. spec.source.path in the sister repo's Application not updated to point at the new chart sibling after a blue-green migration.
  4. Image tag pinned to Docker Hub or Quay instead of the GAR mirror.
  5. replicaCount set on an HPA-managed release. HPA fights the static count.
  6. persistence.storageClass referencing a class that doesn't exist on this cluster — PVC stays Pending forever.
  7. existingSecret referencing a secret the per-cluster external-secrets app doesn't create. Pods crashloop on missing env.
  8. Chart.yaml dependencies[].version bumped without helm dependency update. Argo CD will use the lockfile and silently render the old version.
  9. Edits inside helm-templates/<chart>/templates/ — silently fork the chart; clobbered on next upstream sync.
  10. Sidecar raw-manifest namespace mismatch with the Helm release's namespace — orphaned resources.

The agent's job is to be the second pair of eyes on every one of these.