diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..886ba48 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: trek +version: 0.1.0 +description: Minimal Helm chart for TREK app +appVersion: "latest" diff --git a/chart/README.md b/chart/README.md new file mode 100644 index 0000000..c5689b9 --- /dev/null +++ b/chart/README.md @@ -0,0 +1,33 @@ +# TREK Helm Chart + +This is a minimal Helm chart for deploying the TREK app. + +## Features +- Deploys the TREK container +- Exposes port 3000 via Service +- Optional persistent storage for `/app/data` and `/app/uploads` +- Configurable environment variables and secrets +- Optional generic Ingress support +- Health checks on `/api/health` + +## Usage + +```sh +helm install trek ./chart \ + --set secretEnv.JWT_SECRET=your_jwt_secret \ + --set ingress.enabled=true \ + --set ingress.hosts[0].host=yourdomain.com +``` + +See `values.yaml` for more options. + +## Files +- `Chart.yaml` — chart metadata +- `values.yaml` — configuration values +- `templates/` — Kubernetes manifests + +## Notes +- Ingress is off by default. Enable and configure hosts for your domain. +- PVCs require a default StorageClass or specify one as needed. +- JWT_SECRET must be set for production use. +- If using ingress, you must manually keep `env.ALLOWED_ORIGINS` and `ingress.hosts` in sync to ensure CORS works correctly. The chart does not sync these automatically. diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt new file mode 100644 index 0000000..45a1993 --- /dev/null +++ b/chart/templates/NOTES.txt @@ -0,0 +1,13 @@ +1. JWT_SECRET handling: + - By default, the chart creates a secret with the value from `values.yaml: secretEnv.JWT_SECRET`. + - To generate a random JWT_SECRET at install, set `generateJwtSecret: true`. + - To use an existing Kubernetes secret, set `existingSecret` to the secret name. The secret must have a key matching `existingSecretKey` (defaults to `JWT_SECRET`). + +2. Example usage: + - Set a custom secret: `--set secretEnv.JWT_SECRET=your_secret` + - Generate a random secret: `--set generateJwtSecret=true` + - Use an existing secret: `--set existingSecret=my-k8s-secret` + - Use a custom key in the existing secret: `--set existingSecret=my-k8s-secret --set existingSecretKey=MY_KEY` + +3. Only one method should be used at a time. If both `generateJwtSecret` and `existingSecret` are set, `existingSecret` takes precedence. + If using `existingSecret`, ensure the referenced secret and key exist in the target namespace. diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 0000000..a3089d7 --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,18 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "trek.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "trek.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml new file mode 100644 index 0000000..7a7ed6a --- /dev/null +++ b/chart/templates/configmap.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "trek.fullname" . }}-config + labels: + app: {{ include "trek.name" . }} +data: + NODE_ENV: {{ .Values.env.NODE_ENV | quote }} + PORT: {{ .Values.env.PORT | quote }} + {{- if .Values.env.ALLOWED_ORIGINS }} + ALLOWED_ORIGINS: {{ .Values.env.ALLOWED_ORIGINS | quote }} + {{- end }} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..d10957e --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "trek.fullname" . }} + labels: + app: {{ include "trek.name" . }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ include "trek.name" . }} + template: + metadata: + labels: + app: {{ include "trek.name" . }} + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- range .Values.imagePullSecrets }} + - name: {{ .name }} + {{- end }} + {{- end }} + containers: + - name: trek + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: 3000 + envFrom: + - configMapRef: + name: {{ include "trek.fullname" . }}-config + env: + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ default (printf "%s-secret" (include "trek.fullname" .)) .Values.existingSecret }} + key: {{ .Values.existingSecretKey | default "JWT_SECRET" }} + volumeMounts: + - name: data + mountPath: /app/data + - name: uploads + mountPath: /app/uploads + livenessProbe: + httpGet: + path: /api/health + port: 3000 + initialDelaySeconds: 15 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /api/health + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 + volumes: + - name: data + persistentVolumeClaim: + claimName: {{ include "trek.fullname" . }}-data + - name: uploads + persistentVolumeClaim: + claimName: {{ include "trek.fullname" . }}-uploads diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 0000000..a13b7f4 --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,32 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "trek.fullname" . }} + labels: + app: {{ include "trek.name" . }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.tls }} + tls: + {{- toYaml .Values.ingress.tls | nindent 4 }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host }} + http: + paths: + {{- range .paths }} + - path: {{ . }} + pathType: Prefix + backend: + service: + name: {{ include "trek.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/chart/templates/pvc.yaml b/chart/templates/pvc.yaml new file mode 100644 index 0000000..663bff5 --- /dev/null +++ b/chart/templates/pvc.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "trek.fullname" . }}-data + labels: + app: {{ include "trek.name" . }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.data.size }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "trek.fullname" . }}-uploads + labels: + app: {{ include "trek.name" . }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.uploads.size }} diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml new file mode 100644 index 0000000..b27596a --- /dev/null +++ b/chart/templates/secret.yaml @@ -0,0 +1,23 @@ +{{- if and (not .Values.existingSecret) (not .Values.generateJwtSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "trek.fullname" . }}-secret + labels: + app: {{ include "trek.name" . }} +type: Opaque +data: + {{ .Values.existingSecretKey | default "JWT_SECRET" }}: {{ .Values.secretEnv.JWT_SECRET | b64enc | quote }} +{{- end }} + +{{- if and (not .Values.existingSecret) (.Values.generateJwtSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "trek.fullname" . }}-secret + labels: + app: {{ include "trek.name" . }} +type: Opaque +stringData: + {{ .Values.existingSecretKey | default "JWT_SECRET" }}: {{ randAlphaNum 32 }} +{{- end }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 0000000..f63e56f --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "trek.fullname" . }} + labels: + app: {{ include "trek.name" . }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: 3000 + protocol: TCP + name: http + selector: + app: {{ include "trek.name" . }} diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..f52f3de --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,53 @@ + +image: + repository: mauriceboe/trek + tag: latest + pullPolicy: IfNotPresent + +# Optional image pull secrets for private registries +imagePullSecrets: [] + # - name: my-registry-secret + +service: + type: ClusterIP + port: 3000 + +env: + NODE_ENV: production + PORT: 3000 + # ALLOWED_ORIGINS: "" +# NOTE: If using ingress, ensure env.ALLOWED_ORIGINS matches the domains in ingress.hosts for proper CORS configuration. + + +# JWT secret configuration +secretEnv: + # If set, use this value for JWT_SECRET (base64-encoded in secret.yaml) + JWT_SECRET: "" + +# If true, a random JWT_SECRET will be generated during install (overrides secretEnv.JWT_SECRET) +generateJwtSecret: false + +# If set, use an existing Kubernetes secret for JWT_SECRET +existingSecret: "" +existingSecretKey: JWT_SECRET + +persistence: + enabled: true + data: + size: 1Gi + uploads: + size: 1Gi + +resources: {} + +ingress: + enabled: false + annotations: {} + hosts: + - host: chart-example.local + paths: + - / + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local