Skip to content

ADR-006: Helm Chart Standardization - Removal of Jenkins X Patterns

Context

The SyRF monorepo Helm charts were migrated from a legacy Jenkins X environment and contained numerous references to Jenkins X-specific patterns and configuration structures. These legacy patterns included:

  1. jx.imagePullSecrets - Nested image pull secrets structure from Jenkins X
  2. jxRequirements.ingress.* - Ingress configuration namespace referencing non-existent jx-requirements.yml
  3. draft labels - References to Skaffold/Draft tooling
  4. Hardcoded -jx. subdomain - Environment subdomain defaulting to Jenkins X convention

These patterns caused confusion, violated Kubernetes standards, and created an immediate blocker: the syrf-web deployment in syrf-staging failed with ImagePullBackOff because the chart expected jx.imagePullSecrets but cluster-gitops/global/values.yaml provided top-level imagePullSecrets.

Decision

We have completely removed all Jenkins X legacy patterns from all service Helm charts and adopted standard Kubernetes conventions.

Changes Implemented

1. Image Pull Secrets (All 4 Services)

Before:

# values.yaml
jx:
  imagePullSecrets: []

# deployment.yaml
{{- if .Values.jx.imagePullSecrets}}
      imagePullSecrets:
{{- range $pval := .Values.jx.imagePullSecrets }}
      - name: {{ quote $pval }}
{{- end }}
{{- end }}

After:

# values.yaml
# Image pull secrets for accessing private container registries
imagePullSecrets: []

# deployment.yaml
{{- if .Values.imagePullSecrets }}
      imagePullSecrets:
{{ toYaml .Values.imagePullSecrets | indent 8 }}
{{- end }}

Benefits:

  • Standard Kubernetes structure matching cluster-gitops/global/values.yaml
  • Compatible with kubectl create secret docker-registry command
  • Directly supports Kubernetes imagePullSecrets array format

2. Ingress Configuration (All 4 Services)

Before:

# values.yaml
# values we use from the `jx-requirements.yml` file if we are using helmfile and helm 3
jxRequirements:
  ingress:
    domain: ""
    externalDNS: false
    namespaceSubDomain: -jx.  # Hardcoded!
    serviceType: ""
    tls: {...}
    apiVersion: "networking.k8s.io/v1"
    annotations: {}

# Templates reference: .Values.jxRequirements.ingress.*

After:

# values.yaml
# Ingress configuration for domain and TLS
ingress:
  domain: ""                      # Base domain (e.g., syrf.org.uk)
  namespaceSubDomain: ""          # Prefix for environment (e.g., -staging., -prod., or . for root)
  externalDNS: false              # Enable external-dns integration
  serviceType: ""                 # Service type for ingress
  tls:
    enabled: false                # Enable TLS/HTTPS
    email: ""                     # Email for Let's Encrypt
    production: false             # Use Let's Encrypt production or staging
    secretName: ""                # Custom TLS secret name (optional)
  apiVersion: "networking.k8s.io/v1"  # Kubernetes Ingress API version
  annotations: {}                 # Shared ingress annotations

# Templates reference: .Values.ingress.*

Benefits:

  • Removed reference to non-existent jx-requirements.yml
  • Eliminated hardcoded -jx. subdomain (now environment-specific)
  • Clear, self-documenting field names with inline comments
  • Logical grouping of ingress-related configuration

3. Draft Pattern Cleanup (All 4 Services)

Before:

# deployment.yaml
metadata:
  labels:
    draft: {{ default "draft-app" .Values.draft }}
spec:
  template:
    metadata:
      labels:
        draft: {{ default "draft-app" .Values.draft }}

After:

# deployment.yaml (simplified)
metadata:
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
spec:
  template:
    metadata:
      labels:
        app: {{ template "fullname" . }}

Benefits:

  • Removed references to legacy Skaffold/Draft tooling
  • Cleaner label structure focused on actual application metadata

Scope of Changes

Total changes across all services:

  • 52 jx references removed (4 services × 13 avg references)
  • 16 files updated (4 services × 4 files: values.yaml, deployment.yaml, ingress.yaml, canary.yaml)

Reference breakdown by service:

Service jx refs Files
api 6 4
project-management 6 4
quartz 6 4
web 34 4
Total 52 16

Note: Web service had 30 references in ingress.yaml alone due to complex host name construction logic for multiple domains.

Consequences

Positive

  1. Immediate Bug Fix - Resolves syrf-web ImagePullBackOff issue in staging
  2. Standards Compliance - Aligns with Kubernetes best practices
  3. Maintainability - Clear, self-documenting configuration structure
  4. Flexibility - Environment-specific subdomain configuration (no hardcoded -jx.)
  5. Consistency - All services follow identical patterns
  6. GitOps Integration - Charts work seamlessly with cluster-gitops values

Neutral

  1. Breaking Change - Requires updates to environment values in cluster-gitops
  2. Documentation - Comments and ADR capture migration rationale

Required Follow-up Actions

  1. Update cluster-gitops values:
  2. Change imagePullSecrets structure from {name: ghcr-secret} to array format
  3. Set ingress.namespaceSubDomain appropriately per environment
  4. Remove any jx.* references in global or environment-specific values

  5. Test deployments:

  6. Verify all services deploy successfully to staging
  7. Confirm ingress URLs resolve correctly
  8. Validate imagePullSecrets work with GHCR

  9. Update documentation:

  10. Update CLAUDE.md with new values structure
  11. Update deployment guides if they reference old structure

Validation

All charts validated successfully with helm template:

 api: Helm template renders successfully
 project-management: Helm template renders successfully
 quartz: Helm template renders successfully
 web: Helm template renders successfully

Verification:

# Confirmed zero jx/draft references remain
grep -r "jx\." src/services/*/\.chart/     # No results
grep -r "jxRequirements" src/services/*/\.chart/  # No results
grep -r 'draft:.*default.*"draft-app"' src/services/*/\.chart/  # No results

Alternatives Considered

Option 1: Keep jx Namespace, Update Global Values

  • Keep charts using jx.imagePullSecrets
  • Update cluster-gitops/global/values.yaml to match
  • Rejected: Perpetuates non-standard patterns, confusing for future maintainers

Option 2: Dual Compatibility Layer

  • Support both jx.imagePullSecrets and imagePullSecrets
  • Rejected: Adds complexity, delays inevitable migration

Option 3: Gradual Migration

  • Migrate one service at a time
  • Rejected: Inconsistency between services, multiple rounds of testing

References