Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions charts/answer/ci/external-db-values.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,79 @@
# SPDX-License-Identifier: Apache-2.0
# CI: External PostgreSQL database
# CI: external PostgreSQL database backed by a local disposable PostgreSQL pod
database:
external:
vendor: postgres
host: postgresql.example.svc
host: postgresql
port: "5432"
name: answer
username: answer
password: external-password

extraManifests:
- apiVersion: v1
kind: Service
metadata:
name: postgresql
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
type: ClusterIP
ports:
- name: postgresql
port: 5432
targetPort: postgresql
selector:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
- apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
template:
metadata:
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
containers:
- name: postgresql
image: docker.io/library/postgres:18.4-alpine
imagePullPolicy: IfNotPresent
env:
- name: POSTGRES_DB
value: answer
- name: POSTGRES_USER
value: answer
- name: POSTGRES_PASSWORD
value: external-password
ports:
- name: postgresql
containerPort: 5432
readinessProbe:
exec:
command:
- pg_isready
- -U
- answer
- -d
- answer
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12
volumeMounts:
- name: data
mountPath: /var/lib/postgresql
volumes:
- name: data
emptyDir: {}
88 changes: 77 additions & 11 deletions charts/answer/ci/external-secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ admin:
database:
external:
vendor: postgres
host: postgresql.example.com
host: postgresql
port: "5432"
name: answer
username: answer
existingSecret: answer-db
Expand All @@ -17,30 +18,95 @@ backup:
externalSecrets:
enabled: true
secretStoreRef:
name: vault
kind: SecretStore
name: helmforge-fake-store
kind: ClusterSecretStore
admin:
enabled: true
data:
- secretKey: admin-password
remoteRef:
key: answer/admin
property: password
key: test/password
database:
enabled: true
data:
- secretKey: database-password
remoteRef:
key: answer/database
property: password
key: test/password
backup:
enabled: true
data:
- secretKey: access-key
remoteRef:
key: answer/s3
property: accessKey
key: test/username
- secretKey: secret-key
remoteRef:
key: answer/s3
property: secretKey
key: test/token

extraManifests:
- apiVersion: v1
kind: Service
metadata:
name: postgresql
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
type: ClusterIP
ports:
- name: postgresql
port: 5432
targetPort: postgresql
selector:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
- apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
template:
metadata:
labels:
app.kubernetes.io/name: postgresql
app.kubernetes.io/component: external-db-ci
spec:
containers:
- name: postgresql
image: docker.io/library/postgres:18.4-alpine
imagePullPolicy: IfNotPresent
env:
- name: POSTGRES_DB
value: answer
- name: POSTGRES_USER
value: answer
- name: POSTGRES_PASSWORD
value: helmforge-test-password
ports:
- name: postgresql
containerPort: 5432
readinessProbe:
exec:
command:
- pg_isready
- -U
- answer
- -d
- answer
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12
volumeMounts:
- name: data
mountPath: /var/lib/postgresql
volumes:
- name: data
emptyDir: {}
23 changes: 9 additions & 14 deletions charts/answer/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
=============================================================================
Apache Answer deployed successfully!
=============================================================================
Apache Answer deployed successfully.

SUMMARY:
1. Summary:
Release: {{ .Release.Name }}
Namespace: {{ .Release.Namespace }}
Chart: {{ .Chart.Name }} {{ .Chart.Version }}
Expand All @@ -13,7 +11,7 @@ SUMMARY:
Persistence: {{ if .Values.persistence.enabled }}enabled ({{ .Values.persistence.size }}){{ else }}disabled{{ end }}
ServiceAccount: {{ include "answer.serviceAccountName" . }}

ACCESS:
2. Access:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
Ingress: http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}
Expand All @@ -30,7 +28,7 @@ ACCESS:
http://127.0.0.1:9080
{{- end }}

ADMIN:
3. Credentials:
Username: {{ .Values.admin.name }}
Email: {{ .Values.admin.email }}
{{- if .Values.admin.existingSecret }}
Expand All @@ -42,7 +40,7 @@ ADMIN:
kubectl get secret {{ include "answer.adminSecretName" . }} -n {{ .Release.Namespace }} -o jsonpath="{.data.{{ include "answer.adminSecretKey" . }}}" | base64 -d
{{- end }}

DATABASE:
4. Configuration:
{{- $dbMode := include "answer.databaseMode" . }}
Mode: {{ $dbMode }}
{{- if eq $dbMode "sqlite" }}
Expand All @@ -56,7 +54,6 @@ DATABASE:
Password secret: {{ include "answer.databaseSecretName" . }} / {{ include "answer.databaseSecretKey" . }}
{{- end }}

ROUTING:
Service: {{ include "answer.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}
Ingress enabled: {{ .Values.ingress.enabled }}
Gateway API enabled: {{ .Values.gateway.enabled }}
Expand All @@ -67,7 +64,6 @@ ROUTING:
IP families: {{ join ", " . }}
{{- end }}

OPTIONAL FEATURES:
Backups: {{ .Values.backup.enabled }}
External Secrets: {{ .Values.externalSecrets.enabled }}
{{- if .Values.backup.enabled }}
Expand All @@ -78,7 +74,7 @@ OPTIONAL FEATURES:
SecretStore: {{ .Values.externalSecrets.secretStoreRef.name }} ({{ .Values.externalSecrets.secretStoreRef.kind }})
{{- end }}

VALIDATION:
5. Validation:
Release status:
helm status {{ .Release.Name }} -n {{ .Release.Namespace }}

Expand All @@ -91,7 +87,7 @@ VALIDATION:
Health check:
kubectl run {{ include "answer.fullname" . }}-health-check -n {{ .Release.Namespace }} --rm -i --restart=Never --image=docker.io/library/busybox:1.37 -- wget -qO- http://{{ include "answer.fullname" . }}:{{ .Values.service.port }}/healthz

TROUBLESHOOTING:
6. Troubleshooting:
Events:
kubectl get events -n {{ .Release.Namespace }} --sort-by=.lastTimestamp

Expand All @@ -101,16 +97,15 @@ TROUBLESHOOTING:
Init container logs when using PostgreSQL, MySQL, or external DB:
kubectl logs deploy/{{ include "answer.fullname" . }} -n {{ .Release.Namespace }} -c wait-for-db --tail=200

NOTES:
7. Upgrade Notes:
- Keep the admin password and database password stable across upgrades.
- Use PostgreSQL, MySQL, or an external database for production concurrency.
- Enable backups before changing storage, database mode, or chart major versions.
- Set answer.siteUrl to the externally reachable URL for production installs.

DOCUMENTATION:
8. Resources:
Chart: https://helmforge.dev/docs/charts/answer
Apache Answer: https://answer.apache.org
Source: https://github.com/helmforgedev/charts/tree/main/charts/answer

=============================================================================
Happy Helmforging :)
16 changes: 16 additions & 0 deletions charts/answer/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,19 @@ true
database-password
{{- end -}}
{{- end -}}

{{- define "answer.validate" -}}
{{- $_ := include "answer.databaseMode" . -}}
{{- $_ := include "answer.backupEnabled" . -}}
{{- if and .Values.ingress.enabled (empty .Values.ingress.hosts) -}}
{{- fail "ingress.hosts must contain at least one host when ingress.enabled=true" -}}
{{- end -}}
{{- if .Values.podLabels -}}
{{- if hasKey .Values.podLabels "app.kubernetes.io/name" -}}
{{- fail "podLabels must not override the selector label app.kubernetes.io/name" -}}
{{- end -}}
{{- if hasKey .Values.podLabels "app.kubernetes.io/instance" -}}
{{- fail "podLabels must not override the selector label app.kubernetes.io/instance" -}}
{{- end -}}
{{- end -}}
{{- end -}}
2 changes: 2 additions & 0 deletions charts/answer/templates/validate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
{{- include "answer.validate" . -}}
48 changes: 48 additions & 0 deletions charts/answer/tests/validation_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-License-Identifier: Apache-2.0
suite: Validation
templates:
- templates/validate.yaml
release:
name: test
namespace: default
tests:
- it: should reject ambiguous database auto-selection
set:
database.external.host: db.example.com
postgresql.enabled: true
asserts:
- failedTemplate:
errorMessage: "answer database selection is ambiguous: configure only one of database.external.host, postgresql.enabled, or mysql.enabled"

- it: should require backup S3 endpoint when backup is enabled
set:
backup.enabled: true
backup.s3.bucket: answer-backups
backup.s3.accessKey: access
backup.s3.secretKey: secret
asserts:
- failedTemplate:
errorMessage: "backup.s3.endpoint is required when backup.enabled is true"

- it: should require at least one ingress host when ingress is enabled
set:
ingress.enabled: true
asserts:
- failedTemplate:
errorMessage: "ingress.hosts must contain at least one host when ingress.enabled=true"

- it: should reject podLabels that override the name selector
set:
podLabels:
app.kubernetes.io/name: other
asserts:
- failedTemplate:
errorMessage: "podLabels must not override the selector label app.kubernetes.io/name"

- it: should reject podLabels that override the instance selector
set:
podLabels:
app.kubernetes.io/instance: other
asserts:
- failedTemplate:
errorMessage: "podLabels must not override the selector label app.kubernetes.io/instance"