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:
jx.imagePullSecrets- Nested image pull secrets structure from Jenkins XjxRequirements.ingress.*- Ingress configuration namespace referencing non-existentjx-requirements.ymldraftlabels - References to Skaffold/Draft tooling- 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-registrycommand - 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
jxreferences 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¶
- Immediate Bug Fix - Resolves
syrf-webImagePullBackOffissue in staging - Standards Compliance - Aligns with Kubernetes best practices
- Maintainability - Clear, self-documenting configuration structure
- Flexibility - Environment-specific subdomain configuration (no hardcoded
-jx.) - Consistency - All services follow identical patterns
- GitOps Integration - Charts work seamlessly with
cluster-gitopsvalues
Neutral¶
- Breaking Change - Requires updates to environment values in
cluster-gitops - Documentation - Comments and ADR capture migration rationale
Required Follow-up Actions¶
- Update
cluster-gitopsvalues: - Change
imagePullSecretsstructure from{name: ghcr-secret}to array format - Set
ingress.namespaceSubDomainappropriately per environment -
Remove any
jx.*references in global or environment-specific values -
Test deployments:
- Verify all services deploy successfully to staging
- Confirm ingress URLs resolve correctly
-
Validate imagePullSecrets work with GHCR
-
Update documentation:
- Update CLAUDE.md with new values structure
- 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.yamlto match - Rejected: Perpetuates non-standard patterns, confusing for future maintainers
Option 2: Dual Compatibility Layer¶
- Support both
jx.imagePullSecretsandimagePullSecrets - 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¶
- Kubernetes ImagePullSecrets Documentation
- Helm Best Practices
- ADR-001: CI/CD Approach
- ADR-003: Cluster Architecture