Skip to content

Latest commit

 

History

History
463 lines (349 loc) · 16.8 KB

File metadata and controls

463 lines (349 loc) · 16.8 KB
Status Active
Owner HyperFleet Team
Last Updated 2026-03-23

HyperFleet Helm Chart Conventions

Table of Contents

Overview

This standard defines conventions for Helm charts across all HyperFleet component repositories (API, Sentinel, Adapter). It ensures consistent naming, structure, security posture, and operational patterns across charts. All HyperFleet charts MUST follow these conventions.

1. Key Naming Conventions

values.yaml Keys

All keys in values.yaml MUST use camelCase.

# Good
broker:
  googlepubsub:
    projectId: "my-project"
    createTopicIfMissing: false
    deadLetterTopic: ""

adapterConfig:
  hyperfleetApi:
    baseUrl: http://hyperfleet-api:8000

# Bad
broker:
  googlepubsub:
    project_id: "my-project"
    create_topic_if_missing: false
    dead_letter_topic: ""

adapterConfig:
  hyperfleet_api:
    base_url: http://hyperfleet-api:8000

Generated Application Config Files

Config files generated by templates (e.g., config.yaml, broker.yaml) MUST use snake_case for property names, as required by the Configuration Standard.

# Template output (snake_case) from camelCase values input:
broker:
  type: googlepubsub
  googlepubsub:
    project_id: {{ .Values.broker.googlepubsub.projectId | quote }}
    create_topic_if_missing: {{ .Values.broker.googlepubsub.createTopicIfMissing }}

Environment Variables

Environment variables MUST use UPPER_SNAKE_CASE with the HYPERFLEET_ prefix where applicable, per the Configuration Standard.

Kubernetes Resource Names

Generated Kubernetes resource names (ConfigMaps, Services, etc.) MUST use kebab-case, derived from the Helm release name and chart name via fullname helper.

Helper Template Names

Helper templates MUST be prefixed with the chart name in kebab-case:

{{- define "hyperfleet-adapter.fullname" -}}
{{- define "hyperfleet-sentinel.labels" -}}
{{- define "hyperfleet-api.selectorLabels" -}}

2. Values.yaml API Surface

Mandatory Sections

Every HyperFleet chart MUST expose the following top-level sections:

Section Purpose
image Container image configuration
replicaCount Number of replicas (ignored when HPA enabled)
resources CPU/memory requests and limits
securityContext Container-level security context
podSecurityContext Pod-level security context
serviceAccount ServiceAccount creation and naming
podAnnotations Additional pod annotations
podLabels Additional pod labels
nodeSelector Node scheduling constraints
tolerations Toleration rules
affinity Affinity/anti-affinity rules
podDisruptionBudget PDB configuration
imagePullSecrets Image pull secrets for private registries
nameOverride Override chart name in resource names
fullnameOverride Override full resource name

Conditional Sections

Charts SHOULD expose these when the component requires them:

Section When Required
autoscaling Stateless components that support horizontal scaling
broker Components that consume or publish events
serviceMonitor Components that expose Prometheus metrics
config Components with application-specific configuration
observability Components with health/readiness probes and metrics (see Health Endpoints Standard)

3. Chart Versioning Strategy

  • Chart version (version in Chart.yaml) MUST follow SemVer 2.0.
  • Chart version and appVersion MUST be coupled — both track the same git tag. When a component is tagged v1.5.0, the chart publishes with version: 1.5.0 and appVersion: "1.5.0".
  • The build-helm-chart-oci-ta task derives the version from the git tag automatically, stripping the v prefix (e.g., v1.5.01.5.0).
  • Bump major for breaking changes (values.yaml API, application API).
  • Bump minor for new features.
  • Bump patch for fixes.

Why Coupled Versioning

Chart and application source live in the same repo and are tagged together. Independent versioning would require separate pipeline triggers, a version matrix, and tracking which chart versions are compatible with which app versions — complexity with no practical benefit for our team size and chart simplicity. See Helm OCI Distribution Design Section 6.

appVersion Convention

  • appVersion in source MUST be set to "0.0.0-dev" — it is a placeholder.
  • CI/release tooling stamps the real application version (e.g., 1.5.0) at release time, matching the chart version.
  • appVersion MUST NOT be used as a fallback for the container image tag. Image tags come from git SHAs (dev) or semver (release) and are always set explicitly via image.tag.
  • Umbrella charts (e.g., in hyperfleet-infra) MUST also use appVersion: "0.0.0-dev" since they do not build binaries.

4. Default Security Posture

All charts MUST ship with a restrictive security posture by default. Operators MAY relax these for specific environments.

Pod Security Context (REQUIRED defaults)

podSecurityContext:
  fsGroup: 65532
  runAsNonRoot: true
  runAsUser: 65532

Container Security Context (REQUIRED defaults)

securityContext:
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL
  readOnlyRootFilesystem: true
  seccompProfile:
    type: RuntimeDefault

Charts MUST NOT default allowPrivilegeEscalation to true, readOnlyRootFilesystem to false, or retain any Linux capabilities.

5. Secret Management Pattern

  • Sensitive values (passwords, tokens, API keys) MUST NOT be stored in ConfigMaps.
  • Secrets SHOULD be referenced via secretKeyRef in environment variables.
  • Charts SHOULD support an existingSecret field to reference pre-created Secrets.
  • Charts MUST NOT hardcode credentials in values.yaml defaults (use placeholder values like CHANGE_ME).
  • Production deployments SHOULD use external secret management (External Secrets Operator, Sealed Secrets, Vault).
# Good: Reference an existing secret
database:
  external:
    secretName: "my-db-credentials"

# Good: Environment variable from secret
env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: my-secret
        key: password

# Bad: Credential in ConfigMap or hardcoded default
database:
  password: "my-password"

6. Configuration Reload Strategy

Charts MUST use checksum annotations on the pod template to trigger pod restarts when ConfigMap content changes. This ensures configuration changes are picked up without manual intervention.

# In deployment.yaml pod template metadata:
annotations:
  checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}

Each ConfigMap that affects the pod SHOULD have its own checksum annotation. Use descriptive keys:

Key Purpose
checksum/config Application configuration
checksum/task-config Task/job configuration
checksum/secret Secret content (when chart manages the Secret)

Runtime hot-reload is NOT supported per the Configuration Standard. Pod restart is the reload mechanism.

7. NOTES.txt

Charts SHOULD include a templates/NOTES.txt file with post-install instructions. The content SHOULD include:

  • How to check the deployment status
  • How to access the service
  • Links to documentation

8. Chart Testing

Makefile Target

Charts MUST include a test-helm target in their Makefile, per the Makefile Conventions.

Required Test Scenarios

The test-helm target MUST validate:

  1. Lint (helm lint) - Chart syntax validation
  2. Template render (helm template) - Default values produce valid YAML
  3. Key configuration variants - Template renders for each major feature toggle (e.g., autoscaling enabled, PDB enabled, ServiceMonitor enabled)

Test Assertions

Template tests SHOULD assert on rendered output content, not just exit code:

# Good: Assert content
OUTPUT=$(helm template test charts/ --set image.registry=quay.io)
echo "$OUTPUT" | grep -q 'kind: ServiceMonitor' || { echo "FAIL: ServiceMonitor not found"; exit 1; }

# Bad: Only check exit code
helm template test charts/ --set image.registry=quay.io > /dev/null

9. Deprecation and Migration Pattern

When renaming or removing values.yaml keys:

  1. MUST bump chart major version for breaking changes.
  2. SHOULD add a fail message in templates to guide users:
{{- if .Values.broker.googlepubsub.project_id }}
{{- fail "broker.googlepubsub.project_id has been renamed to broker.googlepubsub.projectId (camelCase). Please update your values." }}
{{- end }}
  1. SHOULD document the migration in Chart.yaml description or a CHANGELOG.

10. Standard Labels and Annotations

Required Labels (all resources)

Charts MUST include these Kubernetes recommended labels on all resources via a shared labels helper:

helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}

The app.kubernetes.io/version label MUST use .Values.image.tag with .Chart.AppVersion as fallback. This ensures the label reflects what is actually deployed — a git SHA in dev (e.g., abc1234) or a semver in release (e.g., 1.2.3). The Kubernetes recommended labels spec explicitly allows both revision hashes and semantic versions.

Selector Labels

Deployment matchLabels and Service selectors MUST use a minimal, immutable subset:

app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}

Component Labels

Resources that are part of a larger deployment (e.g., ConfigMaps, sub-components) SHOULD include:

app.kubernetes.io/component: <component-name>

Custom Labels and Annotations

Charts MUST support adding custom labels and annotations via values:

podAnnotations: {}
podLabels: {}

11. Broker Configuration Pattern

Components that use the hyperfleet-broker library MUST follow a standard broker configuration structure.

values.yaml Structure

broker:
  # Broker type: googlepubsub or rabbitmq
  type: ""

  # Topic name (supports Helm templating)
  topic: ""

  googlepubsub:
    projectId: ""
    createTopicIfMissing: false
    createSubscriptionIfMissing: false
    subscriptionId: ""
    deadLetterTopic: ""
    maxOutstandingMessages: 1000
    numGoroutines: 10

  rabbitmq:
    url: ""
    queue: ""
    exchange: ""
    exchangeType: topic
    routingKey: ""

Key Rules

  • All broker values.yaml keys MUST use camelCase.
  • Generated broker.yaml config file MUST use snake_case (matching the broker library's expected format).
  • The template MUST translate camelCase values to snake_case output.
  • broker.type MUST be set explicitly. Templates MUST NOT attempt to infer the broker type from populated sub-keys.

12. Image Configuration

Charts MUST use the following image configuration following the Bitnami/community convention:

image:
  registry: CHANGE_ME
  repository: CHANGE_ME
  tag: ""  # Required — no default. Set via --set image.tag=<version>
  pullPolicy: Always

Field Definitions

Field Content Example
registry Registry host only quay.io, registry.ci.openshift.org
repository Org path + image name openshift-hyperfleet/hyperfleet-api
tag Version tag (git SHA or semver) abc1234, v1.2.3
pullPolicy Image pull policy Always, IfNotPresent

Template Usage

image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"

image.tag MUST NOT fall back to .Chart.AppVersion. Since appVersion is "0.0.0-dev" in source, a fallback would silently attempt to pull a non-existent image instead of failing fast.

Validation Guard

Charts MUST include a validation guard in _helpers.tpl that fails if image.tag is not set. The guard SHOULD be part of the chart's validateValues helper:

{{- define "<chart-name>.validateValues" -}}
{{- $registry := trim (toString .Values.image.registry) -}}
{{- if or (not $registry) (eq $registry "CHANGE_ME") -}}
{{- fail "image.registry must be set (e.g. --set image.registry=quay.io)" -}}
{{- end -}}
{{- $repository := trim (toString .Values.image.repository) -}}
{{- if or (not $repository) (eq $repository "CHANGE_ME") -}}
{{- fail "image.repository must be set (e.g. --set image.repository=openshift-hyperfleet/<component>)" -}}
{{- end -}}
{{- if not (trim (toString .Values.image.tag)) -}}
{{- fail "image.tag must be set (e.g. --set image.tag=abc1234)" -}}
{{- end -}}
{{- end }}

The validateValues helper MUST be invoked at the top of deployment.yaml (or any template that renders a workload):

{{- include "<chart-name>.validateValues" . }}
apiVersion: apps/v1
kind: Deployment
...

Default Values

  • registry and repository MUST default to CHANGE_ME to force explicit configuration.
  • repository values MUST include the org path (e.g., openshift-hyperfleet/hyperfleet-api).
  • tag MUST default to empty string. It is a required field — there is no fallback.
  • pullPolicy SHOULD default to Always during development. For production with pinned semver tags, operators SHOULD override to IfNotPresent to avoid unnecessary registry pulls.
# When overriding, repository MUST include org path
# Good: registry=quay.io, repository=openshift-hyperfleet/hyperfleet-api
# Bad:  registry=quay.io, repository=hyperfleet-api

Enforcement

  • CI: helm lint and helm template run via make test-helm in CI pipelines.
  • Code Review: Reviewers MUST verify values.yaml naming conventions and security defaults.
  • Migration: Existing charts that do not comply MUST be updated with deprecation fail messages for renamed keys.

References