3 Commits

Author SHA1 Message Date
3f78f2d4c5 Readding ServiceAccount (#212)
* Readding ServiceAccount

* Bumping version
2026-06-10 03:32:09 +00:00
b5eb61a43d Adding health probes, resource requests and fixing container permissions (#210)
* Adding health probes and fixing container permissions

* Bump version

* Cleanup

* No ServiceAccount anymore

* No ServiceAccount anymore

* Update pull secrets

* Fixing yaml issue

* Fixed resource requests and limits

* Bumping version
2026-06-09 23:41:50 +00:00
240af34978 Updating Helm documentation (#209)
* Updating Helm documentation

* No version number
2026-05-29 22:06:53 +00:00
14 changed files with 392 additions and 132 deletions

View File

@ -49,7 +49,6 @@ jobs:
grep -q 'kind: Deployment' /tmp/helm-sqlite.yaml grep -q 'kind: Deployment' /tmp/helm-sqlite.yaml
grep -q 'kind: Service' /tmp/helm-sqlite.yaml grep -q 'kind: Service' /tmp/helm-sqlite.yaml
grep -q 'kind: Ingress' /tmp/helm-sqlite.yaml grep -q 'kind: Ingress' /tmp/helm-sqlite.yaml
grep -q 'kind: ServiceAccount' /tmp/helm-sqlite.yaml
# Verify SQLite-specific rendering # Verify SQLite-specific rendering
grep -q 'emptyDir' /tmp/helm-sqlite.yaml grep -q 'emptyDir' /tmp/helm-sqlite.yaml
@ -82,7 +81,6 @@ jobs:
grep -q 'kind: Deployment' /tmp/helm-postgres.yaml grep -q 'kind: Deployment' /tmp/helm-postgres.yaml
grep -q 'kind: Service' /tmp/helm-postgres.yaml grep -q 'kind: Service' /tmp/helm-postgres.yaml
grep -q 'kind: Secret' /tmp/helm-postgres.yaml grep -q 'kind: Secret' /tmp/helm-postgres.yaml
grep -q 'kind: ServiceAccount' /tmp/helm-postgres.yaml
# Verify PostgreSQL-specific rendering # Verify PostgreSQL-specific rendering
grep -q 'kind: HTTPRoute' /tmp/helm-postgres.yaml grep -q 'kind: HTTPRoute' /tmp/helm-postgres.yaml

View File

@ -5,56 +5,127 @@ A Helm chart is available for deploying taskchampion-sync-server on Kubernetes.
## Adding the Repository ## Adding the Repository
```sh ```sh
helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server/helm-chart helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server
helm repo update helm repo update
``` ```
## Installing the Chart ## Installing
The chart requires exactly one storage backend to be enabled. To install with
the SQLite backend:
```sh ```sh
# SQLite backend
helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \ helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \
--set sqlite.enabled=true --set sqlite.enabled=true
```
To install with the Postgres backend, provide the connection details: # PostgreSQL backend
```sh
helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \ helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \
--set postgres.enabled=true \ --set postgres.enabled=true \
--set postgres.host=my-postgres \ --set postgres.host=my-postgres \
--set postgres.database=taskchampion \ --set postgres.db=taskchampion \
--set postgres.username=myuser \ --set postgres.username=myuser \
--set postgres.password=mypassword --set postgres.password=mypassword
``` ```
Alternatively, pass an existing Secret name via `postgres.existingSecret`. The ## Storage Backends
secret must contain a `connection` key holding a [LibPQ-style connection
URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS).
## Configuration Exactly one storage backend must be enabled. The chart fails validation if
neither or both are enabled.
The chart does not implement TLS. The expectation is that a Kubernetes ingress ### SQLite
or gateway will terminate TLS and proxy HTTP traffic to the server container.
Enable one of the built-in options or configure your own:
```sh Mounts a volume at `sqlite.dataDir` (default `/var/lib/taskchampion-sync-server/data`).
# NGINX ingress The volume defaults to `emptyDir` but can be backed by a PVC or
--set ingress.enabled=true --set ingress.hosts[0]=taskchampion.example.com existing PersistentVolumeClaim:
# Gateway API HTTPRoute | Value | Description |
--set httpRoute.enabled=true \ |-------|-------------|
--set httpRoute.gateway=my-gateway \ | `sqlite.persistence.enabled` | Create a PVC (default: `false`) |
--set httpRoute.host=taskchampion.example.com | `sqlite.existingPV` | Use an existing PVC by name |
| `sqlite.emptyDir` | emptyDir volume settings (default fallback) |
### PostgreSQL
Creates a Deployment with optional replicas, an auto-generated or existing
secret for the connection string, and an init container that waits for
PostgreSQL, applies the schema, and optionally seeds client IDs.
**Secret handling** — When `postgres.existingSecret` is empty (default), the
chart creates a secret named `{release-name}-connection` with a `conn` key.
When set, the chart reads the named secret. It accepts either a `conn` key
with a full [LibPQ-style URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS)
or individual fields (`host`, `port`, `username`, `password`, `database`) that
override the corresponding `postgres.*` values.
**Init container** — Enabled by default. Waits for PG readiness, downloads
the schema from `https://raw.githubusercontent.com/GothenburgBitFactory/taskchampion-sync-server/v0.7.0/postgres/schema.sql`,
applies it, and seeds client IDs if `clientIdSecret` is set. Override the
schema URL with `postgres.initContainer.schemaUrl`.
**Replicas** — Replicas only apply with PostgreSQL:
`replicas.enabled=true`, `replicas.count=N`. SQLite is always single-replica.
## Secrets
### Client ID Secret
Optional. Restrict which client IDs the server accepts. Create a Secret with
comma-separated UUIDs (base64-encoded) under a `client-ids` key:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: my-client-ids
type: Opaque
data:
client-ids: <base64-encoded comma-separated UUIDs>
``` ```
To restrict which client IDs the server accepts, create a Secret containing a Reference it via `clientIdSecret: "my-client-ids"`. The value is available to
newline-separated list of UUIDs and reference it: the server as `CLIENT_ID` and to the init container as `CLIENT_IDS`.
```sh ## Networking
--set clientIdSecret=my-client-ids-secret
The chart does not implement TLS. Terminate TLS at the ingress or gateway and
proxy HTTP to port 8080.
### Ingress (NGINX)
```yaml
ingress:
enabled: true
hosts:
- taskchampion.example.com
``` ```
See the chart's `values.yaml` for the full set of configuration options. ### HTTPRoute (Kubernetes Gateway API)
Use `parentRefs`, `hostnames`, and `rules`:
```yaml
httpRoute:
enabled: true
parentRefs:
- name: my-gateway
hostnames:
- tasks.example.com
rules:
- path:
type: PathPrefix
value: /
backendPort: 8080
```
The deprecated fields `httpRoute.gateway`, `httpRoute.host`,
`httpRoute.path`, and `httpRoute.port` are used as a fallback when the
structured arrays are empty.
### ServiceAccount and RBAC
When `serviceAccount.create` is `true` (default), the chart creates a
ServiceAccount, a Role with `get`/`create`/`update`/`patch` on secrets, and
a RoleBinding. Set `serviceAccount.name` to use an existing SA.
## Reference
See the chart's [values.yaml](https://github.com/GothenburgBitFactory/taskchampion-sync-server/blob/main/helm/taskchampion-sync-server/values.yaml)
for all configurable options.

View File

@ -1,5 +1,5 @@
# Patterns to ignore when building the Helm chart # Patterns to ignore when building the Helm chart
.git .git
.gitignore
*.md
examples/ examples/
.DS_Store
*.tgz

View File

@ -2,7 +2,7 @@ apiVersion: v2
name: taskchampion-sync-server name: taskchampion-sync-server
description: A Helm chart for deploying TaskChampion Sync Server on Kubernetes description: A Helm chart for deploying TaskChampion Sync Server on Kubernetes
type: application type: application
version: 0.1.2 version: 0.2.1
appVersion: "0.7.0" appVersion: "0.7.0"
keywords: keywords:
- taskchampion - taskchampion

View File

@ -7,28 +7,55 @@ Deploy the [TaskChampion Sync Server](https://github.com/GothenburgBitFactory/ta
- Kubernetes 1.23+ - Kubernetes 1.23+
- Helm 3+ - Helm 3+
## Installing
```console
helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server
helm repo update
helm install my-release taskchampion/taskchampion-sync-server --set sqlite.enabled=true
```
## Storage Backends ## Storage Backends
Exactly one storage backend must be enabled. The chart will fail validation if both or neither are enabled. Exactly one storage backend must be enabled. The chart fails validation if neither or both are enabled.
### SQLite ### SQLite
```console | Parameter | Default | Description |
helm install my-release ./helm/taskchampion-sync-server -f helm/taskchampion-sync-server/examples/sqlite-values.yaml |-----------|---------|-------------|
``` | `sqlite.enabled` | `false` | Enable SQLite backend |
| `sqlite.dataDir` | `/var/lib/taskchampion-sync-server/data` | Data directory path |
| `sqlite.existingPV` | `""` | Use an existing PVC name |
| `sqlite.persistence.enabled` | `false` | Create a PVC |
| `sqlite.persistence.size` | `1Gi` | PVC size |
| `sqlite.persistence.accessMode` | `ReadWriteOnce` | PVC access mode |
| `sqlite.emptyDir` | — | emptyDir volume settings |
### PostgreSQL ### PostgreSQL
```console | Parameter | Default | Description |
helm install my-release ./helm/taskchampion-sync-server -f helm/taskchampion-sync-server/examples/postgres-values.yaml |-----------|---------|-------------|
``` | `postgres.enabled` | `false` | Enable PostgreSQL backend |
| `postgres.host` | `""` | PostgreSQL host |
| `postgres.port` | `5432` | PostgreSQL port |
| `postgres.db` | `taskchampion` | Database name |
| `postgres.username` | `""` | Database user |
| `postgres.password` | `""` | Database password |
| `postgres.sslMode` | `disable` | SSL mode |
| `postgres.existingSecret` | `""` | Use existing secret by name |
**Secret** — When `existingSecret` is empty (default), a secret named `{release-name}-connection` is created with a `conn` key. When set, the chart reads that secret. It accepts either a `conn` key with a full URI or individual keys (`host`, `port`, `username`, `password`, `database`) that override `postgres.*` values.
**Init container** — Enabled by default. Waits for PG readiness, downloads and applies the schema (from the chart's `appVersion` URL), and seeds client IDs if `clientIdSecret` is set. Override with `postgres.initContainer.schemaUrl`.
**Replicas** — Only apply with PostgreSQL (`replicas.enabled=true`, `replicas.count=N`). SQLite is single-replica.
## Secrets ## Secrets
The chart expects pre-created secrets referenced by name:
### Client ID Secret ### Client ID Secret
Restrict which client IDs the server accepts. Create a Secret with comma-separated UUIDs (base64-encoded) under a `client-ids` key:
```yaml ```yaml
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@ -39,22 +66,73 @@ data:
client-ids: <base64-encoded comma-separated UUIDs> client-ids: <base64-encoded comma-separated UUIDs>
``` ```
Reference it via `clientIdSecret: "my-client-ids"`. Reference via `clientIdSecret: "my-client-ids"`.
### PostgreSQL Secret ## Networking
For PostgreSQL, the chart can automatically create a secret with the connection string, or use an existing secret. The chart does not implement TLS. Terminate TLS at the ingress or gateway.
**Automatic Secret Creation**: ### Service
- When `postgres.existingSecret` is empty (default), the chart automatically creates a secret
- Secret is named using Helm naming convention: `release-name-taskchampion-sync-server`
- Secret contains only a `connection` key with the built connection string
**Existing Secret Usage**: | Parameter | Default | Description |
- When `postgres.existingSecret` is provided, the chart uses that secret |-----------|---------|-------------|
- The secret **must** contain a `connection` key with the PostgreSQL connection string | `service.type` | `ClusterIP` | Service type |
- If the secret doesn't have a `connection` key, the deployment will fail with a clear error | `service.port` | `8080` | Service port |
| `service.targetPort` | `8080` | Container port |
## Configuration ### Ingress (NGINX)
See [values.yaml](values.yaml) for all configurable options. | Parameter | Default | Description |
|-----------|---------|-------------|
| `ingress.enabled` | `false` | Enable NGINX ingress |
| `ingress.className` | `""` | Ingress class name |
| `ingress.annotations` | `{}` | Ingress annotations |
| `ingress.hosts` | `[]` | Host list |
| `ingress.tls` | `[]` | TLS configuration |
### HTTPRoute (Kubernetes Gateway API)
| Parameter | Default | Description |
|-----------|---------|-------------|
| `httpRoute.enabled` | `false` | Enable HTTPRoute |
| `httpRoute.parentRefs` | `[]` | Parent gateway references (primary) |
| `httpRoute.hostnames` | `[]` | Hostnames |
| `httpRoute.rules` | `[]` | Routing rules |
| `httpRoute.gateway` | `""` | (Deprecated) Single gateway name |
| `httpRoute.host` | `""` | (Deprecated) Single hostname |
| `httpRoute.path` | `"/"` | (Deprecated) Single path |
| `httpRoute.port` | `8080` | (Deprecated) Single port |
**Recommended** — use `parentRefs`, `hostnames`, `rules`. The deprecated
single-value fields (`gateway`, `host`, `path`, `port`) are used as a fallback
when the arrays are empty.
## ServiceAccount and RBAC
| Parameter | Default | Description |
|-----------|---------|-------------|
| `serviceAccount.create` | `true` | Create SA, Role, and RoleBinding |
| `serviceAccount.name` | `""` | Use existing SA name |
When created, the chart provisions a ServiceAccount, a Role with
`get`/`create`/`update`/`patch` on secrets, and a RoleBinding.
## Image Configuration
| Parameter | Default | Description |
|-----------|---------|-------------|
| `image.repo` | `ghcr.io/gothenburgbitfactory/taskchampion-sync-server` | Image repository |
| `image.tag` | `"0.7.0"` | Image tag |
| `image.pullPolicy` | `IfNotPresent` | Pull policy |
| `image.pullSecrets` | `[]` | Pull secrets |
PostgreSQL appends `-postgres` to the image repo automatically.
## Environment Variables
Custom env vars are passed via `env`. `DATA_DIR` (SQLite) and `conn`
(PostgreSQL) are set automatically and must not be set manually.
## Full Configuration
See [values.yaml](values.yaml).

View File

@ -10,9 +10,12 @@ postgres:
clientIdSecret: "taskchampion-client-ids" clientIdSecret: "taskchampion-client-ids"
env: env:
RUST_LOG: debug - name: RUST_LOG
LISTEN: "0.0.0.0:8080" value: debug
CREATE_CLIENTS: "false" - name: LISTEN
value: "0.0.0.0:8080"
- name: CREATE_CLIENTS
value: "false"
replicas: replicas:
enabled: true enabled: true
@ -20,7 +23,7 @@ replicas:
image: image:
pullSecrets: pullSecrets:
- my-registry-secret - name: my-registry-secret
httpRoute: httpRoute:
enabled: true enabled: true

View File

@ -12,9 +12,12 @@ postgres:
clientIdSecret: "taskchampion-client-ids" clientIdSecret: "taskchampion-client-ids"
env: env:
RUST_LOG: info - name: RUST_LOG
LISTEN: "0.0.0.0:8080" value: info
CREATE_CLIENTS: "false" - name: LISTEN
value: "0.0.0.0:8080"
- name: CREATE_CLIENTS
value: "false"
ingress: ingress:
enabled: true enabled: true

View File

@ -0,0 +1,46 @@
Thank you for installing {{ .Chart.Name }} — version {{ .Chart.Version }} (app: {{ .Chart.AppVersion }}).
## Storage Backend
{{- if eq .Values.sqlite.enabled true }}
**SQLite** — Data is stored at `{{ .Values.sqlite.dataDir }}`.
{{- if .Values.sqlite.persistence.enabled }}
PersistentVolumeClaim: `{{ include "taskchampion-sync-server.fullname" . }}-pvc`
{{- else if .Values.sqlite.existingPV }}
Using existing PVC: `{{ .Values.sqlite.existingPV }}`
{{- else }}
Using an emptyDir volume (ephemeral — data is lost on pod restart).
Set `sqlite.persistence.enabled=true` for persistent storage.
{{- end }}
{{- end }}
{{- if eq .Values.postgres.enabled true }}
**PostgreSQL** — Connection URI stored in Secret `{{ include "taskchampion-sync-server.postgres-secret-name" . }}`.
Host: {{ .Values.postgres.host }}:{{ .Values.postgres.port }}
Database: {{ .Values.postgres.database }}
{{- end }}
## Verify the Deployment
kubectl get pods -l {{ include "taskchampion-sync-server.selectorLabels" . | replace ": " "=" | replace "\n" "," | trimSuffix "," }}
## Check Pod Logs
kubectl logs -l {{ include "taskchampion-sync-server.selectorLabels" . | replace ": " "=" | replace "\n" "," | trimSuffix "," }}
## Test the API
kubectl port-forward svc/{{ include "taskchampion-sync-server.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }}
curl http://localhost:{{ .Values.service.port }}/
## View Connection Secret (PostgreSQL only)
{{- if eq .Values.postgres.enabled true }}
kubectl get secret {{ include "taskchampion-sync-server.postgres-secret-name" . }} -o jsonpath="{.data.connection}" | base64 -d
{{- end }}
## Configuration
For full configuration options, see:
https://github.com/GothenburgBitFactory/taskchampion-sync-server

View File

@ -23,6 +23,7 @@ taskchampion-sync-server helpers
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/name: {{ include "taskchampion-sync-server.name" . }} app.kubernetes.io/name: {{ include "taskchampion-sync-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: server
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }} {{- end }}
@ -32,13 +33,6 @@ app.kubernetes.io/name: {{ include "taskchampion-sync-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }} {{- end }}
{{- define "taskchampion-sync-server.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "taskchampion-sync-server.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{- define "taskchampion-sync-server.postgres-connection" -}} {{- define "taskchampion-sync-server.postgres-connection" -}}
{{- $host := .Values.postgres.host -}} {{- $host := .Values.postgres.host -}}
@ -100,3 +94,7 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{- define "taskchampion-sync-server.postgres-secret-name" -}} {{- define "taskchampion-sync-server.postgres-secret-name" -}}
{{- printf "%s-connection" .Release.Name -}} {{- printf "%s-connection" .Release.Name -}}
{{- end -}} {{- end -}}
{{- define "taskchampion-sync-server.serviceAccountName" -}}
{{- default (include "taskchampion-sync-server.fullname" .) .Values.serviceAccount.name -}}
{{- end -}}

View File

@ -18,10 +18,13 @@ spec:
labels: labels:
{{- include "taskchampion-sync-server.selectorLabels" . | nindent 8 }} {{- include "taskchampion-sync-server.selectorLabels" . | nindent 8 }}
spec: spec:
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.serviceAccount.create }} {{- if .Values.serviceAccount.create }}
serviceAccountName: {{ include "taskchampion-sync-server.fullname" . }} serviceAccountName: {{ include "taskchampion-sync-server.serviceAccountName" . }}
{{- else if .Values.serviceAccount.name }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }} {{- end }}
securityContext: securityContext:
{{- toYaml .Values.securityContext | nindent 8 }} {{- toYaml .Values.securityContext | nindent 8 }}
@ -32,6 +35,8 @@ spec:
imagePullPolicy: {{ .Values.postgres.initContainer.imagePullPolicy }} imagePullPolicy: {{ .Values.postgres.initContainer.imagePullPolicy }}
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 999
env: env:
- name: PGURI - name: PGURI
valueFrom: valueFrom:
@ -90,11 +95,18 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
env: runAsNonRoot: true
{{- range $name, $value := .Values.env }} runAsUser: 1092
- name: {{ $name }} readOnlyRootFilesystem: true
value: {{ $value | quote }} {{- if eq .Values.postgres.enabled true }}
command:
- /bin/taskchampion-sync-server-postgres
{{- else }}
command:
- /bin/taskchampion-sync-server
{{- end }} {{- end }}
env:
{{- toYaml .Values.env | nindent 12 }}
{{- if .Values.clientIdSecret }} {{- if .Values.clientIdSecret }}
- name: CLIENT_ID - name: CLIENT_ID
valueFrom: valueFrom:
@ -117,9 +129,24 @@ spec:
- name: http - name: http
containerPort: {{ .Values.service.targetPort }} containerPort: {{ .Values.service.targetPort }}
protocol: TCP protocol: TCP
{{- with .Values.resources }} livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 10
{{- if eq .Values.postgres.enabled true }}
resources: resources:
{{- toYaml . | nindent 12 }} {{- toYaml .Values.postgres.resources | nindent 12 }}
{{- else }}
resources:
{{- toYaml .Values.sqlite.resources | nindent 12 }}
{{- end }} {{- end }}
{{- if eq .Values.sqlite.enabled true }} {{- if eq .Values.sqlite.enabled true }}
volumeMounts: volumeMounts:

View File

@ -18,7 +18,10 @@ spec:
{{- toYaml .Values.ingress.tls | nindent 4 }} {{- toYaml .Values.ingress.tls | nindent 4 }}
{{- end }} {{- end }}
rules: rules:
{{- $svcName := include "taskchampion-sync-server.fullname" . -}}
{{- $svcPort := $.Values.service.port -}}
{{- range .Values.ingress.hosts }} {{- range .Values.ingress.hosts }}
{{- if kindIs "string" . }}
- host: {{ . | quote }} - host: {{ . | quote }}
http: http:
paths: paths:
@ -26,8 +29,30 @@ spec:
pathType: Prefix pathType: Prefix
backend: backend:
service: service:
name: {{ include "taskchampion-sync-server.fullname" $ }} name: {{ $svcName }}
port: port:
number: {{ $.Values.service.port }} number: {{ $svcPort }}
{{- else }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path | default "/" | quote }}
pathType: {{ .pathType | default "Prefix" }}
backend:
service:
name: {{ $svcName }}
port:
number: {{ $svcPort }}
{{- else }}
- path: /
pathType: Prefix
backend:
service:
name: {{ $svcName }}
port:
number: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@ -2,37 +2,8 @@
apiVersion: v1 apiVersion: v1
kind: ServiceAccount kind: ServiceAccount
metadata: metadata:
name: {{ include "taskchampion-sync-server.fullname" . }} name: {{ include "taskchampion-sync-server.serviceAccountName" . }}
labels:
{{- include "taskchampion-sync-server.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "taskchampion-sync-server.fullname" . }}-secret-manager
labels:
{{- include "taskchampion-sync-server.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "get", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "taskchampion-sync-server.fullname" . }}-secret-binding
labels:
{{- include "taskchampion-sync-server.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "taskchampion-sync-server.fullname" . }}-secret-manager
subjects:
- kind: ServiceAccount
name: {{ include "taskchampion-sync-server.fullname" . }}
namespace: {{ .Release.Namespace }} namespace: {{ .Release.Namespace }}
{{- end -}} labels:
{{- include "taskchampion-sync-server.labels" . | nindent 4 }}
{{- end }}

View File

@ -1,3 +1,10 @@
{{- /* Validate imagePullSecrets entries are objects (not plain strings) */ -}}
{{- range $i, $secret := .Values.image.pullSecrets -}}
{{- if not (kindIs "map" $secret) -}}
{{- fail (printf "ERROR: image.pullSecrets[%d] is a string (%q). Each entry must be an object with a 'name' key, e.g.\n image:\n pullSecrets:\n - name: %s" $i $secret $secret) -}}
{{- end -}}
{{- end -}}
{{- if and (eq .Values.sqlite.enabled false) (eq .Values.postgres.enabled false) -}} {{- if and (eq .Values.sqlite.enabled false) (eq .Values.postgres.enabled false) -}}
{{- fail "ERROR: Exactly one storage backend must be enabled. Either sqlite.enabled or postgres.enabled must be true." -}} {{- fail "ERROR: Exactly one storage backend must be enabled. Either sqlite.enabled or postgres.enabled must be true." -}}
{{- end -}} {{- end -}}

View File

@ -9,6 +9,9 @@ image:
tag: "0.7.0" tag: "0.7.0"
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
pullSecrets: [] pullSecrets: []
# pullSecrets expects a list of objects with a "name" key:
# pullSecrets:
# - name: my-registry-cred
# Existing secret containing client IDs (comma-separated UUIDs) # Existing secret containing client IDs (comma-separated UUIDs)
# Expected key: client-ids # Expected key: client-ids
@ -16,10 +19,19 @@ clientIdSecret: ""
# Environment variables passed directly to the container # Environment variables passed directly to the container
# NOTE: DATA_DIR and CONNECTION are set automatically based on backend # NOTE: DATA_DIR and CONNECTION are set automatically based on backend
# To add secret/configMap references, use valueFrom:
# - name: MY_SECRET_VAR
# valueFrom:
# secretKeyRef:
# name: my-secret
# key: my-key
env: env:
RUST_LOG: info - name: RUST_LOG
LISTEN: "0.0.0.0:8080" value: info
CREATE_CLIENTS: "true" - name: LISTEN
value: "0.0.0.0:8080"
- name: CREATE_CLIENTS
value: "true"
# Service configuration # Service configuration
service: service:
@ -28,6 +40,14 @@ service:
targetPort: 8080 targetPort: 8080
# Ingress configuration # Ingress configuration
# Each host can be a string (simple) or a map with path rules:
# hosts:
# - host: app.example.com
# paths:
# - path: /
# pathType: Prefix
# - path: /api
# pathType: Exact
ingress: ingress:
enabled: false enabled: false
className: "" className: ""
@ -71,20 +91,21 @@ httpRoute:
path: "/" path: "/"
port: 8080 port: 8080
# Resource limits and requests
resources: {}
# limits:
# memory: 128Mi
# cpu: 100m
# requests:
# memory: 64Mi
# cpu: 50m
# Replica configuration (only applies when postgres is enabled) # Replica configuration (only applies when postgres is enabled)
replicas: replicas:
enabled: false enabled: false
count: 1 count: 1
# ServiceAccount configuration
# The app does not access the Kubernetes API, so no RBAC permissions are needed.
# A dedicated ServiceAccount is created to give the pod a stable identity
# for network policies, pod security, or future RBAC.
serviceAccount:
# create specifies whether a ServiceAccount should be created
create: true
# name sets the ServiceAccount name
name: taskchampion-sync-server
# Security context for the pod # Security context for the pod
# NOTE: runAsUser and runAsGroup are intentionally unset. # NOTE: runAsUser and runAsGroup are intentionally unset.
# The Docker entrypoint requires root to chown the data directory and then # The Docker entrypoint requires root to chown the data directory and then
@ -96,6 +117,15 @@ securityContext:
sqlite: sqlite:
enabled: false enabled: false
dataDir: /var/lib/taskchampion-sync-server/data dataDir: /var/lib/taskchampion-sync-server/data
# Resource limits and requests
resources:
limits:
memory: 25Mi
cpu: 100m
requests:
# 5Mi is sufficient for the SQLite image
memory: 5Mi
cpu: 10m
existingPV: "" existingPV: ""
emptyDir: emptyDir:
sizeLimit: "" sizeLimit: ""
@ -107,12 +137,6 @@ sqlite:
storageClass: "" storageClass: ""
existingClaim: "" existingClaim: ""
# Service account configuration
serviceAccount:
create: true # Default: automatically create service account
name: "" # Optional: use existing service account
annotations: {}
# PostgreSQL configuration # PostgreSQL configuration
postgres: postgres:
enabled: false enabled: false
@ -134,3 +158,12 @@ postgres:
# Override the schema URL. Defaults to the official schema for this chart's appVersion. # Override the schema URL. Defaults to the official schema for this chart's appVersion.
# e.g. https://raw.githubusercontent.com/GothenburgBitFactory/taskchampion-sync-server/v0.7.0/postgres/schema.sql # e.g. https://raw.githubusercontent.com/GothenburgBitFactory/taskchampion-sync-server/v0.7.0/postgres/schema.sql
schemaUrl: "" schemaUrl: ""
# Resource limits and requests
resources:
limits:
memory: 100Mi
cpu: 100m
requests:
# 20Mi is the minimum for the Postgres image
memory: 20Mi
cpu: 10m