Skip to content

Commit 69e4526

Browse files
authored
Merge pull request #116 from bcgov/i5okie/helm-chart
Create tails-server Helm Chart
2 parents eb5be1d + 0cd03e6 commit 69e4526

File tree

14 files changed

+739
-0
lines changed

14 files changed

+739
-0
lines changed

charts/tails-server/.helmignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

charts/tails-server/Chart.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: v2
2+
name: tails-server
3+
description: Indy Tails Server - receive, store and serve Hyperledger Indy tails files
4+
5+
type: application
6+
7+
# This is the chart version. This version number should be incremented each time you make changes
8+
# to the chart and its templates, including the app version.
9+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
10+
version: 0.1.0
11+
12+
# Application version the chart deploys.
13+
appVersion: "1.2.1"
14+
15+
# Minimum Kubernetes version supported by this chart (HPA v2)
16+
kubeVersion: ">=1.23.0-0"
17+
18+
maintainers:
19+
- email: emiliano.sune@quartech.com
20+
name: esune
21+
url: https://github.com/esune
22+
- email: ivan.polchenko@quartech.com
23+
name: i5okie
24+
url: https://github.com/i5okie
25+
26+
keywords:
27+
- indy
28+
- anoncreds
29+
- tails
30+
- revocation
31+
home: https://github.com/bcgov/indy-tails-server
32+
sources:
33+
- https://github.com/bcgov/indy-tails-server

charts/tails-server/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Tails Server Helm Chart
2+
3+
A simple, production‑ready Helm chart to deploy the Indy Tails Server.
4+
5+
- App: <https://github.com/bcgov/indy-tails-server>
6+
- Kubernetes: >= 1.23 (uses autoscaling/v2)
7+
8+
## TL;DR
9+
10+
```bash
11+
# From repo root
12+
helm upgrade --install tails-server charts/tails-server \
13+
--namespace tails --create-namespace
14+
```
15+
16+
## Prerequisites
17+
18+
- Kubernetes >= 1.23
19+
- Metrics server (if enabling HPA)
20+
- A default StorageClass (if enabling persistence without existingClaim)
21+
22+
## Configuration
23+
24+
Values below are the most commonly tuned. See `values.yaml` for the full list.
25+
26+
| Parameter | Default | Description |
27+
|---|---|---|
28+
| replicaCount | 1 | Desired replicas when HPA disabled |
29+
| autoscaling.enabled | false | Enable HorizontalPodAutoscaler |
30+
| autoscaling.minReplicas | 1 | HPA min replicas |
31+
| autoscaling.maxReplicas | 4 | HPA max replicas |
32+
| autoscaling.targetCPUUtilizationPercentage | 80 | HPA CPU target |
33+
| image.repository | ghcr.io/bcgov/tails-server | Image repository |
34+
| image.tag | chart appVersion | Image tag |
35+
| service.type | ClusterIP | Service type |
36+
| service.port | 6543 | Service port (TCP) |
37+
| service.annotations | {} | Extra Service annotations |
38+
| ingress.enabled | false | Create Ingress (OpenShift will auto‑create Route) |
39+
| persistence.enabled | false | Enable persistent storage for tails files |
40+
| persistence.existingClaim | "" | Use an existing PVC name |
41+
| persistence.size | 5Gi | PVC size when creating new |
42+
| persistence.accessModes | [ReadWriteOnce] | PVC access modes |
43+
| persistence.storageClass | "" | StorageClass name |
44+
| persistence.mountPath | /data | Container mount path for storage |
45+
| server.host | 0.0.0.0 | Bind address inside the pod |
46+
| server.port | "" | Override port (defaults to service.port) |
47+
| server.logLevel | WARNING | Log level (e.g., INFO, WARNING, ERROR) |
48+
| livenessProbe | see values.yaml | TCP liveness probe config |
49+
| readinessProbe | see values.yaml | TCP readiness probe config |
50+
| startupProbe | see values.yaml | TCP startup probe config |
51+
| securityContext | {} | Container security context (see example below) |
52+
| serviceAccount.create | true | Create a ServiceAccount |
53+
| serviceAccount.automount | true | Automount SA token (set false to harden) |
54+
| nodeSelector | {} | Node selector |
55+
| tolerations | [] | Tolerations |
56+
| affinity | {} | Affinity/anti‑affinity |
57+
| pdb.enabled | false | Create PodDisruptionBudget when replicas > 1 |
58+
59+
### OpenShift notes
60+
61+
- Prefer using Ingress. OpenShift will create a Route automatically and may terminate TLS at the edge regardless of `ingress.tls` settings.
62+
- For multi‑replica deployments, use RWX storage if your storage class supports it.
63+
64+
Example RWX values (OpenShift NetApp):
65+
66+
```yaml
67+
persistence:
68+
enabled: true
69+
size: 10Gi
70+
accessModes:
71+
- ReadWriteMany
72+
storageClass: netapp-file-standard
73+
mountPath: /data
74+
autoscaling:
75+
enabled: true
76+
minReplicas: 2
77+
maxReplicas: 4
78+
pdb:
79+
enabled: true
80+
minAvailable: 1
81+
```
82+
83+
### Security hardening (example)
84+
85+
```yaml
86+
securityContext:
87+
runAsNonRoot: true
88+
allowPrivilegeEscalation: false
89+
readOnlyRootFilesystem: true
90+
capabilities:
91+
drop: ["ALL"]
92+
serviceAccount:
93+
automount: false
94+
```
95+
96+
## License
97+
98+
Apache-2.0
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
1. Get the application URL:
2+
{{- if .Values.ingress.enabled }}
3+
{{- range $host := .Values.ingress.hosts }}
4+
{{- range .paths }}
5+
{{- /* Scheme depends on your ingress/route configuration; OpenShift Routes may terminate TLS regardless of values */ -}}
6+
http(s)://{{ $host.host }}{{ .path }}
7+
{{- end }}
8+
{{- end }}
9+
{{- else if contains "NodePort" .Values.service.type }}
10+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tails-server.fullname" . }})
11+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
12+
echo http://$NODE_IP:$NODE_PORT
13+
{{- else if contains "LoadBalancer" .Values.service.type }}
14+
echo "Waiting for LoadBalancer IP..."
15+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tails-server.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
16+
echo http://$SERVICE_IP:{{ .Values.service.port }}
17+
{{- else }}
18+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tails-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19+
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20+
echo "Port-forward to test locally:"
21+
echo " kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT"
22+
echo "Then open: http://127.0.0.1:8080/match/test"
23+
{{- end }}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "tails-server.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "tails-server.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "tails-server.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "tails-server.labels" -}}
37+
helm.sh/chart: {{ include "tails-server.chart" . }}
38+
{{ include "tails-server.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "tails-server.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "tails-server.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}
52+
53+
{{/*
54+
Create the name of the service account to use
55+
*/}}
56+
{{- define "tails-server.serviceAccountName" -}}
57+
{{- if .Values.serviceAccount.create }}
58+
{{- default (include "tails-server.fullname" .) .Values.serviceAccount.name }}
59+
{{- else }}
60+
{{- default "default" .Values.serviceAccount.name }}
61+
{{- end }}
62+
{{- end }}
63+
64+
{{/*
65+
PVC name helper
66+
*/}}
67+
{{- define "tails-server.dataPvcName" -}}
68+
{{ include "tails-server.fullname" . }}-data
69+
{{- end }}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "tails-server.fullname" . }}
5+
labels:
6+
{{- include "tails-server.labels" . | nindent 4 }}
7+
spec:
8+
{{- if not .Values.autoscaling.enabled }}
9+
replicas: {{ .Values.replicaCount }}
10+
{{- end }}
11+
revisionHistoryLimit: {{ .Values.revisionHistoryLimit | default 3 }}
12+
strategy:
13+
{{- toYaml .Values.strategy | nindent 2 }}
14+
selector:
15+
matchLabels:
16+
{{- include "tails-server.selectorLabels" . | nindent 6 }}
17+
template:
18+
metadata:
19+
{{- with .Values.podAnnotations }}
20+
annotations:
21+
{{- toYaml . | nindent 8 }}
22+
{{- end }}
23+
labels:
24+
{{- include "tails-server.labels" . | nindent 8 }}
25+
{{- with .Values.podLabels }}
26+
{{- toYaml . | nindent 8 }}
27+
{{- end }}
28+
spec:
29+
{{- with .Values.imagePullSecrets }}
30+
imagePullSecrets:
31+
{{- toYaml . | nindent 8 }}
32+
{{- end }}
33+
serviceAccountName: {{ include "tails-server.serviceAccountName" . }}
34+
{{- if not .Values.serviceAccount.automount }}
35+
automountServiceAccountToken: false
36+
{{- end }}
37+
{{- with .Values.podSecurityContext }}
38+
securityContext:
39+
{{- toYaml . | nindent 8 }}
40+
{{- end }}
41+
{{- with .Values.topologySpreadConstraints }}
42+
topologySpreadConstraints:
43+
{{- toYaml . | nindent 8 }}
44+
{{- end }}
45+
{{- if .Values.priorityClassName }}
46+
priorityClassName: {{ .Values.priorityClassName }}
47+
{{- end }}
48+
containers:
49+
- name: {{ .Chart.Name }}
50+
{{- with .Values.securityContext }}
51+
securityContext:
52+
{{- toYaml . | nindent 12 }}
53+
{{- end }}
54+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
55+
imagePullPolicy: {{ .Values.image.pullPolicy }}
56+
command: ["tails-server"]
57+
ports:
58+
- name: http
59+
containerPort: {{ .Values.service.port }}
60+
protocol: TCP
61+
args:
62+
- "--host"
63+
- {{ .Values.server.host | quote }}
64+
- "--port"
65+
- {{ (default .Values.service.port .Values.server.port) | quote }}
66+
- "--storage-path"
67+
- {{ .Values.persistence.mountPath | quote }}
68+
- "--log-level"
69+
- {{ .Values.server.logLevel | quote }}
70+
{{- if .Values.extraArgs }}
71+
{{- toYaml .Values.extraArgs | nindent 12 }}
72+
{{- end }}
73+
{{- with .Values.extraEnv }}
74+
env:
75+
{{- toYaml . | nindent 12 }}
76+
{{- end }}
77+
{{- with .Values.extraEnvFrom }}
78+
envFrom:
79+
{{- toYaml . | nindent 12 }}
80+
{{- end }}
81+
{{- with .Values.startupProbe }}
82+
startupProbe:
83+
{{- toYaml . | nindent 12 }}
84+
{{- end }}
85+
{{- with .Values.livenessProbe }}
86+
livenessProbe:
87+
{{- toYaml . | nindent 12 }}
88+
{{- end }}
89+
{{- with .Values.readinessProbe }}
90+
readinessProbe:
91+
{{- toYaml . | nindent 12 }}
92+
{{- end }}
93+
{{- with .Values.resources }}
94+
resources:
95+
{{- toYaml . | nindent 12 }}
96+
{{- end }}
97+
volumeMounts:
98+
- name: data
99+
mountPath: {{ .Values.persistence.mountPath }}
100+
volumes:
101+
- name: data
102+
{{- if .Values.persistence.enabled }}
103+
persistentVolumeClaim:
104+
claimName: {{ .Values.persistence.existingClaim | default (include "tails-server.dataPvcName" .) }}
105+
{{- else }}
106+
emptyDir: {}
107+
{{- end }}
108+
{{- with .Values.nodeSelector }}
109+
nodeSelector:
110+
{{- toYaml . | nindent 8 }}
111+
{{- end }}
112+
{{- with .Values.affinity }}
113+
affinity:
114+
{{- toYaml . | nindent 8 }}
115+
{{- end }}
116+
{{- with .Values.tolerations }}
117+
tolerations:
118+
{{- toYaml . | nindent 8 }}
119+
{{- end }}

0 commit comments

Comments
 (0)