Skip to content

Commit 369585c

Browse files
committed
build: add a simple helm chart for backend deployment only + publish pipeline
1 parent c7ca897 commit 369585c

17 files changed

Lines changed: 816 additions & 0 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Release Chart
2+
3+
on:
4+
# Push includes PR merge
5+
push:
6+
branches:
7+
- develop
8+
paths:
9+
# Workflow is triggered only if chart dir changes
10+
- chart/**
11+
# Allow manual trigger
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: read
16+
packages: write
17+
18+
jobs:
19+
publish:
20+
uses: hotosm/gh-workflows/.github/workflows/just.yml@3.3.2
21+
with:
22+
environment: "test"
23+
command: "chart publish"
24+
secrets: inherit

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,6 @@ fAIr-utilities
6262
data
6363

6464
fair-app-data/*
65+
66+
# helm charts
67+
fair-*.tgz

Justfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
set dotenv-load
2+
3+
mod chart 'tasks/chart'
4+
5+
# List available commands
6+
[private]
7+
default:
8+
just help
9+
10+
# List available commands
11+
help:
12+
just --justfile {{justfile()}} --list
13+
14+
# Echo to terminal with blue colour
15+
[no-cd]
16+
_echo-blue text:
17+
#!/usr/bin/env sh
18+
printf "\033[0;34m%s\033[0m\n" "{{ text }}"
19+
20+
# Echo to terminal with yellow colour
21+
[no-cd]
22+
_echo-yellow text:
23+
#!/usr/bin/env sh
24+
printf "\033[0;33m%s\033[0m\n" "{{ text }}"
25+
26+
# Echo to terminal with red colour
27+
[no-cd]
28+
_echo-red text:
29+
#!/usr/bin/env sh
30+
printf "\033[0;41m%s\033[0m\n" "{{ text }}"

chart/.helmignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.DS_Store
2+
.git
3+
.gitignore
4+
.idea
5+
*.swp
6+
*.bak
7+
*.tmp
8+
*.orig
9+
*~

chart/Chart.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: fair
3+
description: AI Assisted Mapping Tool
4+
type: application
5+
version: 0.1.0
6+
appVersion: "2.2.19"

chart/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# fAIr Helm Chart
2+
3+
Deploys the fAIr backend API and Django-Q async worker.
4+
5+
The frontend is deployed separately (S3 + CloudFront via GitHub Actions).
6+
PostgreSQL is expected to be provided externally (e.g.
7+
[CloudNativePG](https://cloudnative-pg.io/) or a managed database service).
8+
9+
## Quick start
10+
11+
```bash
12+
helm install fair oci://ghcr.io/hotosm/charts/fair
13+
```
14+
15+
## Example values
16+
17+
```yaml
18+
externalDatabase:
19+
host: my-pg-cluster-rw.db.svc
20+
database: ai
21+
username: fair
22+
existingSecret: fair-db-credentials
23+
existingSecretKey: password
24+
25+
ingress:
26+
enabled: true
27+
className: nginx
28+
hosts:
29+
- host: fair.example.com
30+
paths:
31+
- path: /api
32+
pathType: Prefix
33+
34+
backend:
35+
envFrom:
36+
- secretRef:
37+
name: fair-backend-secrets
38+
```
39+
40+
## Key values
41+
42+
| Parameter | Description | Default |
43+
|---|---|---|
44+
| `externalDatabase.host` | PostgreSQL host | `""` |
45+
| `externalDatabase.existingSecret` | Secret containing DB password | `""` |
46+
| `backend.djangoQ.enabled` | Run Django-Q sidecar for async tasks | `true` |
47+
| `backend.migrate.enabled` | Run migrations on install/upgrade | `true` |
48+
| `ingress.enabled` | Create Ingress resource | `false` |

chart/templates/NOTES.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
fAIr has been deployed!
2+
3+
Components:
4+
- Backend API: {{ include "fair.backend.fullname" . }}
5+
{{- if .Values.backend.djangoQ.enabled }}
6+
- Django-Q: running as sidecar in backend pod
7+
{{- end }}
8+
- PostgreSQL: external ({{ .Values.externalDatabase.host }})
9+
10+
{{- if .Values.ingress.enabled }}
11+
12+
Access the application at:
13+
{{- range .Values.ingress.hosts }}
14+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}
15+
{{- end }}
16+
{{- else }}
17+
18+
To access the backend API, run:
19+
kubectl port-forward svc/{{ include "fair.backend.fullname" . }} 8000:{{ .Values.backend.service.port }}
20+
{{- end }}

chart/templates/_helpers.tpl

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "fair.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
*/}}
11+
{{- define "fair.fullname" -}}
12+
{{- if .Values.fullnameOverride }}
13+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
14+
{{- else }}
15+
{{- $name := default .Chart.Name .Values.nameOverride }}
16+
{{- if contains $name .Release.Name }}
17+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
18+
{{- else }}
19+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
20+
{{- end }}
21+
{{- end }}
22+
{{- end }}
23+
24+
{{/*
25+
Create chart name and version as used by the chart label.
26+
*/}}
27+
{{- define "fair.chart" -}}
28+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
29+
{{- end }}
30+
31+
{{/*
32+
Common labels
33+
*/}}
34+
{{- define "fair.labels" -}}
35+
helm.sh/chart: {{ include "fair.chart" . }}
36+
{{ include "fair.selectorLabels" . }}
37+
{{- if .Chart.AppVersion }}
38+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
39+
{{- end }}
40+
app.kubernetes.io/managed-by: {{ .Release.Service }}
41+
{{- end }}
42+
43+
{{/*
44+
Selector labels
45+
*/}}
46+
{{- define "fair.selectorLabels" -}}
47+
app.kubernetes.io/name: {{ include "fair.name" . }}
48+
app.kubernetes.io/instance: {{ .Release.Name }}
49+
{{- end }}
50+
51+
{{/*
52+
Create the name of the service account to use
53+
*/}}
54+
{{- define "fair.serviceAccountName" -}}
55+
{{- if .Values.serviceAccount.create }}
56+
{{- default (include "fair.fullname" .) .Values.serviceAccount.name }}
57+
{{- else }}
58+
{{- default "default" .Values.serviceAccount.name }}
59+
{{- end }}
60+
{{- end }}
61+
62+
{{/*
63+
Backend fullname
64+
*/}}
65+
{{- define "fair.backend.fullname" -}}
66+
{{- printf "%s-backend" (include "fair.fullname" .) | trunc 63 | trimSuffix "-" }}
67+
{{- end }}
68+
69+
{{/*
70+
Backend selector labels
71+
*/}}
72+
{{- define "fair.backend.selectorLabels" -}}
73+
{{ include "fair.selectorLabels" . }}
74+
app.kubernetes.io/component: backend
75+
{{- end }}
76+
77+
{{/*
78+
Backend image
79+
*/}}
80+
{{- define "fair.backend.image" -}}
81+
{{- $tag := default .Chart.AppVersion .Values.image.backend.tag -}}
82+
{{- printf "%s:%s" .Values.image.backend.repository $tag }}
83+
{{- end }}
84+
85+
{{/*
86+
DATABASE_URL for Django
87+
*/}}
88+
{{- define "fair.databaseUrl" -}}
89+
postgis://$(DATABASE_USER):$(DATABASE_PASSWORD)@{{ .Values.externalDatabase.host }}:{{ .Values.externalDatabase.port | toString }}/{{ .Values.externalDatabase.database }}
90+
{{- end }}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ include "fair.backend.fullname" . }}
5+
labels:
6+
{{- include "fair.labels" . | nindent 4 }}
7+
app.kubernetes.io/component: backend
8+
data:
9+
DATABASE_HOST: {{ .Values.externalDatabase.host | quote }}
10+
DATABASE_PORT: {{ .Values.externalDatabase.port | toString | quote }}
11+
DATABASE_NAME: {{ .Values.externalDatabase.database | quote }}
12+
DATABASE_USER: {{ .Values.externalDatabase.username | quote }}
13+
{{- range $key, $value := .Values.backend.env }}
14+
{{ $key }}: {{ $value | quote }}
15+
{{- end }}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "fair.backend.fullname" . }}
5+
labels:
6+
{{- include "fair.labels" . | nindent 4 }}
7+
app.kubernetes.io/component: backend
8+
spec:
9+
replicas: {{ .Values.backendReplicaCount }}
10+
selector:
11+
matchLabels:
12+
{{- include "fair.backend.selectorLabels" . | nindent 6 }}
13+
template:
14+
metadata:
15+
annotations:
16+
checksum/config: {{ include (print $.Template.BasePath "/backend/configmap.yaml") . | sha256sum }}
17+
{{- with .Values.backend.podAnnotations }}
18+
{{- toYaml . | nindent 8 }}
19+
{{- end }}
20+
labels:
21+
{{- include "fair.backend.selectorLabels" . | nindent 8 }}
22+
spec:
23+
{{- with .Values.imagePullSecrets }}
24+
imagePullSecrets:
25+
{{- toYaml . | nindent 8 }}
26+
{{- end }}
27+
serviceAccountName: {{ include "fair.serviceAccountName" . }}
28+
{{- with .Values.backend.podSecurityContext }}
29+
securityContext:
30+
{{- toYaml . | nindent 8 }}
31+
{{- end }}
32+
containers:
33+
- name: api
34+
image: {{ include "fair.backend.image" . }}
35+
imagePullPolicy: {{ .Values.image.backend.pullPolicy }}
36+
{{- with .Values.backend.command }}
37+
command:
38+
{{- toYaml . | nindent 12 }}
39+
{{- end }}
40+
ports:
41+
- name: http
42+
containerPort: {{ .Values.backend.port }}
43+
protocol: TCP
44+
envFrom:
45+
- configMapRef:
46+
name: {{ include "fair.backend.fullname" . }}
47+
{{- with .Values.backend.envFrom }}
48+
{{- toYaml . | nindent 12 }}
49+
{{- end }}
50+
env:
51+
- name: DATABASE_PASSWORD
52+
valueFrom:
53+
secretKeyRef:
54+
{{- if .Values.externalDatabase.existingSecret }}
55+
name: {{ .Values.externalDatabase.existingSecret }}
56+
key: {{ .Values.externalDatabase.existingSecretKey }}
57+
{{- else }}
58+
name: {{ include "fair.backend.fullname" . }}-db
59+
key: password
60+
{{- end }}
61+
- name: DATABASE_URL
62+
value: {{ include "fair.databaseUrl" . | quote }}
63+
{{- with .Values.backend.livenessProbe }}
64+
livenessProbe:
65+
{{- toYaml . | nindent 12 }}
66+
{{- end }}
67+
{{- with .Values.backend.readinessProbe }}
68+
readinessProbe:
69+
{{- toYaml . | nindent 12 }}
70+
{{- end }}
71+
{{- with .Values.backend.resources }}
72+
resources:
73+
{{- toYaml . | nindent 12 }}
74+
{{- end }}
75+
{{- with .Values.backend.securityContext }}
76+
securityContext:
77+
{{- toYaml . | nindent 12 }}
78+
{{- end }}
79+
{{- if .Values.backend.djangoQ.enabled }}
80+
- name: django-q
81+
image: {{ include "fair.backend.image" . }}
82+
imagePullPolicy: {{ .Values.image.backend.pullPolicy }}
83+
command:
84+
- python
85+
- manage.py
86+
- qcluster
87+
envFrom:
88+
- configMapRef:
89+
name: {{ include "fair.backend.fullname" . }}
90+
{{- with .Values.backend.envFrom }}
91+
{{- toYaml . | nindent 12 }}
92+
{{- end }}
93+
env:
94+
- name: DATABASE_PASSWORD
95+
valueFrom:
96+
secretKeyRef:
97+
{{- if .Values.externalDatabase.existingSecret }}
98+
name: {{ .Values.externalDatabase.existingSecret }}
99+
key: {{ .Values.externalDatabase.existingSecretKey }}
100+
{{- else }}
101+
name: {{ include "fair.backend.fullname" . }}-db
102+
key: password
103+
{{- end }}
104+
- name: DATABASE_URL
105+
value: {{ include "fair.databaseUrl" . | quote }}
106+
{{- with .Values.backend.djangoQ.resources }}
107+
resources:
108+
{{- toYaml . | nindent 12 }}
109+
{{- end }}
110+
{{- with .Values.backend.securityContext }}
111+
securityContext:
112+
{{- toYaml . | nindent 12 }}
113+
{{- end }}
114+
{{- end }}
115+
{{- with .Values.backend.nodeSelector }}
116+
nodeSelector:
117+
{{- toYaml . | nindent 8 }}
118+
{{- end }}
119+
{{- with .Values.backend.affinity }}
120+
affinity:
121+
{{- toYaml . | nindent 8 }}
122+
{{- end }}
123+
{{- with .Values.backend.tolerations }}
124+
tolerations:
125+
{{- toYaml . | nindent 8 }}
126+
{{- end }}

0 commit comments

Comments
 (0)