Skip to content
Draft
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
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,94 @@ This setup is only suitable for development and testing.
>
> However, please note that provisioning the Persistent Volume (PV) and PVC for storage backends like NFS is outside the scope of this Chart. You will need to provision the PV and PVC separately according to your storage backend's documentation before using the `dictionaries.existingClaim` parameter.

## Database service provider

By default WProofreader Server runs **without a database**. The chart exposes the
DB feature as **two independent concerns**:

- **`database`** — *does WProofreader talk to a DB, and how.* When `database.enabled=true`
the chart injects `WPR_ENABLE_DATABASE_PROVIDER` + `WPR_DATABASE_*` env into the
AppServer (it reads them directly at startup since v6.0.0), with the password from a Secret.
- **`databaseProvisioning`** — *do we also create/migrate that DB with db-manager.*
A separate operational decision (usually driven by CI/CD via `--set`). When
`databaseProvisioning.enabled=true` the chart runs the
[`db-manager`](https://github.com/WebSpellChecker/db-manager) image as a
`pre-install`/`pre-upgrade` Helm hook Job that bootstraps the database, applies the
versioned migrations, and creates the `appserver` and `app_manager` users
with per-table grants — **before** the AppServer pods start.

The two are independent: point `database` at an already-provisioned DB and leave
`databaseProvisioning` off, or run provisioning on its own.

> The MySQL server itself is **not** created by this chart. Point `database.host`
> at an existing MySQL (in-cluster, RDS, etc.). The provisioning Job connects as
> a privileged user (`databaseProvisioning.rootUser`, default `root`) to bootstrap
> the schema and create the application users.

### Minimal example (connect + provision)

```shell
helm install wproofreader-app ./wproofreader --namespace wsc --create-namespace \
--set licenseTicketID=$WPR_LICENSE_TICKET_ID \
--set database.enabled=true \
--set database.host=my-mysql-primary.wsc.svc.cluster.local \
--set database.appServerPassword=$APPSERVER_PASSWORD \
--set databaseProvisioning.enabled=true \
--set databaseProvisioning.rootPassword=$MYSQL_ROOT_PASSWORD \
--set databaseProvisioning.appManagerPassword=$APP_MANAGER_PASSWORD
```

### Connect to an already-provisioned DB (no provisioning)

```shell
helm install wproofreader-app ./wproofreader --namespace wsc \
--set licenseTicketID=$WPR_LICENSE_TICKET_ID \
--set database.enabled=true \
--set database.host=my-mysql-primary.wsc.svc.cluster.local \
--set database.appServerPassword=$APPSERVER_PASSWORD
```

### Using an existing Secret

Pre-create a Secret with key `appserver-password` (and, when provisioning,
`root-password` + `app-manager-password`), then reference it via
`database.existingSecret` — the chart renders no Secret of its own:

```shell
kubectl -n wsc create secret generic wpr-db \
--from-literal=appserver-password=... \
--from-literal=root-password=... \
--from-literal=app-manager-password=...

helm install wproofreader-app ./wproofreader --namespace wsc \
--set licenseTicketID=$WPR_LICENSE_TICKET_ID \
--set database.enabled=true \
--set database.host=my-mysql-primary.wsc.svc.cluster.local \
--set database.existingSecret=wpr-db \
--set databaseProvisioning.enabled=true
```

### Key parameters

| Parameter | Default | Description |
|-----------|---------|-------------|
| `database.enabled` | `false` | WProofreader uses a DB service provider |
| `database.host` | `""` | MySQL host (required when enabled or provisioning) |
| `database.port` | `3306` | MySQL port |
| `database.name` | `cloud_service` | Service database/schema name |
| `database.user` | `appserver` | User WProofreader authenticates as |
| `database.appServerPassword` | `""` | Password for `database.user` (or use `existingSecret`) |
| `database.existingSecret` | `""` | Pre-made Secret instead of inline passwords |
| `databaseProvisioning.enabled` | `false` | Run the db-manager provisioning Job |
| `databaseProvisioning.rootPassword` | `""` | Privileged password to bootstrap + create users |
| `databaseProvisioning.appManagerPassword` | `""` | Password for the `app_manager` user |
| `databaseProvisioning.image.repository` | `…/db-manager` | db-manager image (ECR) |
| `databaseProvisioning.contexts` | `internal,seed` | Migration contexts (`internal` = schema only for prod) |
| `databaseProvisioning.migrationMode` | `bootstrap` | `bootstrap` creates the DB if missing |

> With `databaseProvisioning.enabled=false`, the chart only wires the connection
> env — you must ensure the schema and the `appserver` user already exist.

## Use in production

For production deployments, it is highly recommended **to specify resource requests and limits for your Kubernetes pods**. This helps ensure that your applications have the necessary resources to run efficiently while preventing them from consuming excessive resources on the cluster which can impact other applications.
Expand Down
33 changes: 33 additions & 0 deletions manifests/db-credentials-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Example DB credentials Secret for the `database.existingSecret` option.
#
# Use this when you prefer to manage the database passwords yourself (sealed-secrets,
# SOPS, Vault, or plain kubectl) instead of passing them through Helm values.
#
# Usage:
# 1. Edit the passwords below (stringData is plaintext for convenience).
# 2. kubectl apply -n <namespace> -f manifests/db-credentials-secret.yaml
# 3. helm install ... \
# --set database.enabled=true \
# --set database.host=<mysql-host> \
# --set database.existingSecret=wproofreader-db-credentials \
# --set databaseProvisioning.enabled=true # if you also provision with db-manager
#
# Required keys:
# - appserver-password ALWAYS (WProofreader logs in as the appserver user)
# - root-password only when databaseProvisioning.enabled (db-manager bootstraps the DB)
# - app-manager-password only when databaseProvisioning.enabled (creates the app_manager user)
#
# When databaseProvisioning is OFF you can drop root-password and app-manager-password.
apiVersion: v1
kind: Secret
metadata:
name: wproofreader-db-credentials
# namespace: wsc # uncomment / set, or pass -n <namespace> to kubectl
type: Opaque
stringData:
# privileged password db-manager uses to bootstrap + create users
root-password: "change-me-root"
# password WProofreader authenticates with (database.user, default "appserver")
appserver-password: "change-me-appserver"
# password for the app_manager user db-manager creates (App-Manager login)
app-manager-password: "change-me-app-manager"
4 changes: 2 additions & 2 deletions wproofreader/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ description: A Helm chart for deploying webspellchecker/wproofreader in Kubernet

type: application

version: 1.3.1
version: 1.4.0

appVersion: "6.11.0.0"
appVersion: "6.13.1.0"
10 changes: 10 additions & 0 deletions wproofreader/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,14 @@ Version:

Port forwarding to the app server can be enabled with the following command (sudo might be required on some platforms)
kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "wproofreader.fullname" . }} {{ .Values.webPort | default (include "wproofreader.defaultWebPort" .) }}:{{ include "wproofreader.servicePort" . }}
{{- if .Values.database.enabled }}

Database service provider is ENABLED (schema "{{ .Values.database.name }}" on {{ .Values.database.host }}).
{{- end }}
{{- if .Values.databaseProvisioning.enabled }}
db-manager provisioning ran as a pre-install/pre-upgrade hook (schema "{{ .Values.database.name }}" on {{ .Values.database.host }}). Check it with:
kubectl logs --namespace {{ .Release.Namespace }} job/{{ include "wproofreader.fullname" . }}-db-provision
{{- else if .Values.database.enabled }}
Provisioning is OFF (databaseProvisioning.enabled=false) — ensure the schema and the "{{ .Values.database.user }}" user already exist in {{ .Values.database.name }}.
{{- end }}

12 changes: 12 additions & 0 deletions wproofreader/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ Create the name of the PersistentVolumeClaim.
{{- end }}
{{- end }}

{{/*
Name of the Secret holding database credentials.
Uses database.existingSecret when provided, otherwise a chart-managed Secret.
*/}}
{{- define "wproofreader.dbSecretName" -}}
{{- if .Values.database.existingSecret -}}
{{- .Values.database.existingSecret -}}
{{- else -}}
{{- printf "%s-db" (include "wproofreader.fullname" .) -}}
{{- end -}}
{{- end }}

{{/*
Prints key-value pairs encoded in base 64.
*/}}
Expand Down
100 changes: 100 additions & 0 deletions wproofreader/templates/db-provision-job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{{- if .Values.databaseProvisioning.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "wproofreader.fullname" . }}-db-provision
labels:
{{- include "wproofreader.labels" . | nindent 4 }}
app.kubernetes.io/component: db-provision
annotations:
# Runs after the db-secret (weight -10) and before WProofreader pods start,
# so the schema and users exist before the AppServer connects.
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
backoffLimit: {{ .Values.databaseProvisioning.backoffLimit }}
{{- with .Values.databaseProvisioning.ttlSecondsAfterFinished }}
ttlSecondsAfterFinished: {{ . }}
{{- end }}
template:
metadata:
labels:
{{- include "wproofreader.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: db-provision
spec:
restartPolicy: Never
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: db-manager
# tag defaults to the chart appVersion (the WProofreader version) so the
# db-manager image always carries the matching per-version schema migrations.
image: "{{ .Values.databaseProvisioning.image.repository }}:{{ .Values.databaseProvisioning.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.databaseProvisioning.image.pullPolicy }}
# 'args' overrides the image CMD; the entrypoint (/usr/local/bin/entrypoint.sh)
# still runs and receives "migrate" to execute the full provisioning lifecycle.
args: ["migrate"]
env:
# --- target database (connection comes from the `database` block) ---
- name: DB_HOST
value: {{ required "database.host is required when databaseProvisioning.enabled" .Values.database.host | quote }}
- name: DB_PORT
value: {{ .Values.database.port | quote }}
- name: DB_NAME
value: {{ .Values.database.name | quote }}
# --- privileged login db-manager uses to bootstrap + create users ---
- name: DB_USER
value: {{ .Values.databaseProvisioning.rootUser | quote }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "wproofreader.dbSecretName" . }}
key: root-password
# --- migration behaviour ---
- name: MIGRATION_MODE
value: {{ .Values.databaseProvisioning.migrationMode | quote }}
- name: DB_CONTEXTS
value: {{ .Values.databaseProvisioning.contexts | quote }}
- name: PROVISION_PREDEFINED_USERS
value: {{ .Values.databaseProvisioning.provisionPredefinedUsers | quote }}
# --- appserver user (WProofreader login) — name from `database`, password shared ---
- name: APPSERVER_USERNAME
value: {{ .Values.database.user | quote }}
- name: APPSERVER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "wproofreader.dbSecretName" . }}
key: appserver-password
# --- app_manager user (App-Manager login) ---
- name: APP_MANAGER_USERNAME
value: {{ .Values.databaseProvisioning.appManagerUsername | quote }}
- name: APP_MANAGER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "wproofreader.dbSecretName" . }}
key: app-manager-password
optional: true
# --- safety + resilience ---
- name: CREATE_BACKUP
value: {{ .Values.databaseProvisioning.createBackup | quote }}
- name: AUTO_ROLLBACK_ON_FAILURE
value: {{ .Values.databaseProvisioning.autoRollbackOnFailure | quote }}
- name: DB_MAX_RETRIES
value: {{ .Values.databaseProvisioning.maxRetries | quote }}
- name: DB_RETRY_INTERVAL
value: {{ .Values.databaseProvisioning.retryInterval | quote }}
- name: CHANGELOG_LOCK_STALE_MINUTES
value: {{ .Values.databaseProvisioning.changelogLockStaleMinutes | quote }}
- name: LOG_LEVEL
value: {{ .Values.databaseProvisioning.logLevel | quote }}
{{- with .Values.databaseProvisioning.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.databaseProvisioning.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
28 changes: 28 additions & 0 deletions wproofreader/templates/db-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{- if and (or .Values.database.enabled .Values.databaseProvisioning.enabled) (not .Values.database.existingSecret) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "wproofreader.dbSecretName" . }}
labels:
{{- include "wproofreader.labels" . | nindent 4 }}
{{- if .Values.databaseProvisioning.enabled }}
annotations:
# When provisioning is enabled this Secret must exist before the db-manager
# Job (lower weight). before-hook-creation only (no hook-succeeded) so it
# persists for the long-running WProofreader Deployment to reference.
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-10"
"helm.sh/hook-delete-policy": before-hook-creation
{{- end }}
type: Opaque
stringData:
# WProofreader logs in as the appserver user; db-manager also needs this to
# create that user, so it is required whenever the DB feature is on.
appserver-password: {{ required "database.appServerPassword is required when the database feature is enabled (or provide database.existingSecret)" .Values.database.appServerPassword | quote }}
{{- if .Values.databaseProvisioning.enabled }}
root-password: {{ required "databaseProvisioning.rootPassword is required when databaseProvisioning.enabled (or provide database.existingSecret)" .Values.databaseProvisioning.rootPassword | quote }}
{{- with .Values.databaseProvisioning.appManagerPassword }}
app-manager-password: {{ . | quote }}
{{- end }}
{{- end }}
{{- end }}
29 changes: 29 additions & 0 deletions wproofreader/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,35 @@ spec:
value: {{ .Values.webPort | default (include "wproofreader.defaultWebPort" .) | quote }}
- name: WPR_VIRTUAL_DIR
value: {{ .Values.virtualDir }}
{{- if .Values.database.enabled }}
- name: WPR_ENABLE_DATABASE_PROVIDER
value: "true"
- name: WPR_DATABASE_HOST
value: {{ required "database.host is required when database.enabled" .Values.database.host | quote }}
- name: WPR_DATABASE_PORT
value: {{ .Values.database.port | quote }}
- name: WPR_DATABASE_SCHEMA
value: {{ .Values.database.name | quote }}
- name: WPR_DATABASE_USER
value: {{ .Values.database.user | quote }}
- name: WPR_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "wproofreader.dbSecretName" . }}
key: appserver-password
{{- if .Values.database.enableRequestStatistic }}
- name: WPR_ENABLE_REQUEST_STATISTIC
value: "true"
{{- end }}
{{- if .Values.database.enableUserActionStatistic }}
- name: WPR_ENABLE_USER_ACTION_STATISTIC
value: "true"
{{- end }}
{{- if .Values.database.enableRequestValidation }}
- name: WPR_ENABLE_REQUEST_VALIDATION
value: "true"
{{- end }}
{{- end }}
{{- with .Values.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
Loading