From e4e5762032a038e4c5c63490f47e0a8fd2d545bf Mon Sep 17 00:00:00 2001 From: NGPixel Date: Sun, 27 Sep 2026 11:52:35 -0400 Subject: [PATCH] ci: add helm chart --- .github/workflows/build.yml | 14 + CLAUDE.md | 5 +- backend/core/scheduler.ts | 5 +- config.sample.yml | 3 +- dev/chart/.helmignore | 11 + dev/chart/Chart.yaml | 25 + dev/chart/README.md | 159 ++++++ dev/chart/templates/NOTES.txt | 54 ++ dev/chart/templates/_helpers.tpl | 253 ++++++++++ dev/chart/templates/configmap.yaml | 48 ++ dev/chart/templates/deployment.yaml | 228 +++++++++ dev/chart/templates/httproute.yaml | 29 ++ dev/chart/templates/ingress.yaml | 41 ++ dev/chart/templates/pdb.yaml | 17 + dev/chart/templates/postgresql/configmap.yaml | 26 + dev/chart/templates/postgresql/secret.yaml | 18 + dev/chart/templates/postgresql/service.yaml | 44 ++ .../templates/postgresql/statefulset.yaml | 195 ++++++++ dev/chart/templates/pvc.yaml | 25 + dev/chart/templates/secret.yaml | 32 ++ dev/chart/templates/service.yaml | 39 ++ .../templates/serviceaccount.yaml | 5 +- .../templates/tests/test-connection.yaml | 39 ++ dev/chart/values.yaml | 462 ++++++++++++++++++ dev/helm/.helmignore | 23 - dev/helm/Chart.lock | 6 - dev/helm/Chart.yaml | 42 -- dev/helm/README.md | 177 ------- dev/helm/charts/postgresql-6.5.0.tgz | Bin 23426 -> 0 bytes dev/helm/templates/NOTES.txt | 21 - dev/helm/templates/_helpers.tpl | 108 ---- dev/helm/templates/deployment.yaml | 96 ---- dev/helm/templates/ingress.yaml | 61 --- dev/helm/templates/service.yaml | 23 - dev/helm/templates/tests/test-connection.yaml | 15 - dev/helm/values.yaml | 163 ------ 36 files changed, 1771 insertions(+), 741 deletions(-) create mode 100644 dev/chart/.helmignore create mode 100644 dev/chart/Chart.yaml create mode 100644 dev/chart/README.md create mode 100644 dev/chart/templates/NOTES.txt create mode 100644 dev/chart/templates/_helpers.tpl create mode 100644 dev/chart/templates/configmap.yaml create mode 100644 dev/chart/templates/deployment.yaml create mode 100644 dev/chart/templates/httproute.yaml create mode 100644 dev/chart/templates/ingress.yaml create mode 100644 dev/chart/templates/pdb.yaml create mode 100644 dev/chart/templates/postgresql/configmap.yaml create mode 100644 dev/chart/templates/postgresql/secret.yaml create mode 100644 dev/chart/templates/postgresql/service.yaml create mode 100644 dev/chart/templates/postgresql/statefulset.yaml create mode 100644 dev/chart/templates/pvc.yaml create mode 100644 dev/chart/templates/secret.yaml create mode 100644 dev/chart/templates/service.yaml rename dev/{helm => chart}/templates/serviceaccount.yaml (68%) create mode 100644 dev/chart/templates/tests/test-connection.yaml create mode 100644 dev/chart/values.yaml delete mode 100644 dev/helm/.helmignore delete mode 100644 dev/helm/Chart.lock delete mode 100644 dev/helm/Chart.yaml delete mode 100644 dev/helm/README.md delete mode 100644 dev/helm/charts/postgresql-6.5.0.tgz delete mode 100644 dev/helm/templates/NOTES.txt delete mode 100644 dev/helm/templates/_helpers.tpl delete mode 100644 dev/helm/templates/deployment.yaml delete mode 100644 dev/helm/templates/ingress.yaml delete mode 100644 dev/helm/templates/service.yaml delete mode 100644 dev/helm/templates/tests/test-connection.yaml delete mode 100644 dev/helm/values.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8704cdd8e..c15366872 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: run: | yq -iP '.version = strenv(REL_VERSION)' backend/package.json -o json yq -iP '.version = strenv(REL_VERSION)' frontend/package.json -o json + yq -i '.version = strenv(REL_VERSION) | .appVersion = strenv(REL_VERSION)' dev/chart/Chart.yaml - name: Upload translations source file uses: crowdin/github-action@v3 @@ -99,6 +100,19 @@ jobs: labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} + # After the image push, so that no published chart ever names an image that is not there + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Package and push Helm chart + env: + REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + helm lint --strict dev/chart + helm package dev/chart --destination _chart + echo "$REGISTRY_TOKEN" | helm registry login ghcr.io --username ${{ github.actor }} --password-stdin + helm push "_chart/wiki-${REL_VERSION}.tgz" oci://ghcr.io/requarks/charts + - name: Prepare build archive run: | mkdir -p _dist/blocks diff --git a/CLAUDE.md b/CLAUDE.md index 12d4362ff..03b7a1e29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,8 +33,9 @@ The backend is **TypeScript 7**; `frontend/` and `blocks/` are JavaScript. See `frontend/vite.config.js` in dev mode to learn the proxy target port. - `assets/` — **build output** of the frontend (`vite build` writes here), plus static assets under `assets/_assets/`. Served by the backend. Don't hand-edit. -- `dev/` — deployment/packaging artifacts: `dev/build/Dockerfile` (production image), `dev/helm/`, - `dev/packer/`. +- `dev/` — deployment/packaging artifacts: `dev/build/Dockerfile` (production image), `dev/chart/` + (the 3.x Helm chart, published as an OCI artifact — its `README.md` is the reference), `dev/packer/`. + `dev/helm/` is the 2.x chart and nothing for 3.x should be built on it. - `.devcontainer/` — VS Code dev container (app + postgres + pgAdmin + mailpit via docker-compose). Mailpit is the mail server for development: it accepts everything and delivers nothing, so a confirmation link or a password reset lands in a web inbox at `http://localhost:8025` rather than a diff --git a/backend/core/scheduler.ts b/backend/core/scheduler.ts index daf6212f4..03f301312 100644 --- a/backend/core/scheduler.ts +++ b/backend/core/scheduler.ts @@ -78,9 +78,12 @@ export default { tasks: null as Record | null, completionPromises: [] as CompletionPromise[], async init() { + // -> `availableParallelism` rather than `cpus().length`, which counts every core of the host: in a + // container that is the whole node, while this is capped by the cgroup CPU quota (a pod's + // `limits.cpu`, rounded down) and the CPU affinity mask this.maxWorkers = WIKI.config.scheduler.workers === 'auto' - ? os.cpus().length - 1 + ? os.availableParallelism() - 1 : WIKI.config.scheduler.workers if (this.maxWorkers < 1) { this.maxWorkers = 1 diff --git a/config.sample.yml b/config.sample.yml index 6c2f37473..ff815df7b 100644 --- a/config.sample.yml +++ b/config.sample.yml @@ -97,7 +97,8 @@ bodyParserLimit: 5242880 scheduler: # Maximum number of workers to run background cpu-intensive jobs. - # Leave 'auto' to use number of CPU cores as maximum. + # Leave 'auto' for one fewer than the CPUs available to the process (at + # least 1), which respects a container's CPU limit. workers: auto # --------------------------------------------------------------------- diff --git a/dev/chart/.helmignore b/dev/chart/.helmignore new file mode 100644 index 000000000..f84ac91fd --- /dev/null +++ b/dev/chart/.helmignore @@ -0,0 +1,11 @@ +.DS_Store +.git/ +.gitignore +*.swp +*.bak +*.tmp +*.orig +*~ +.idea/ +.vscode/ +*.tgz diff --git a/dev/chart/Chart.yaml b/dev/chart/Chart.yaml new file mode 100644 index 000000000..c5defbc96 --- /dev/null +++ b/dev/chart/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +name: wiki +description: Wiki.js 3.x, the next-generation open source wiki +type: application +# Both overwritten by the build workflow with the release version (`3.0.0-beta.`) before the +# chart is published, so that every chart version pins exactly the image built alongside it and +# upgrading the wiki is `helm upgrade --version `. Placeholders here, never bumped by hand. +version: 3.0.0-beta +# The image tag deployed when `image.tag` is left empty. +appVersion: 3.0.0-beta +# `lifecycle.preStop.sleep` (the default drain delay) is a native action from 1.30 on. +kubeVersion: '>=1.30.0-0' +home: https://js.wiki +icon: https://cdn.js.wiki/images/wikijs-butterfly.svg +sources: + - https://github.com/requarks/wiki +keywords: + - wiki + - documentation + - knowledge base +maintainers: + - name: Nicolas Giard + url: https://github.com/NGPixel +annotations: + licenses: AGPL-3.0-only diff --git a/dev/chart/README.md b/dev/chart/README.md new file mode 100644 index 000000000..cdef73f7f --- /dev/null +++ b/dev/chart/README.md @@ -0,0 +1,159 @@ +# Wiki.js Helm chart + +Deploys Wiki.js 3.x, with a bundled PostgreSQL 18 or an existing PostgreSQL 16+ server. + +## Installing + +The chart is published as an OCI artifact, so no `helm repo add` is needed: + +```sh +helm install wiki oci://ghcr.io/requarks/charts/wiki --version 3.0.0-beta. +``` + +Pass `--version` explicitly while the chart is a pre-release, because Helm skips pre-release +versions unless you name one or pass `--devel`. With no values set, you get one wiki replica +reachable inside the cluster only, backed by a bundled PostgreSQL on an 8 GiB claim. The first +start runs the database migrations; `kubectl rollout status deployment/wiki` shows when it is done. + +Unless `admin.password` is set, the first login is `admin@example.com` / `12345678`, and it has to be +changed. + +## Database + +### Bundled + +`postgresql.enabled: true` (the default) runs the official `postgres:18` image as a single +StatefulSet replica. The wiki connects as `postgresql.auth.username`, an ordinary role that owns its +own database. The `postgres` superuser is never handed to the wiki. + +- **Passwords** are generated on first install and kept in the `-postgresql` Secret, which + survives `helm uninstall`. Tools that render without cluster access, Argo CD among them, would + generate new passwords on every sync, so set `postgresql.auth.password` and `postgresPassword`, or + point `postgresql.auth.existingSecret` at a Secret with `password` and `postgres-password` keys. +- **Everything under `postgresql.auth` is applied once**, when the data directory is initialised. + Changing it afterwards changes what the wiki is told, not the database; alter the role to match. +- **Server settings** go in `postgresql.parameters` (passed as `-c key=value`), first-run SQL or + shell in `postgresql.initdbScripts`. +- **Major upgrades** (18 → 19) need a dump and restore, as they would anywhere. Pin a minor tag + (`postgresql.image.tag: "18.4"`) if you want upgrades to happen only when you choose. + +For replication, backups and failover, run PostgreSQL with an operator (CloudNativePG, for example) +and use it as an external database. + +### External + +Set `postgresql.enabled: false`, then say where the server is in **one** of two ways. Fill in +`externalDatabase.connectionString` or `externalDatabase.parameters` and leave the other empty; the +chart refuses to render with both. `externalDatabase.schema` (`wiki` by default) and +`externalDatabase.ssl` apply to either. + +The database user needs to own the database, or at least be allowed to create a schema in it. + +#### Option 1: a connection string + +Passed to the wiki as `DATABASE_URL`, so an operator's generated Secret can be used as it is. For +CloudNativePG: + +```yaml +postgresql: + enabled: false +externalDatabase: + connectionString: + existingSecret: mycluster-app # created by CloudNativePG for the cluster's app database + existingSecretKey: uri + ssl: + enabled: true + existingSecret: mycluster-ca # key: ca.crt +``` + +`connectionString.value` takes the string itself instead, and the chart stores it in a Secret. +Either way, percent-encode special characters in the password. TLS parameters in the string +(`sslmode`, `sslrootcert`, …) replace everything under `externalDatabase.ssl`. CloudNativePG's +`uri` has none, which is why it is paired with `ssl` and the cluster's CA above. + +#### Option 2: individual parameters + +```yaml +postgresql: + enabled: false +externalDatabase: + parameters: + host: pg.databases.svc + database: wiki + user: wiki + existingSecret: wiki-db # key: password + ssl: + enabled: true + existingSecret: pg-ca # key: ca.crt, and optionally tls.crt / tls.key +``` + +## Exposing it + +Both can be on at once, which is useful while moving from one to the other. + +- **Gateway API**: `httpRoute.enabled`, with `parentRefs` naming your Gateway and `hostnames`. + `rules` are passed through as written, and the chart adds the `backendRefs`. +- **Ingress**: `ingress.enabled`, `className`, `hosts`, `tls`. Most controllers cap request body + size, which also caps uploads; raise the limit with the controller's own annotation + (`nginx.ingress.kubernetes.io/proxy-body-size` for ingress-nginx). + +Behind either one, turn on **Admin → Security → Trust Proxy**, so the wiki records the visitor's +address rather than the proxy's. + +## Replicas + +`replicaCount` can be raised freely. Replicas coordinate through the database, collaborative editing +included, so no sticky sessions are needed. + +The data directory (`/wiki/data`) is per replica unless `persistence` points every replica at one +ReadWriteMany claim. That does not matter for the caches it normally holds, which are rebuilt from +the database. It does matter for a **Local Disk** or **Git** storage target left at its default +location under `data/`, which would otherwise be a separate copy on each replica. + +With one replica on a ReadWriteOnce claim, set `strategy.type: Recreate`. Otherwise a rolling +update's new pod may land on a node the volume cannot be attached to. + +## Hardening + +Both workloads run as non-root, with a read-only root filesystem, every capability dropped, no +privilege escalation, `RuntimeDefault` seccomp and no service account token. The wiki writes only to +`/wiki/data`, `/tmp` and `/home/node`; PostgreSQL writes only to its data volume, +`/var/run/postgresql`, `/tmp` and `/dev/shm`. + +The one thing the read-only root filesystem rules out is installing an extension from +**Admin → Extensions**, which runs `npm install` inside the image. Puppeteer is already in the +image; to add anything else, build an image `FROM` this one. + +## Health and shutdown + +`/_live` backs the startup and liveness probes, `/_ready` the readiness probe. The startup probe +allows five minutes for migrations before liveness takes over. `KUBERNETES_SERVICE_HOST` is set on +the container. It switches the wiki's shutdown to Kubernetes semantics: on SIGTERM `/_ready` turns +503 while in-flight and still-routed requests keep being served. The default `preStop` sleep gives +load balancers five seconds to drop the pod first. + +## Upgrading + +Every build of the wiki publishes a chart of the same version, `3.0.0-beta.`, whose +`appVersion` is that build's image. Upgrading the wiki is therefore upgrading the chart: + +```sh +helm upgrade wiki oci://ghcr.io/requarks/charts/wiki --version 3.0.0-beta. -f my-values.yaml +``` + +`helm rollback` returns to the previous image along with everything else. Leave `image.tag` unset, +since setting it pins the image no matter which chart version is installed. + +## Publishing + +The build workflow (`.github/workflows/build.yml`) publishes the chart. It writes the release +version into `version` and `appVersion` in `Chart.yaml`, and pushes the chart to +`oci://ghcr.io/requarks/charts` once the image is up. The two values committed in `Chart.yaml` are +placeholders, so nothing is bumped by hand. + +To try a change locally: + +```sh +helm lint --strict dev/chart +helm template wiki dev/chart -f my-values.yaml +``` diff --git a/dev/chart/templates/NOTES.txt b/dev/chart/templates/NOTES.txt new file mode 100644 index 000000000..41f862c05 --- /dev/null +++ b/dev/chart/templates/NOTES.txt @@ -0,0 +1,54 @@ +Wiki.js {{ .Values.image.tag | default .Chart.AppVersion }} is being deployed as {{ include "wiki.fullname" . }}. + +{{- if .Values.httpRoute.enabled }} + +It is routed through {{ range $i, $p := .Values.httpRoute.parentRefs }}{{ if $i }}, {{ end }}Gateway {{ $p.name }}{{ end }} for: +{{- range .Values.httpRoute.hostnames }} + {{ . }} +{{- end }} +{{- end }} +{{- if .Values.ingress.enabled }} + +It is served through the Ingress at: +{{- range $host := .Values.ingress.hosts }} +{{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} +{{- end }} +{{- end }} +{{- end }} +{{- if not (or .Values.httpRoute.enabled .Values.ingress.enabled) }} + +It is reachable inside the cluster only. To open it from this machine: + + kubectl --namespace {{ .Release.Namespace }} port-forward service/{{ include "wiki.fullname" . }} 8080:{{ .Values.service.port }} + +and browse to http://localhost:8080. +{{- end }} + +The first start runs the database migrations, which takes a little while: + + kubectl --namespace {{ .Release.Namespace }} rollout status deployment/{{ include "wiki.fullname" . }} + +{{- if not (or .Values.admin.password .Values.admin.existingSecret) }} + +Sign in as {{ .Values.admin.email | default "admin@example.com" }} with the password 12345678; you will be asked to change it. +{{- end }} +{{- if or .Values.httpRoute.enabled .Values.ingress.enabled }} + +Behind a Gateway or an Ingress, turn on Admin → Security → Trust Proxy, so that the wiki sees the +visitor's address rather than the proxy's. +{{- end }} +{{- if and .Values.postgresql.enabled (not .Values.postgresql.auth.existingSecret) (not .Values.postgresql.auth.password) }} + +The database passwords were generated and are kept in Secret {{ include "wiki.postgresql.fullname" . }}. +{{- end }} +{{- if and (gt (int .Values.replicaCount) 1) .Values.persistence.enabled (not (has "ReadWriteMany" .Values.persistence.accessModes)) }} + +WARNING: {{ .Values.replicaCount }} replicas share a data claim that is not ReadWriteMany. Replicas scheduled +on different nodes will not be able to mount it. +{{- end }} +{{- if and (gt (int .Values.replicaCount) 1) (not .Values.persistence.enabled) }} + +Note: each of the {{ .Values.replicaCount }} replicas has a data directory of its own. That is fine for the caches it +holds, but a Local Disk or Git storage target would be a separate copy on each replica. +{{- end }} diff --git a/dev/chart/templates/_helpers.tpl b/dev/chart/templates/_helpers.tpl new file mode 100644 index 000000000..7c12dacea --- /dev/null +++ b/dev/chart/templates/_helpers.tpl @@ -0,0 +1,253 @@ +{{/* Chart name, truncated to fit a DNS label. */}} +{{- define "wiki.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Fully qualified app name. Truncated to 49 rather than 63 so that the longest suffix added to it +(`-postgresql-hl`) still fits in a DNS label. +*/}} +{{- define "wiki.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 49 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 49 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 49 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "wiki.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "wiki.commonLabels" -}} +helm.sh/chart: {{ include "wiki.chart" . }} +app.kubernetes.io/name: {{ include "wiki.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 }} +app.kubernetes.io/part-of: wiki +{{- end }} + +{{/* +The component is part of both selectors on purpose: without it, the wiki's Service and Deployment +would select the PostgreSQL pod too, which carries the same name and instance labels. +*/}} +{{- define "wiki.selectorLabels" -}} +app.kubernetes.io/name: {{ include "wiki.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: wiki +{{- end }} + +{{- define "wiki.labels" -}} +{{ include "wiki.commonLabels" . }} +app.kubernetes.io/component: wiki +{{- end }} + +{{- define "wiki.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "wiki.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* `repository:tag`, or `repository@digest` when a digest is given. Takes the image dict and a default tag. */}} +{{- define "wiki.imageRef" -}} +{{- $img := index . 0 }} +{{- if $img.digest }} +{{- printf "%s@%s" $img.repository $img.digest }} +{{- else }} +{{- printf "%s:%s" $img.repository (toString (index . 1)) }} +{{- end }} +{{- end }} + +{{- define "wiki.image" -}} +{{- include "wiki.imageRef" (list .Values.image (.Values.image.tag | default .Chart.AppVersion)) }} +{{- end }} + +{{/* ---------------------------------------------------------------------------------------------- */}} +{{/* PostgreSQL */}} +{{/* ---------------------------------------------------------------------------------------------- */}} + +{{- define "wiki.postgresql.fullname" -}} +{{- printf "%s-postgresql" (include "wiki.fullname" .) }} +{{- end }} + +{{- define "wiki.postgresql.selectorLabels" -}} +app.kubernetes.io/name: {{ include "wiki.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: postgresql +{{- end }} + +{{- define "wiki.postgresql.labels" -}} +helm.sh/chart: {{ include "wiki.chart" . }} +app.kubernetes.io/name: {{ include "wiki.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: {{ .Values.postgresql.image.tag | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: wiki +app.kubernetes.io/component: postgresql +{{- end }} + +{{- define "wiki.postgresql.image" -}} +{{- include "wiki.imageRef" (list .Values.postgresql.image .Values.postgresql.image.tag) }} +{{- end }} + +{{- define "wiki.postgresql.secretName" -}} +{{- .Values.postgresql.auth.existingSecret | default (include "wiki.postgresql.fullname" .) }} +{{- end }} + +{{/* ---------------------------------------------------------------------------------------------- */}} +{{/* The database the wiki connects to, bundled or external */}} +{{/* ---------------------------------------------------------------------------------------------- */}} + +{{- define "wiki.db.host" -}} +{{- if .Values.postgresql.enabled }} +{{- include "wiki.postgresql.fullname" . }} +{{- else }} +{{- .Values.externalDatabase.parameters.host }} +{{- end }} +{{- end }} + +{{- define "wiki.db.port" -}} +{{- if .Values.postgresql.enabled }} +{{- .Values.postgresql.service.port }} +{{- else }} +{{- .Values.externalDatabase.parameters.port }} +{{- end }} +{{- end }} + +{{- define "wiki.db.name" -}} +{{- ternary .Values.postgresql.auth.database .Values.externalDatabase.parameters.database .Values.postgresql.enabled }} +{{- end }} + +{{- define "wiki.db.user" -}} +{{- ternary .Values.postgresql.auth.username .Values.externalDatabase.parameters.user .Values.postgresql.enabled }} +{{- end }} + +{{- define "wiki.db.schema" -}} +{{- ternary .Values.postgresql.schema .Values.externalDatabase.schema .Values.postgresql.enabled }} +{{- end }} + +{{/* The Secret and key the wiki reads its database password from. */}} +{{- define "wiki.db.secretName" -}} +{{- if .Values.postgresql.enabled }} +{{- include "wiki.postgresql.secretName" . }} +{{- else if .Values.externalDatabase.parameters.existingSecret }} +{{- .Values.externalDatabase.parameters.existingSecret }} +{{- else }} +{{- printf "%s-db" (include "wiki.fullname" .) }} +{{- end }} +{{- end }} + +{{- define "wiki.db.secretKey" -}} +{{- if .Values.postgresql.enabled }} +{{- .Values.postgresql.auth.secretKeys.userPasswordKey }} +{{- else if .Values.externalDatabase.parameters.existingSecret }} +{{- .Values.externalDatabase.parameters.existingSecretPasswordKey }} +{{- else -}} +password +{{- end }} +{{- end }} + +{{/* +Whether the wiki is handed a connection string (DATABASE_URL) rather than individual parameters. +Truthy or empty, for `if`. +*/}} +{{- define "wiki.db.useUrl" -}} +{{- $url := .Values.externalDatabase.connectionString }} +{{- if and (not .Values.postgresql.enabled) (or $url.value $url.existingSecret) }}true{{ end }} +{{- end }} + +{{/* The Secret and key the connection string is read from. */}} +{{- define "wiki.db.urlSecretName" -}} +{{- .Values.externalDatabase.connectionString.existingSecret | default (printf "%s-db" (include "wiki.fullname" .)) }} +{{- end }} + +{{- define "wiki.db.urlSecretKey" -}} +{{- if .Values.externalDatabase.connectionString.existingSecret }} +{{- .Values.externalDatabase.connectionString.existingSecretKey }} +{{- else -}} +url +{{- end }} +{{- end }} + +{{- define "wiki.db.tlsEnabled" -}} +{{- if and (not .Values.postgresql.enabled) .Values.externalDatabase.ssl.enabled }}true{{ end }} +{{- end }} + +{{/* ---------------------------------------------------------------------------------------------- */}} +{{/* Generated secrets */}} +{{/* ---------------------------------------------------------------------------------------------- */}} + +{{/* +A password: the value given, else the one already stored in the Secret, else a new random one. The +lookup is what keeps a generated password across `helm upgrade`; it returns nothing to a render that +has no cluster access (`helm template`, Argo CD), which is why those should be given passwords. +Takes (list $ secretName key value). +*/}} +{{- define "wiki.secretValue" -}} +{{- $ctx := index . 0 }} +{{- $name := index . 1 }} +{{- $key := index . 2 }} +{{- $given := index . 3 }} +{{- if $given }} +{{- $given | b64enc }} +{{- else }} +{{- $existing := lookup "v1" "Secret" $ctx.Release.Namespace $name }} +{{- if and $existing (hasKey (default dict $existing.data) $key) }} +{{- index $existing.data $key }} +{{- else }} +{{- randAlphaNum 32 | b64enc }} +{{- end }} +{{- end }} +{{- end }} + +{{/* ---------------------------------------------------------------------------------------------- */}} +{{/* Validation */}} +{{/* ---------------------------------------------------------------------------------------------- */}} + +{{- define "wiki.validate" -}} +{{- if hasKey .Values.config "db" }} +{{- fail "config.db is set by the chart: configure the database under postgresql or externalDatabase instead" }} +{{- end }} +{{- range $key := list "port" "bindIP" "dataPath" }} +{{- if hasKey $.Values.config $key }} +{{- fail (printf "config.%s is set by the chart and cannot be overridden" $key) }} +{{- end }} +{{- end }} +{{- if and .Values.postgresql.enabled (eq .Values.postgresql.auth.username "postgres") }} +{{- fail "postgresql.auth.username must not be \"postgres\": the wiki connects as its own role, not as the superuser" }} +{{- end }} +{{- if not .Values.postgresql.enabled }} +{{- $url := .Values.externalDatabase.connectionString }} +{{- $params := .Values.externalDatabase.parameters }} +{{- if and $url.value $url.existingSecret }} +{{- fail "externalDatabase.connectionString: set value or existingSecret, not both" }} +{{- end }} +{{- $hasUrl := or $url.value $url.existingSecret }} +{{- $hasParams := or $params.host $params.password $params.existingSecret }} +{{- if and $hasUrl $hasParams }} +{{- fail "externalDatabase: use connectionString or parameters, not both — empty the one you are not using" }} +{{- end }} +{{- if not (or $hasUrl $hasParams) }} +{{- fail "externalDatabase: set either connectionString or parameters when postgresql.enabled is false" }} +{{- end }} +{{- if $hasParams }} +{{- if not $params.host }} +{{- fail "externalDatabase.parameters.host is required" }} +{{- end }} +{{- if not (or $params.password $params.existingSecret) }} +{{- fail "externalDatabase.parameters: password or existingSecret is required" }} +{{- end }} +{{- end }} +{{- end }} +{{- if and .Values.externalDatabase.ssl.existingSecret (ne (empty .Values.externalDatabase.ssl.certKey) (empty .Values.externalDatabase.ssl.keyKey)) }} +{{- fail "externalDatabase.ssl.certKey and externalDatabase.ssl.keyKey go together: set both for a client certificate, or neither" }} +{{- end }} +{{- end }} diff --git a/dev/chart/templates/configmap.yaml b/dev/chart/templates/configmap.yaml new file mode 100644 index 000000000..ae77893e7 --- /dev/null +++ b/dev/chart/templates/configmap.yaml @@ -0,0 +1,48 @@ +{{- include "wiki.validate" . }} +{{- $config := deepCopy .Values.config }} +{{- $_ := set $config "port" 3000 }} +{{- $_ := set $config "bindIP" "0.0.0.0" }} +{{- $_ := set $config "dataPath" "/wiki/data" }} +{{- $ssl := .Values.externalDatabase.ssl }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "wiki.fullname" . }} + labels: + {{- include "wiki.labels" . | nindent 4 }} +data: + config.yml: | + {{- toYaml $config | nindent 4 }} + db: + {{- if include "wiki.db.useUrl" . }} + # Connecting through DATABASE_URL, which takes the place of host, port, user, pass and db. + {{- else }} + host: {{ include "wiki.db.host" . | toJson }} + port: {{ include "wiki.db.port" . }} + user: {{ include "wiki.db.user" . | toJson }} + # Substituted from the environment when the file is read. A block scalar rather than a quoted + # string, so that no character a password may contain can end it early. + pass: |2- + $(DB_PASS) + db: {{ include "wiki.db.name" . | toJson }} + {{- end }} + schema: {{ include "wiki.db.schema" . | toJson }} + {{- if include "wiki.db.tlsEnabled" . }} + ssl: true + sslOptions: + {{- if or $ssl.existingSecret (not $ssl.rejectUnauthorized) }} + auto: false + rejectUnauthorized: {{ $ssl.rejectUnauthorized }} + {{- if $ssl.existingSecret }} + ca: /etc/wiki/db-tls/{{ $ssl.caKey }} + {{- if $ssl.certKey }} + cert: /etc/wiki/db-tls/{{ $ssl.certKey }} + key: /etc/wiki/db-tls/{{ $ssl.keyKey }} + {{- end }} + {{- end }} + {{- else }} + auto: true + {{- end }} + {{- else }} + ssl: false + {{- end }} diff --git a/dev/chart/templates/deployment.yaml b/dev/chart/templates/deployment.yaml new file mode 100644 index 000000000..f14cd8110 --- /dev/null +++ b/dev/chart/templates/deployment.yaml @@ -0,0 +1,228 @@ +{{- $ssl := .Values.externalDatabase.ssl }} +{{- $adminSecret := .Values.admin.existingSecret | default (printf "%s-admin" (include "wiki.fullname" .)) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "wiki.fullname" . }} + labels: + {{- include "wiki.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- with .Values.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "wiki.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + # Rolls the pods when config.yml changes, since nothing in the pod spec would otherwise. + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "wiki.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "wiki.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.serviceAccount.automount }} + # Load-bearing, not tidiness: service links put `_PORT=tcp://…` in the environment of + # every pod for every service in the namespace, and the wiki reads `WIKI_PORT` as the port to + # listen on — so a Service named `wiki` would take the whole pod down. + enableServiceLinks: false + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- if .Values.waitForDatabase.enabled }} + initContainers: + # The wiki image is used rather than a second one, and asked only whether the port accepts a + # connection: a bundled server opens it only once initialisation is over. + - name: wait-for-db + image: {{ include "wiki.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - node + - -e + - | + const net = require('node:net') + // Only the host and port of a connection string are ever read or printed; it carries the password. + const url = process.env.DATABASE_URL ? new URL(process.env.DATABASE_URL) : null + const host = url ? url.hostname.replace(/^\[|\]$/g, '') : process.env.DB_HOST + const port = Number(url ? url.port || 5432 : process.env.DB_PORT) + const attempt = () => { + const socket = net.connect({ host, port, timeout: 3000 }) + socket.once('connect', () => { console.info(`${host}:${port} is accepting connections`); socket.destroy(); process.exit(0) }) + const retry = (err) => { console.info(`Waiting for ${host}:${port}... (${err?.code ?? 'timeout'})`); socket.destroy(); setTimeout(attempt, 2000) } + socket.once('error', retry) + socket.once('timeout', retry) + } + attempt() + env: + {{- if include "wiki.db.useUrl" . }} + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: {{ include "wiki.db.urlSecretName" . }} + key: {{ include "wiki.db.urlSecretKey" . }} + {{- else }} + - name: DB_HOST + value: {{ include "wiki.db.host" . | quote }} + - name: DB_PORT + value: {{ include "wiki.db.port" . | quote }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- with .Values.waitForDatabase.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + containers: + - name: wiki + image: {{ include "wiki.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 3000 + protocol: TCP + env: + - name: CONFIG_FILE + value: /etc/wiki/config.yml + # Switches the health endpoints to Kubernetes semantics: a replica shutting down reports + # not ready but keeps serving what is still routed to it. The kubelet normally provides this + # variable already; it is set here so the behaviour does not rest on that, and to a name + # that still reaches the API server should anything in the pod ever use it. + - name: KUBERNETES_SERVICE_HOST + value: kubernetes.default.svc + {{- if include "wiki.db.useUrl" . }} + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: {{ include "wiki.db.urlSecretName" . }} + key: {{ include "wiki.db.urlSecretKey" . }} + {{- else }} + - name: DB_PASS + valueFrom: + secretKeyRef: + name: {{ include "wiki.db.secretName" . }} + key: {{ include "wiki.db.secretKey" . }} + {{- end }} + {{- with .Values.admin.email }} + - name: ADMIN_EMAIL + value: {{ . | quote }} + {{- end }} + {{- if or .Values.admin.password .Values.admin.existingSecret }} + - name: ADMIN_PASS + valueFrom: + secretKeyRef: + name: {{ $adminSecret }} + key: {{ ternary .Values.admin.existingSecretPasswordKey "password" (not (empty .Values.admin.existingSecret)) }} + {{- end }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.extraEnvFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /etc/wiki + readOnly: true + - name: data + mountPath: /wiki/data + # The root filesystem is read-only; these are what the wiki, git, ssh and Chromium write to + # besides the data directory. + - name: tmp + mountPath: /tmp + - name: home + mountPath: /home/node + {{- if and (include "wiki.db.tlsEnabled" .) $ssl.existingSecret }} + - name: db-tls + mountPath: /etc/wiki/db-tls + readOnly: true + {{- end }} + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ include "wiki.fullname" . }} + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (printf "%s-data" (include "wiki.fullname" .)) }} + {{- else }} + {{- with .Values.persistence.sizeLimit }} + emptyDir: + sizeLimit: {{ . }} + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + - name: tmp + emptyDir: {} + - name: home + emptyDir: {} + {{- if and (include "wiki.db.tlsEnabled" .) $ssl.existingSecret }} + - name: db-tls + secret: + secretName: {{ $ssl.existingSecret }} + # Owned by root and readable through fsGroup, which 0400 would shut out. + defaultMode: 0440 + {{- end }} + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/dev/chart/templates/httproute.yaml b/dev/chart/templates/httproute.yaml new file mode 100644 index 000000000..9c64f9959 --- /dev/null +++ b/dev/chart/templates/httproute.yaml @@ -0,0 +1,29 @@ +{{- if .Values.httpRoute.enabled }} +{{- $fullname := include "wiki.fullname" . }} +{{- $port := .Values.service.port }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullname }} + labels: + {{- include "wiki.labels" . | nindent 4 }} + {{- with .Values.httpRoute.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- toYaml (required "httpRoute.parentRefs needs at least one Gateway" .Values.httpRoute.parentRefs) | nindent 4 }} + {{- with .Values.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.httpRoute.rules }} + {{- $rule := merge (dict "backendRefs" (list (dict "name" $fullname "port" $port))) (omit (default dict .) "backendRefs") }} + - {{ toYaml $rule | nindent 6 | trim }} + {{- end }} +{{- end }} diff --git a/dev/chart/templates/ingress.yaml b/dev/chart/templates/ingress.yaml new file mode 100644 index 000000000..5a7542404 --- /dev/null +++ b/dev/chart/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.ingress.enabled }} +{{- $fullname := include "wiki.fullname" . }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullname }} + labels: + {{- include "wiki.labels" . | nindent 4 }} + {{- with .Values.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType | default "Prefix" }} + backend: + service: + name: {{ $fullname }} + port: + name: http + {{- end }} + {{- with .host }} + host: {{ . | quote }} + {{- end }} + {{- end }} +{{- end }} diff --git a/dev/chart/templates/pdb.yaml b/dev/chart/templates/pdb.yaml new file mode 100644 index 000000000..1f8be237b --- /dev/null +++ b/dev/chart/templates/pdb.yaml @@ -0,0 +1,17 @@ +{{- if .Values.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "wiki.fullname" . }} + labels: + {{- include "wiki.labels" . | nindent 4 }} +spec: + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- else }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + {{- include "wiki.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/dev/chart/templates/postgresql/configmap.yaml b/dev/chart/templates/postgresql/configmap.yaml new file mode 100644 index 000000000..88cb9a15b --- /dev/null +++ b/dev/chart/templates/postgresql/configmap.yaml @@ -0,0 +1,26 @@ +{{- if .Values.postgresql.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "wiki.postgresql.fullname" . }}-initdb + labels: + {{- include "wiki.postgresql.labels" . | nindent 4 }} +data: + # The wiki's own role and database, owned by it so that it can create its schema — and nothing more. + # Values reach psql as variables, quoted by psql itself, so none of them is ever spliced into SQL. + 00-wiki.sh: | + #!/bin/bash + set -Eeo pipefail + psql -v ON_ERROR_STOP=1 --no-psqlrc --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" \ + --set app_user="$WIKI_DB_USER" \ + --set app_password="$WIKI_DB_PASSWORD" \ + --set app_db="$WIKI_DB_NAME" <<'EOSQL' + CREATE ROLE :"app_user" LOGIN PASSWORD :'app_password'; + CREATE DATABASE :"app_db" OWNER :"app_user"; + REVOKE ALL ON DATABASE :"app_db" FROM PUBLIC; + EOSQL + {{- range $file, $content := .Values.postgresql.initdbScripts }} + {{ $file }}: | + {{- $content | nindent 4 }} + {{- end }} +{{- end }} diff --git a/dev/chart/templates/postgresql/secret.yaml b/dev/chart/templates/postgresql/secret.yaml new file mode 100644 index 000000000..191136465 --- /dev/null +++ b/dev/chart/templates/postgresql/secret.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.postgresql.enabled (not .Values.postgresql.auth.existingSecret) }} +{{- $name := include "wiki.postgresql.fullname" . }} +{{- $keys := .Values.postgresql.auth.secretKeys }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $name }} + labels: + {{- include "wiki.postgresql.labels" . | nindent 4 }} + annotations: + # Deleting this with the release would lose the only record of a generated password, for a + # database whose claim outlives the release. + helm.sh/resource-policy: keep +type: Opaque +data: + {{ $keys.userPasswordKey }}: {{ include "wiki.secretValue" (list . $name $keys.userPasswordKey .Values.postgresql.auth.password) | quote }} + {{ $keys.adminPasswordKey }}: {{ include "wiki.secretValue" (list . $name $keys.adminPasswordKey .Values.postgresql.auth.postgresPassword) | quote }} +{{- end }} diff --git a/dev/chart/templates/postgresql/service.yaml b/dev/chart/templates/postgresql/service.yaml new file mode 100644 index 000000000..b1e0de639 --- /dev/null +++ b/dev/chart/templates/postgresql/service.yaml @@ -0,0 +1,44 @@ +{{- if .Values.postgresql.enabled }} +{{- $svc := .Values.postgresql.service }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "wiki.postgresql.fullname" . }} + labels: + {{- include "wiki.postgresql.labels" . | nindent 4 }} + {{- with $svc.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $svc.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ $svc.type }} + ports: + - name: postgresql + port: {{ $svc.port }} + targetPort: postgresql + protocol: TCP + selector: + {{- include "wiki.postgresql.selectorLabels" . | nindent 4 }} +--- +# The StatefulSet's governing service, which gives the pod its stable DNS name. +apiVersion: v1 +kind: Service +metadata: + name: {{ include "wiki.postgresql.fullname" . }}-hl + labels: + {{- include "wiki.postgresql.labels" . | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: postgresql + port: {{ $svc.port }} + targetPort: postgresql + protocol: TCP + selector: + {{- include "wiki.postgresql.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/dev/chart/templates/postgresql/statefulset.yaml b/dev/chart/templates/postgresql/statefulset.yaml new file mode 100644 index 000000000..8de4a636b --- /dev/null +++ b/dev/chart/templates/postgresql/statefulset.yaml @@ -0,0 +1,195 @@ +{{- if .Values.postgresql.enabled }} +{{- $pg := .Values.postgresql }} +{{- $secret := include "wiki.postgresql.secretName" . }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "wiki.postgresql.fullname" . }} + labels: + {{- include "wiki.postgresql.labels" . | nindent 4 }} +spec: + # One server. A replicated PostgreSQL is a job for an operator (CloudNativePG and the like), used + # through `externalDatabase`. + replicas: 1 + serviceName: {{ include "wiki.postgresql.fullname" . }}-hl + {{- with $pg.updateStrategy }} + updateStrategy: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if and $pg.persistence.enabled (not $pg.persistence.existingClaim) }} + persistentVolumeClaimRetentionPolicy: + {{- toYaml $pg.persistence.retentionPolicy | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "wiki.postgresql.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/initdb: {{ include (print $.Template.BasePath "/postgresql/configmap.yaml") . | sha256sum }} + {{- with $pg.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "wiki.postgresql.labels" . | nindent 8 }} + {{- with $pg.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: false + enableServiceLinks: false + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $pg.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + securityContext: + {{- toYaml $pg.podSecurityContext | nindent 8 }} + # The official image declares SIGINT as its stop signal, which the container runtime sends in + # place of SIGTERM: a fast shutdown, where SIGTERM would wait for every client to disconnect. + terminationGracePeriodSeconds: {{ $pg.terminationGracePeriodSeconds }} + containers: + - name: postgresql + image: {{ include "wiki.postgresql.image" . }} + imagePullPolicy: {{ $pg.image.pullPolicy }} + args: + - postgres + {{- range $key, $value := $pg.parameters }} + - -c + - {{ printf "%s=%v" $key $value | quote }} + {{- end }} + {{- range $pg.extraArgs }} + - {{ . | quote }} + {{- end }} + ports: + - name: postgresql + containerPort: 5432 + protocol: TCP + env: + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_DB + value: postgres + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $secret }} + key: {{ $pg.auth.secretKeys.adminPasswordKey }} + {{- with $pg.initdbArgs }} + - name: POSTGRES_INITDB_ARGS + value: {{ . | quote }} + {{- end }} + # Read by the first-run script that creates the wiki's role and database. + - name: WIKI_DB_USER + value: {{ $pg.auth.username | quote }} + - name: WIKI_DB_NAME + value: {{ $pg.auth.database | quote }} + - name: WIKI_DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $secret }} + key: {{ $pg.auth.secretKeys.userPasswordKey }} + {{- with $pg.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $pg.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $pg.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $pg.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + {{- toYaml $pg.securityContext | nindent 12 }} + {{- with $pg.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + # The parent of PGDATA rather than PGDATA itself, as the image expects from 18 on + # (/var/lib/postgresql/18/docker): the version is part of the path, and the data directory + # is never the root of the volume, where a lost+found would make initdb refuse it. + - name: data + mountPath: /var/lib/postgresql + - name: run + mountPath: /var/run/postgresql + - name: tmp + mountPath: /tmp + - name: initdb + mountPath: /docker-entrypoint-initdb.d + readOnly: true + {{- if $pg.shm.enabled }} + - name: shm + mountPath: /dev/shm + {{- end }} + {{- with $pg.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: run + emptyDir: {} + - name: tmp + emptyDir: {} + - name: initdb + configMap: + name: {{ include "wiki.postgresql.fullname" . }}-initdb + defaultMode: 0555 + {{- if $pg.shm.enabled }} + - name: shm + emptyDir: + medium: Memory + {{- with $pg.shm.sizeLimit }} + sizeLimit: {{ . }} + {{- end }} + {{- end }} + {{- if not $pg.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if $pg.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ $pg.persistence.existingClaim }} + {{- end }} + {{- with $pg.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $pg.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $pg.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $pg.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if and $pg.persistence.enabled (not $pg.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + labels: + {{- include "wiki.postgresql.selectorLabels" . | nindent 10 }} + {{- with $pg.persistence.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- toYaml $pg.persistence.accessModes | nindent 10 }} + {{- if $pg.persistence.storageClass }} + storageClassName: {{ ternary "" $pg.persistence.storageClass (eq $pg.persistence.storageClass "-") | quote }} + {{- end }} + resources: + requests: + storage: {{ $pg.persistence.size }} + {{- end }} +{{- end }} diff --git a/dev/chart/templates/pvc.yaml b/dev/chart/templates/pvc.yaml new file mode 100644 index 000000000..bd043e1cc --- /dev/null +++ b/dev/chart/templates/pvc.yaml @@ -0,0 +1,25 @@ +{{- $p := .Values.persistence }} +{{- if and $p.enabled (not $p.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "wiki.fullname" . }}-data + labels: + {{- include "wiki.labels" . | nindent 4 }} + annotations: + # Uninstalling the release leaves the claim behind, since it may be the only copy of a Local Disk + # or Git storage target. + helm.sh/resource-policy: keep + {{- with $p.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + {{- toYaml $p.accessModes | nindent 4 }} + {{- if $p.storageClass }} + storageClassName: {{ ternary "" $p.storageClass (eq $p.storageClass "-") | quote }} + {{- end }} + resources: + requests: + storage: {{ $p.size }} +{{- end }} diff --git a/dev/chart/templates/secret.yaml b/dev/chart/templates/secret.yaml new file mode 100644 index 000000000..11d193179 --- /dev/null +++ b/dev/chart/templates/secret.yaml @@ -0,0 +1,32 @@ +{{- $ext := .Values.externalDatabase }} +{{- $useUrl := include "wiki.db.useUrl" . }} +{{- $dbUrl := and $useUrl $ext.connectionString.value }} +{{- $dbPassword := and (not .Values.postgresql.enabled) (not $useUrl) (not $ext.parameters.existingSecret) }} +{{- $adminPassword := and .Values.admin.password (not .Values.admin.existingSecret) }} +{{- if or $dbUrl $dbPassword }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "wiki.fullname" . }}-db + labels: + {{- include "wiki.labels" . | nindent 4 }} +type: Opaque +data: + {{- if $dbUrl }} + url: {{ $ext.connectionString.value | b64enc | quote }} + {{- else }} + password: {{ $ext.parameters.password | b64enc | quote }} + {{- end }} +{{- end }} +{{- if $adminPassword }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "wiki.fullname" . }}-admin + labels: + {{- include "wiki.labels" . | nindent 4 }} +type: Opaque +data: + password: {{ .Values.admin.password | b64enc | quote }} +{{- end }} diff --git a/dev/chart/templates/service.yaml b/dev/chart/templates/service.yaml new file mode 100644 index 000000000..732dc1f0a --- /dev/null +++ b/dev/chart/templates/service.yaml @@ -0,0 +1,39 @@ +{{- $svc := .Values.service }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "wiki.fullname" . }} + labels: + {{- include "wiki.labels" . | nindent 4 }} + {{- with $svc.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $svc.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ $svc.type }} + {{- if and (eq $svc.type "LoadBalancer") $svc.loadBalancerClass }} + loadBalancerClass: {{ $svc.loadBalancerClass }} + {{- end }} + {{- if and (eq $svc.type "LoadBalancer") $svc.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml $svc.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and (has $svc.type (list "NodePort" "LoadBalancer")) $svc.externalTrafficPolicy }} + externalTrafficPolicy: {{ $svc.externalTrafficPolicy }} + {{- end }} + {{- with $svc.sessionAffinity }} + sessionAffinity: {{ . }} + {{- end }} + ports: + - name: http + port: {{ $svc.port }} + targetPort: http + protocol: TCP + {{- if and (has $svc.type (list "NodePort" "LoadBalancer")) $svc.nodePort }} + nodePort: {{ $svc.nodePort }} + {{- end }} + selector: + {{- include "wiki.selectorLabels" . | nindent 4 }} diff --git a/dev/helm/templates/serviceaccount.yaml b/dev/chart/templates/serviceaccount.yaml similarity index 68% rename from dev/helm/templates/serviceaccount.yaml rename to dev/chart/templates/serviceaccount.yaml index 7f6c89173..c48281caa 100644 --- a/dev/helm/templates/serviceaccount.yaml +++ b/dev/chart/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.serviceAccount.create -}} +{{- if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: @@ -9,4 +9,5 @@ metadata: annotations: {{- toYaml . | nindent 4 }} {{- end }} -{{- end -}} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/dev/chart/templates/tests/test-connection.yaml b/dev/chart/templates/tests/test-connection.yaml new file mode 100644 index 000000000..7f8cd2c1f --- /dev/null +++ b/dev/chart/templates/tests/test-connection.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{ include "wiki.fullname" . }}-test + labels: + {{- include "wiki.commonLabels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +spec: + restartPolicy: Never + automountServiceAccountToken: false + enableServiceLinks: false + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 4 }} + containers: + # Asked through the Service, so that it tests the selector as well as the wiki. + - name: ready + image: {{ include "wiki.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - node + - -e + - | + const url = 'http://{{ include "wiki.fullname" . }}:{{ .Values.service.port }}/_ready' + fetch(url).then((res) => { + console.info(`${url} → ${res.status}`) + process.exit(res.ok ? 0 : 1) + }, (err) => { + console.error(`${url} → ${err.cause?.code ?? err.message}`) + process.exit(1) + }) + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} diff --git a/dev/chart/values.yaml b/dev/chart/values.yaml new file mode 100644 index 000000000..3f8a123f1 --- /dev/null +++ b/dev/chart/values.yaml @@ -0,0 +1,462 @@ +# Wiki.js 3.x +# +# Every key below is optional. With none of them set, the chart runs one wiki replica against a +# bundled PostgreSQL 18, reachable inside the cluster only — turn on `ingress` or `httpRoute` to +# expose it. + +nameOverride: '' +fullnameOverride: '' + +image: + repository: ghcr.io/requarks/wiki + # Defaults to the chart's appVersion. + tag: '' + # Pins the image by digest (`sha256:…`), taking precedence over the tag. + digest: '' + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +# Replicas share the database and find each other through it (LISTEN/NOTIFY), collaborative editing +# included, so no sticky sessions are needed. Anything kept on the data volume is per replica unless +# `persistence` points them all at one ReadWriteMany claim. +replicaCount: 1 + +revisionHistoryLimit: 10 + +# Deployment strategy. Leave empty for the default RollingUpdate; use `{ type: Recreate }` with a +# single replica on a ReadWriteOnce `persistence` claim, which a second pod on another node could +# not attach while the old one still holds it. +strategy: {} + +# --------------------------------------------------------------------------------------------------- +# Wiki.js configuration +# --------------------------------------------------------------------------------------------------- + +# Rendered as the instance's config.yml. Any key the file accepts can be added here (see +# config.sample.yml), except the ones the chart owns: `port`, `bindIP` and `dataPath` follow the +# container, and `db` is built from `postgresql` / `externalDatabase` below. +# +# Everything else about the wiki is configured in its administration area and kept in the database. +config: + # error, warn, info or debug + logLevel: info + # default or json + logFormat: default + # Stops every outbound request the wiki would otherwise make (Iconify, update checks, …). + offline: false + # Largest API request body accepted, in bytes. File uploads are not bound by it. + bodyParserLimit: 5242880 + icons: + apiUrl: https://api.iconify.design + scheduler: + # Most worker threads for CPU-heavy background jobs. `auto` is one fewer than the CPUs the pod + # may use: its `limits.cpu`, rounded down, or every core of the node when no limit is set. + workers: auto + +# The administrator account created the first time the wiki starts against an empty database. Never +# read again afterwards. Left empty, the account is admin@example.com / 12345678 and must change its +# password on first login. +admin: + email: '' + password: '' + # A Secret holding the password, in place of `password`. + existingSecret: '' + existingSecretPasswordKey: password + +# Waits for the database port to open before the wiki starts. The wiki itself gives up after ~30s of +# failed connections, which a bundled PostgreSQL initialising its data directory can outlast. +waitForDatabase: + enabled: true + resources: {} + +extraEnv: [] +# - name: FOO +# value: bar + +extraEnvFrom: [] +# - secretRef: +# name: wiki-extra-env + +# --------------------------------------------------------------------------------------------------- +# Pod +# --------------------------------------------------------------------------------------------------- + +serviceAccount: + create: true + # Generated from the release name when empty. + name: '' + annotations: {} + # The wiki never talks to the Kubernetes API, so the pod gets no token unless asked for. + automount: false + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + seccompProfile: + type: RuntimeDefault + +# `readOnlyRootFilesystem` holds everything except installing an extension from Admin → Extensions, +# which runs `npm install` inside the image. The image already carries Puppeteer, the extension that +# needs it most; anything else would be added by building an image FROM this one. +securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + privileged: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + +resources: {} +# requests: +# cpu: 250m +# memory: 512Mi +# limits: +# memory: 2Gi + +# The probes are merged with what is given here, so a single field can be overridden. To swap the +# handler, null the default one out: `livenessProbe: { httpGet: null, exec: { … } }`. +# +# `/_live` answers only once the HTTP server is listening, which is after migrations have run — the +# startup probe is what covers that, and allows 5 minutes by default. +startupProbe: + httpGet: + path: /_live + port: http + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 60 + +livenessProbe: + httpGet: + path: /_live + port: http + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + +# `/_ready` turns 503 as soon as a replica starts shutting down, while it goes on serving the requests +# still reaching it until the endpoint removal has propagated. +readinessProbe: + httpGet: + path: /_ready + port: http + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + +# The pause before SIGTERM, so a replica being replaced is out of every load balancer before it stops +# accepting connections. +lifecycle: + preStop: + sleep: + seconds: 5 + +terminationGracePeriodSeconds: 30 + +priorityClassName: '' +nodeSelector: {} +tolerations: [] +affinity: {} +topologySpreadConstraints: [] + +# Added to the pod, next to the chart's own (config, data, tmp, home). +volumes: [] +# - name: git-known-hosts +# configMap: +# name: git-known-hosts + +# Added to the wiki container. +volumeMounts: [] +# - name: git-known-hosts +# mountPath: /home/node/.ssh/known_hosts +# subPath: known_hosts +# readOnly: true + +# The wiki's data directory, /wiki/data. Holds the caches (icons, served files, blocks), which rebuild +# themselves from the database, plus the default locations of the Local Disk (`data/content`) and Git +# (`data/repo`) storage targets. An emptyDir is enough unless one of those two is in use. +persistence: + enabled: false + # Use an existing claim instead of creating one. + existingClaim: '' + # `-` for the cluster's default class, empty to leave it unset. + storageClass: '' + # ReadWriteMany is what lets several replicas share one Local Disk or Git storage target. + accessModes: + - ReadWriteOnce + size: 10Gi + annotations: {} + # Caps the emptyDir used while persistence is off. + sizeLimit: '' + +podDisruptionBudget: + enabled: false + minAvailable: 1 + # Used instead of `minAvailable` when set. + maxUnavailable: '' + +# --------------------------------------------------------------------------------------------------- +# Networking +# --------------------------------------------------------------------------------------------------- + +service: + type: ClusterIP + port: 80 + # For NodePort / LoadBalancer. + nodePort: '' + annotations: {} + labels: {} + loadBalancerClass: '' + loadBalancerSourceRanges: [] + # Cluster or Local, for NodePort / LoadBalancer. + externalTrafficPolicy: '' + sessionAffinity: '' + +# Behind either of these, turn on Admin → Security → Trust Proxy so the wiki sees the visitor's +# address rather than the proxy's. + +# Gateway API. The recommended way in; it needs a Gateway to attach to. +httpRoute: + enabled: false + annotations: {} + labels: {} + parentRefs: + - name: gateway + # namespace: gateway-system + # sectionName: https + hostnames: + - wiki.example.com + # HTTPRoute rules, passed through as given (matches, filters, timeouts, …) except for `backendRefs`, + # which the chart fills in with the wiki's Service. + rules: + - matches: + - path: + type: PathPrefix + value: / + # filters: [] + # timeouts: + # request: 300s + +# networking.k8s.io/v1 Ingress. Most controllers cap request bodies (ingress-nginx at 1m by default), +# which is also the ceiling for uploads — raise it with the controller's own annotation. +ingress: + enabled: false + className: '' + annotations: {} + labels: {} + hosts: + - host: wiki.example.com + paths: + - path: / + pathType: Prefix + tls: [] + # - secretName: wiki-tls + # hosts: + # - wiki.example.com + +# --------------------------------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------------------------------- + +# A PostgreSQL server of the chart's own: one StatefulSet replica, the official image. Turn it off to +# use `externalDatabase` instead. +postgresql: + enabled: true + + image: + repository: docker.io/library/postgres + # Pin a minor release (e.g. `18.4`) for reproducible upgrades. Moving to a new MAJOR needs a + # dump and restore, as it would anywhere — the data directory is per major. + tag: '18' + digest: '' + pullPolicy: IfNotPresent + + # The wiki connects as `username`, an ordinary role owning `database`; the `postgres` superuser is + # kept for administration and never handed to the wiki. Both passwords are generated and kept across + # upgrades when left empty — except under a tool that renders without cluster access (Argo CD), which + # would generate new ones on every sync: set them, or use `existingSecret`, there. + # + # All of this is applied when the data directory is first initialised. Changing it afterwards + # changes what the wiki is told, not the database: alter the role by hand to match. + auth: + username: wiki + database: wiki + password: '' + postgresPassword: '' + existingSecret: '' + secretKeys: + userPasswordKey: password + adminPasswordKey: postgres-password + + # The schema the wiki creates its tables in. + schema: wiki + + # Server settings, passed as `-c key=value`. + parameters: {} + # max_connections: 200 + # shared_buffers: 256MB + + extraArgs: [] + + # Extra arguments for `initdb`, used on first start only. + initdbArgs: '' + + # Scripts run once, after the data directory is initialised and the wiki's role and database exist, + # in file name order. `.sh`, `.sql` and `.sql.gz` are understood. + initdbScripts: {} + # 10-extensions.sql: | + # \connect wiki + # CREATE EXTENSION IF NOT EXISTS pg_trgm; + + extraEnv: [] + + podAnnotations: {} + podLabels: {} + + # 999 is the `postgres` user of the official Debian image (70 on -alpine tags). + podSecurityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + # Anything else lets the kubelet re-own the data directory on every start, with group write, + # which PostgreSQL refuses to start on. + fsGroupChangePolicy: OnRootMismatch + seccompProfile: + type: RuntimeDefault + + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + privileged: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + + resources: {} + # requests: + # cpu: 250m + # memory: 256Mi + # limits: + # memory: 1Gi + + startupProbe: + exec: + command: ['pg_isready', '-h', '127.0.0.1', '-p', '5432'] + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 60 + livenessProbe: + exec: + command: ['pg_isready', '-h', '127.0.0.1', '-p', '5432'] + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + readinessProbe: + exec: + command: ['pg_isready', '-h', '127.0.0.1', '-p', '5432'] + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + + # A shutdown checkpoint on a busy server takes a while; SIGKILL before it finishes means crash + # recovery on the next start. + terminationGracePeriodSeconds: 60 + + updateStrategy: + type: RollingUpdate + + priorityClassName: '' + nodeSelector: {} + tolerations: [] + affinity: {} + + volumes: [] + volumeMounts: [] + + persistence: + enabled: true + existingClaim: '' + # `-` for the cluster's default class, empty to leave it unset. + storageClass: '' + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + # What happens to the claim when the StatefulSet is deleted or scaled down. + retentionPolicy: + whenDeleted: Retain + whenScaled: Retain + + # /dev/shm, which parallel queries use for shared memory; a container's default is 64 MiB. Counted + # against the pod's memory. + shm: + enabled: true + sizeLimit: 256Mi + + service: + type: ClusterIP + port: 5432 + annotations: {} + labels: {} + +# An existing PostgreSQL 16+ server, used when `postgresql.enabled` is false. The user needs to own the +# database, or at least be able to create a schema in it. +# +# Say where it is in ONE of two ways — `connectionString` or `parameters` — and leave the other empty. +# `schema` and `ssl` apply to both. +externalDatabase: + # ── Option 1: a connection string ─────────────────────────────────────────────────────────────── + # `postgresql://user:password@host:5432/database`, with special characters in the password + # percent-encoded as in any URL. Give it inline or, better, as a Secret: CloudNativePG's + # `-app` Secret carries one under `uri`. + # + # TLS parameters in the string (`sslmode`, `sslrootcert`, …) replace everything under `ssl` below. + # CloudNativePG's has none, so pair it with `ssl` and the `-ca` Secret to verify the server. + connectionString: + value: '' + existingSecret: '' + existingSecretKey: uri + + # ── Option 2: individual parameters ───────────────────────────────────────────────────────────── + parameters: + host: '' + port: 5432 + database: wiki + user: wiki + password: '' + # A Secret holding the password, in place of `password`. + existingSecret: '' + existingSecretPasswordKey: password + + # ── Either way ────────────────────────────────────────────────────────────────────────────────── + # The schema the wiki creates its tables in. + schema: wiki + ssl: + enabled: false + # Verify the server's certificate. Only for testing when off. + rejectUnauthorized: true + # A Secret with the CA to verify against and, for client certificate authentication, a + # certificate and key. Without one, the system CAs are used. + existingSecret: '' + caKey: ca.crt + # Both empty unless the server asks for a client certificate. + certKey: '' + keyKey: '' diff --git a/dev/helm/.helmignore b/dev/helm/.helmignore deleted file mode 100644 index 0e8a0eb36..000000000 --- a/dev/helm/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/dev/helm/Chart.lock b/dev/helm/Chart.lock deleted file mode 100644 index d383d6e44..000000000 --- a/dev/helm/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 8.10.14 -digest: sha256:db7c1e0bc9ec0ed45520521bd76bb390d04711fd0f04affaadafa1dc498ce68b -generated: "2020-07-21T20:34:41.41180748-04:00" diff --git a/dev/helm/Chart.yaml b/dev/helm/Chart.yaml deleted file mode 100644 index 0b8d593a1..000000000 --- a/dev/helm/Chart.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: v2 -name: wiki -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -version: 2.2.0 -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. -AppVersion: latest -description: The most powerful and extensible open source Wiki software. -keywords: - - wiki - - documentation - - knowledge base - - docs - - reference - - editor -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application -dependencies: - - name: postgresql - version: 8.10.14 - repository: https://charts.bitnami.com/bitnami - condition: postgresql.enabled -home: https://wiki.js.org -icon: https://cdn.js.wiki/images/wikijs-butterfly.svg -sources: - - https://github.com/Requarks/wiki -maintainers: - - name: Nicolas Giard - email: github@ngpixel.com - url: https://github.com/NGPixel - - name: James Greenhill - email: james@fuziontech.net - url: https://github.com/fuziontech -engine: gotpl diff --git a/dev/helm/README.md b/dev/helm/README.md deleted file mode 100644 index ae95ab45d..000000000 --- a/dev/helm/README.md +++ /dev/null @@ -1,177 +0,0 @@ -
- -Wiki.js - -[![Release](https://img.shields.io/github/release/Requarks/wiki.svg?style=flat&maxAge=3600)](https://github.com/Requarks/wiki/releases) -[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg?style=flat)](https://github.com/requarks/wiki/blob/master/LICENSE) -[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-green.svg?style=flat&logo=javascript&logoColor=white)](http://standardjs.com/) -[![Downloads](https://img.shields.io/github/downloads/Requarks/wiki/total.svg?style=flat&logo=github)](https://github.com/Requarks/wiki/releases) -[![Docker Pulls](https://img.shields.io/docker/pulls/requarks/wiki.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/requarks/wiki/) -[![Build + Publish](https://github.com/Requarks/wiki/actions/workflows/build.yml/badge.svg)](https://github.com/Requarks/wiki/actions/workflows/build.yml) -[![Huntr](https://img.shields.io/badge/security%20bounty-disclose-brightgreen.svg?style=flat&logo=cachet&logoColor=white)](https://huntr.dev/bounties/disclose) -[![GitHub Sponsors](https://img.shields.io/github/sponsors/ngpixel?logo=github&color=ea4aaa)](https://github.com/users/NGPixel/sponsorship) -[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/wikijs?label=backers&color=218bff&logo=opencollective&logoColor=white)](https://opencollective.com/wikijs) -[![Chat on Slack](https://img.shields.io/badge/slack-requarks-CC2B5E.svg?style=flat&logo=slack)](https://wiki.requarks.io/slack) -[![Twitter Follow](https://img.shields.io/badge/follow-%40requarks-blue.svg?style=flat&logo=twitter)](https://twitter.com/requarks) -[![Reddit](https://img.shields.io/badge/reddit-%2Fr%2Fwikijs-orange?logo=reddit&logoColor=white)](https://www.reddit.com/r/wikijs/) -[![Subscribe to Newsletter](https://img.shields.io/badge/newsletter-subscribe-yellow.svg?style=flat&logo=mailchimp)](https://blog.js.wiki/subscribe) - -##### A modern, lightweight and powerful wiki app built on NodeJS - -
- -- **[Official Website](https://wiki.js.org/)** -- **[Documentation](https://docs.requarks.io/)** - -

Donate

- -
- -Wiki.js is an open source project that has been made possible due to the generous contributions by community [backers](https://wiki.js.org/about). If you are interested in supporting this project, please consider [becoming a sponsor](https://github.com/users/NGPixel/sponsorship), [becoming a patron](https://www.patreon.com/requarks), donating to our [OpenCollective](https://opencollective.com/wikijs), via [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url) or via Ethereum (`0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5`). - - [![Become a Sponsor](https://img.shields.io/badge/donate-github-ea4aaa.svg?style=popout&logo=github)](https://github.com/users/NGPixel/sponsorship) - [![Become a Patron](https://img.shields.io/badge/donate-patreon-orange.svg?style=popout&logo=patreon)](https://www.patreon.com/requarks) - [![Donate on OpenCollective](https://img.shields.io/badge/donate-open%20collective-blue.svg?style=popout&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNTZweCIgaGVpZ2h0PSIyNTZweCIgdmlld0JveD0iMCAwIDI1NiAyNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiPjxnPjxwYXRoIGQ9Ik0yMDkuNzY1MTQ0LDEyOC4xNDk5NzkgQzIwOS43NjUxNDQsMTQ0LjE2MzMgMjA0Ljg2NDM4MSwxNTkuNDg5ODkgMTk2LjQ5ODc0NywxNzIuNzI1MDcyIEwyMjkuOTQ1Njc1LDIwNi4xNzE5OTkgQzI0Ni42ODIxMDUsMTgzLjg1Njc1OSAyNTUuNzI5MzA3LDE1Ni43MTUxNTIgMjU1LjcyOTMwNywxMjguODIxMTAyIEMyNTUuNzI5MzA3LDk5LjU1Njk5MTcgMjQ1Ljk3NDYwMyw3My4wNzEwMjA3IDIyOS4yNTg5NDQsNTEuNDg1ODEyOCBMMTk2LjQ4MzE0LDg0LjIxNDc5NCBDMjA1LjEyMjU2MSw5Ny4yMjI0NjgzIDIwOS43MzY5MDcsMTEyLjQ4NzgxIDIwOS43NDk1MzcsMTI4LjEwMzE1NiBMMjA5Ljc2NTE0NCwxMjguMTQ5OTc5IFoiIGZpbGw9IiNCOEQzRjQiPjwvcGF0aD48cGF0aCBkPSJNMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEM4Mi4xNDYwODcyLDIxMC4yNjg5NTggNDUuMzg3NTA5NCwxNzMuNTE3MzU4IDQ1LjI5MzAzOTMsMTI4LjE0OTk3OSBDNDUuMzYxNzUwMiw4Mi43NjQzMTM4IDgyLjEyNzg0ODcsNDUuOTg0MjU3IDEyNy41MTM0ODQsNDUuODk4MzE4NiBDMTQ0LjI0NDc1Miw0NS44OTgzMTg2IDE1OS41NzEzNDIsNTAuNzk5MDgxNyAxNzIuMTE5NzkyLDU5LjE2NDcxNTQgTDIwNC44NjQzODEsMjYuMzg4OTExNiBDMTgyLjU0MzY1LDkuNjY2NjUxMjkgMTU1LjQwMzQyOSwwLjYzMDg2MzI5OCAxMjcuNTEzNDg0LDAuNjM2NDk0NDAzIEM1Ny4xMjM1NDM3LDAuNjM2NDk0NDAzIDAsNTcuNzYwMDM4MSAwLDEyOC4xNDk5NzkgQzAsMTk4LjUwODcwNCA1Ny4xMjM1NDM3LDI1NS42NjM0NjMgMTI3LjUxMzQ4NCwyNTUuNjYzNDYzIEMxNTUuNTM3MzUyLDI1NS43NDA4NzYgMTgyLjc3NTk4OSwyNDYuNDA4NTEgMjA0Ljg2NDM4MSwyMjkuMTYxODg0IEwxNzEuNDE3NDU0LDE5NS43MzA1NjQgQzE1OS41NTU3MzQsMjA1LjQ4NTI2OCAxNDQuMjYwMzU5LDIxMC4zNTQ4MTYgMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEwxMjcuNTEzNDg0LDIxMC4zNTQ4MTYgWiIgZmlsbD0iIzdGQURGMiI+PC9wYXRoPjwvZz48L3N2Zz4=)](https://opencollective.com/wikijs) - [![Donate via Paypal](https://img.shields.io/badge/donate-paypal-blue.svg?style=popout&logo=paypal)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url) - [![Donate via Ethereum](https://img.shields.io/badge/donate-ethereum-999.svg?style=popout&logo=ethereum&logoColor=CCC)](https://etherscan.io/address/0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5) - [![Donate via Bitcoin](https://img.shields.io/badge/donate-bitcoin-ff9900.svg?style=popout&logo=bitcoin&logoColor=CCC)](https://checkout.opennode.com/p/2553c612-f863-4407-82b3-1a7685268747) - [![Buy a T-Shirt](https://img.shields.io/badge/buy-t--shirts-teal.svg?style=popout&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4Igp3aWR0aD0iMjQiIGhlaWdodD0iMjQiCnZpZXdCb3g9IjAgMCAxOTIgMTkyIgpzdHlsZT0iIGZpbGw6IzAwMDAwMDsiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIj48L3BhdGg+PGcgZmlsbD0iIzFhYmM5YyI+PGcgaWQ9InN1cmZhY2UxIj48cGF0aCBkPSJNOTYsMGMtMTUuMjE4NzUsMCAtMjQuNjg3NSwzLjY1NjI1IC0yNS41LDRsLTIyLjUsNy4yNWMtMTAuNDA2MjUsMy4xODc1IC0xOS4wOTM3NSw5LjQzNzUgLTI1LjUsMTguMjVsLTIyLjUsNDIuNWwyNy4yNSwxNi43NWwxMi43NSwtMjR2MTE5LjI1YzAsNC40MDYyNSAyNS4wNjI1LDggNTYsOGMzMC45Mzc1LDAgNTYsLTMuNTkzNzUgNTYsLTh2LTExOS4yNWwxMi43NSwyNGwyNy4yNSwtMTYuNzVsLTIyLjUsLTQyLjVjLTYuNDA2MjUsLTguODEyNSAtMTUuMTU2MjUsLTE1LjA2MjUgLTI0Ljc1LC0xOC4yNWwtMjIuMjUsLTcuMjVjLTAuMTg3NSwwIC0xLjAzMTI1LDEuMzEyNSAtMiwyLjc1bDEuMjUsLTIuNWMwLDAgLTkuODQzNzUsLTQuMjUgLTI1Ljc1LC00LjI1ek05Niw4YzExLjQwNjI1LDAgMTguNDM3NSwyLjI1IDIxLDMuMjVjLTQuNDY4NzUsNS43NSAtMTEuNDA2MjUsMTIuNzUgLTIxLDEyLjc1Yy05LjQwNjI1LDAgLTE2LjQwNjI1LC03LjA2MjUgLTIwLjc1LC0xMi43NWMyLjg3NSwtMS4wNjI1IDkuODc1LC0zLjI1IDIwLjc1LC0zLjI1eiI+PC9wYXRoPjwvZz48L2c+PC9nPjwvc3ZnPg==)](https://wikijs.threadless.com) - -
- -## Introduction - -This chart bootstraps a Wiki.js deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -It also optionally packages the [PostgreSQL](https://github.com/kubernetes/charts/tree/master/stable/postgresql) as the database but you are free to bring your own. - -## Prerequisites - -- PV provisioner support in the underlying infrastructure (with persistence storage enabled) if you want data persistance - -## Adding the Wiki.js Helm Repository - -```console -$ helm repo add requarks https://charts.js.wiki -``` - -## Installing the Chart - -To install the chart with the release name `my-release` run the following: - -### Using Helm 3: -```console -$ helm install my-release requarks/wiki -``` -### Using Helm 2: -```console -$ helm install --name my-release requarks/wiki -``` - -The command deploys Wiki.js on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` deployment: - -```console -$ helm delete my-release -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -> **Warning**: Persistant Volume Claims for the database are not deleted automatically. They need to be manually deleted - -```console -$ kubectl delete pvc/data-wiki-postgresql-0 -``` - -## Configuration - -The following table lists the configurable parameters of the Wiki.js chart and their default values. - -| Parameter | Description | Default | -| ------------------------------- | ------------------------------- | ---------------------------------------------------------- | -| `image.repository` | Wiki.js image | `requarks/wiki` | -| `image.tag` | Wiki.js image tag | `latest` | -| `imagePullPolicy` | Image pull policy | `IfNotPresent` | -| `replicacount` | Amount of wiki.js service pods to run | `1` | -| `revisionHistoryLimit` | Total amount of revision history points | `10` | -| `resources.limits` | wiki.js service resource limits | `nil` | -| `resources.requests` | wiki.js service resource requests | `nil` | -| `nodeSelector` | Node labels for wiki.js pod assignment | `{}` | -| `affinity` | Affinity settings for wiki.js pod assignment | `{}` | -| `schedulerName` | Name of an alternate scheduler for wiki.js pod | `nil` | -| `tolerations` | Toleration labels for wiki.jsk pod assignment | `[]` | -| `volumeMounts` | Volume mounts for Wiki.js container | `[]` | -| `volumes` | Volumes for Wiki.js Pod | `[]` | -| `ingress.enabled` | Enable ingress controller resource | `false` | -| `ingress.className` | Ingress class name | `""` | -| `ingress.annotations` | Ingress annotations | `{}` | -| `ingress.hosts` | List of ingress rules | `[{"host": "wiki.local", "paths": ["/"]}]` | -| `ingress.tls` | Ingress TLS configuration | `[]` | -| `sideload.enabled` | Enable sideloading of locale files from git | `false` | -| `sideload.repoURL` | Git repository URL containing locale files | `https://github.com/Requarks/wiki-localization` | -| `sideload.env` | Environment variables for sideload Container | `{}` | -| `postgresql.enabled` | Deploy postgres server (see below) | `true` | -| `postgresql.postgresqlDatabase` | Postgres database name | `wiki` | -| `postgresql.postgresqlUser` | Postgres username | `postgres` | -| `postgresql.postgresqlHost` | External postgres host | `nil` | -| `postgresql.postgresqlPassword` | External postgres password | `nil` | -| `postgresql.existingSecret` | Provide an existing `Secret` for postgres | `nil` | -| `postgresql.existingSecretKey` | The postgres password key in the existing `Secret` | `postgresql-password` | -| `postgresql.postgresqlPort` | External postgres port | `5432` | -| `postgresql.ssl` | Enable external postgres SSL connection | `false` | -| `postgresql.ca` | Certificate of Authority content for postgres | `nil` | -| `postgresql.persistence.enabled` | Enable postgres persistence using PVC | `true` | -| `postgresql.persistence.existingClaim` | Provide an existing `PersistentVolumeClaim` for postgres | `nil` | -| `postgresql.persistence.storageClass` | Postgres PVC Storage Class (example: `nfs`) | `nil` | -| `postgresql.persistence.size` | Postgers PVC Storage Request | `8Gi` | - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -$ helm install --name my-release \ - --set postgresql.persistence.enabled=false \ - requarks/wiki -``` - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -$ helm install --name my-release -f values.yaml requarks/wiki -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - -## PostgresSQL - -By default, PostgreSQL is installed as part of the chart. - -### Using an external PostgreSQL server - -To use an external PostgreSQL server, set `postgresql.enabled` to `false` and then set `postgresql.postgresqlHost` and `postgresql.postgresqlPassword`. To use an existing `Secret`, set `postgresql.existingSecret`. The other options (`postgresql.postgresqlDatabase`, `postgresql.postgresqlUser`, `postgresql.postgresqlPort` and `postgresql.existingSecretKey`) may also want changing from their default values. - -To use an SSL connection you can set `postgresql.ssl` to `true` and if needed the path to a Certificate of Authority can be set using `postgresql.ca` to `/path/to/ca`. Default `postgresql.ssl` value is `false`. - -If `postgresql.existingSecret` is not specified, you also need to add the following Helm template to your deployment in order to create the postgresql `Secret`: - -```yaml -kind: Secret -apiVersion: v1 -metadata: - name: {{ template "wiki.postgresql.secret" . }} -data: - {{ template "wiki.postgresql.secretKey" . }}: "{{ .Values.postgresql.postgresqlPassword | b64enc }}" -``` - -## Persistence - -Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. -See the [Configuration](#configuration) section to configure the PVC or to disable persistence. - -## Ingress - -This chart provides support for Ingress resource. If you have an available Ingress Controller such as Nginx or Traefik you maybe want to set `ingress.enabled` to true and add `ingress.hosts` for the URL. Then, you should be able to access the installation using that address. diff --git a/dev/helm/charts/postgresql-6.5.0.tgz b/dev/helm/charts/postgresql-6.5.0.tgz deleted file mode 100644 index acc79962ce8f45da1a26062d7d64520e3e065af8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23426 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 LD}N+mWTgK9>s^5l diff --git a/dev/helm/templates/NOTES.txt b/dev/helm/templates/NOTES.txt deleted file mode 100644 index 5199428e9..000000000 --- a/dev/helm/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "wiki.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "wiki.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "wiki.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "wiki.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/dev/helm/templates/_helpers.tpl b/dev/helm/templates/_helpers.tpl deleted file mode 100644 index 193ceab78..000000000 --- a/dev/helm/templates/_helpers.tpl +++ /dev/null @@ -1,108 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "wiki.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "wiki.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "wiki.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "wiki.labels" -}} -helm.sh/chart: {{ include "wiki.chart" . }} -{{ include "wiki.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "wiki.selectorLabels" -}} -app.kubernetes.io/name: {{ include "wiki.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "wiki.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "wiki.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "wiki.postgresql.fullname" -}} -{{- if .Values.postgresql.fullnameOverride -}} -{{- .Values.postgresql.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{ printf "%s-%s" .Release.Name "postgresql"}} -{{- end -}} -{{- end -}} - -{{/* -Set postgres host -*/}} -{{- define "wiki.postgresql.host" -}} -{{- if .Values.postgresql.enabled -}} -{{- template "wiki.postgresql.fullname" . -}} -{{- else -}} -{{- .Values.postgresql.postgresqlHost | quote -}} -{{- end -}} -{{- end -}} - -{{/* -Set postgres secret -*/}} -{{- define "wiki.postgresql.secret" -}} -{{- if .Values.postgresql.enabled -}} -{{- template "wiki.postgresql.fullname" . -}} -{{- else -}} -{{- template "wiki.fullname" . -}} -{{- end -}} -{{- end -}} - -{{/* -Set postgres secretKey -*/}} -{{- define "wiki.postgresql.secretKey" -}} -{{- if .Values.postgresql.enabled -}} -"postgresql-password" -{{- else -}} -{{- default "postgresql-password" .Values.postgresql.existingSecretKey | quote -}} -{{- end -}} -{{- end -}} diff --git a/dev/helm/templates/deployment.yaml b/dev/helm/templates/deployment.yaml deleted file mode 100644 index 24910f2b5..000000000 --- a/dev/helm/templates/deployment.yaml +++ /dev/null @@ -1,96 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "wiki.fullname" . }} - labels: - {{- include "wiki.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} - selector: - matchLabels: - {{- include "wiki.selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "wiki.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "wiki.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - {{- if .Values.sideload.enabled }} - initContainers: - - name: {{ .Chart.Name }}-sideload - image: "{{ .Values.image.repository }}:{{ default "latest" .Values.image.tag }}" - imagePullPolicy: {{ default "IfNotPresent" .Values.image.imagePullPolicy }} - env: - {{- toYaml .Values.sideload.env | nindent 12 }} - command: [ "sh", "-c" ] - args: [ "mkdir -p /wiki/data/sideload && git clone --depth=1 {{ .Values.sideload.repoURL }} /wiki/data/sideload/" ] - {{- end }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ default "latest" .Values.image.tag }}" - imagePullPolicy: {{ default "IfNotPresent" .Values.image.imagePullPolicy }} - env: - - name: DB_TYPE - value: postgres - - name: DB_HOST - value: {{ template "wiki.postgresql.host" . }} - - name: DB_PORT - value: "{{ default "5432" .Values.postgresql.postgresqlPort }}" - - name: DB_NAME - value: {{ default "wiki" .Values.postgresql.postgresqlDatabase }} - - name: DB_USER - value: {{ default "wiki" .Values.postgresql.postgresqlUser }} - - name: DB_SSL - value: "{{ default "false" .Values.postgresql.ssl }}" - - name: DB_SSL_CA - value: "{{ default "" .Values.postgresql.ca }}" - - name: DB_PASS - valueFrom: - secretKeyRef: - {{- if .Values.postgresql.existingSecret }} - name: {{ .Values.postgresql.existingSecret }} - {{- else }} - name: {{ template "wiki.postgresql.secret" . }} - {{- end }} - key: {{ template "wiki.postgresql.secretKey" . }} - - name: HA_ACTIVE - value: {{ .Values.replicaCount | int | le 2 | quote }} - {{- with .Values.volumeMounts }} - volumeMounts: - {{- toYaml . | nindent 12 }} - {{- end }} - ports: - - name: http - containerPort: 3000 - protocol: TCP - livenessProbe: - {{- toYaml .Values.livenessProbe | nindent 12 }} - readinessProbe: - {{- toYaml .Values.readinessProbe | nindent 12 }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.volumes }} - volumes: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/dev/helm/templates/ingress.yaml b/dev/helm/templates/ingress.yaml deleted file mode 100644 index 8ac1c9390..000000000 --- a/dev/helm/templates/ingress.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} - {{- $fullName := include "wiki.fullname" . -}} - {{- $svcPort := .Values.service.port -}} - {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} - {{- end }} - {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 - {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 - {{- else -}} -apiVersion: extensions/v1beta1 - {{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "wiki.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} diff --git a/dev/helm/templates/service.yaml b/dev/helm/templates/service.yaml deleted file mode 100644 index 5b27cb7ed..000000000 --- a/dev/helm/templates/service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{include "wiki.fullname" .}} - labels: {{- include "wiki.labels" . | nindent 4 }} - {{- with .Values.service.annotations }} - annotations: - {{- range $key, $value := . }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} -spec: - type: {{.Values.service.type}} - ports: - - port: {{ default "80" .Values.service.port}} - targetPort: http - protocol: TCP - name: http - - port: {{ default "443" .Values.service.httpsPort}} - targetPort: http - protocol: TCP - name: https - selector: {{- include "wiki.selectorLabels" . | nindent 4}} diff --git a/dev/helm/templates/tests/test-connection.yaml b/dev/helm/templates/tests/test-connection.yaml deleted file mode 100644 index 2d0887d47..000000000 --- a/dev/helm/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "wiki.fullname" . }}-test-connection" - labels: - {{- include "wiki.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "wiki.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/dev/helm/values.yaml b/dev/helm/values.yaml deleted file mode 100644 index 6b7296a7a..000000000 --- a/dev/helm/values.yaml +++ /dev/null @@ -1,163 +0,0 @@ -# Default values for wiki. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 -revisionHistoryLimit: 10 - -image: - repository: requarks/wiki - imagePullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: - -livenessProbe: - httpGet: - path: /healthz - port: http - -readinessProbe: - httpGet: - path: /healthz - port: http - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - # Annotations applied for services such as externalDNS or - # service type LoadBalancer - # type: LoadBalancer - # httpsPort: 443 - # annotations: {} - -ingress: - enabled: true - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: wiki.minikube.local - paths: - - path: "/" - pathType: Prefix - - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -volumeMounts: [] - -volumes: [] - -# This will allow us to install locales even without internet access using a initContainer & wikjs "sideloading" -sideload: - enabled: false - # Git-Repo containing all locales.json-files you need: - repoURL: https://github.com/Requarks/wiki-localization - - ## This can be helpfull if you have internet access over a http proxy: - env: [] - # - name: HTTPS_PROXY - # value: http://my.proxy.com:3128 - -## Configuration values for the postgresql dependency. -## ref: https://github.com/kubernetes/charts/blob/master/stable/postgresql/README.md -## -postgresql: - ## Use the PostgreSQL chart dependency. - ## Set to false if bringing your own PostgreSQL, and set secret value postgresql-uri. - ## - enabled: true - ## ssl enforce SSL communication with PostgresSQL - ## Default to false - ## - # ssl: false - ## ca Certificate of Authority - ## Default to empty, point to location of CA - ## - # ca: "path to ca" - ## postgresqlHost override postgres database host - ## Default to postgres - ## - # postgresqlHost: postgres - ## postgresqlPort port for postgres - ## Default to 5432 - ## - # postgresqlPort: 5432 - ## PostgreSQL fullname Override - ## Default to wiki-postgresql unless fullname override is set for Chart - ## - fullnameOverride: "" - ## PostgreSQL User to create. - ## - postgresqlUser: postgres - ## PostgreSQL Database to create. - ## - postgresqlDatabase: wiki - ## Persistent Volume Storage configuration. - ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes - ## - replication: - ## Enable PostgreSQL replication (primary/secondary) - ## - enabled: false - persistence: - ## Enable PostgreSQL persistence using Persistent Volume Claims. - ## - enabled: true - ## concourse data Persistent Volume Storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - # storageClass: "-" - ## Persistent Volume Access Mode. - ## - accessMode: ReadWriteOnce - ## Persistent Volume Storage Size. - ## - size: 8Gi