mirror of
https://github.com/GothenburgBitFactory/taskchampion-sync-server.git
synced 2026-07-16 16:35:41 +00:00
Compare commits
2 Commits
taskchampi
...
taskchampi
| Author | SHA1 | Date | |
|---|---|---|---|
| b5eb61a43d | |||
| 240af34978 |
2
.github/workflows/helm-test.yml
vendored
2
.github/workflows/helm-test.yml
vendored
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.0
|
||||||
appVersion: "0.7.0"
|
appVersion: "0.7.0"
|
||||||
keywords:
|
keywords:
|
||||||
- taskchampion
|
- taskchampion
|
||||||
|
|||||||
@ -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).
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
46
helm/taskchampion-sync-server/templates/NOTES.txt
Normal file
46
helm/taskchampion-sync-server/templates/NOTES.txt
Normal 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
|
||||||
@ -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 -}}
|
||||||
|
|||||||
@ -18,10 +18,10 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
{{- include "taskchampion-sync-server.selectorLabels" . | nindent 8 }}
|
{{- include "taskchampion-sync-server.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.serviceAccount.create }}
|
|
||||||
serviceAccountName: {{ include "taskchampion-sync-server.fullname" . }}
|
{{- with .Values.image.pullSecrets }}
|
||||||
{{- else if .Values.serviceAccount.name }}
|
imagePullSecrets:
|
||||||
serviceAccountName: {{ .Values.serviceAccount.name }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 8 }}
|
{{- toYaml .Values.securityContext | nindent 8 }}
|
||||||
@ -32,6 +32,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 +92,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 +126,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:
|
||||||
|
|||||||
@ -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 }}
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
{{- if .Values.serviceAccount.create -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: {{ include "taskchampion-sync-server.fullname" . }}
|
|
||||||
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 }}
|
|
||||||
{{- end -}}
|
|
||||||
@ -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 -}}
|
||||||
|
|||||||
@ -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,15 +91,6 @@ 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
|
||||||
@ -96,6 +107,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 +127,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 +148,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
|
||||||
|
|||||||
Reference in New Issue
Block a user