Skip to content

Commit 2344463

Browse files
committed
feat: add xapi-lrs chart
1 parent 8479e02 commit 2344463

19 files changed

Lines changed: 1289 additions & 1 deletion

.release-please-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
"charts/nodejs": "0.3.2",
1111
"charts/pachyderm": "0.3.0",
1212
"charts/postgrest": "0.7.0",
13-
"charts/openldap": "1.3.0"
13+
"charts/openldap": "1.3.0",
14+
"charts/xapi-lrs": "0.1.0"
1415
}

charts/xapi-lrs/.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/xapi-lrs/Chart.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apiVersion: v2
2+
name: xapi-lrs
3+
description: xapi-lrs Learning Record Store helm chart
4+
type: application
5+
home: https://github.com/pelotech/xapi-lrs
6+
sources:
7+
- https://github.com/pelotech/xapi-lrs
8+
keywords:
9+
- xapi
10+
- lrs
11+
- learning-record-store
12+
- tin-can
13+
14+
# This is the chart version. This version number should be incremented each time you make changes
15+
# to the chart and its templates, including the app version.
16+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
17+
version: 0.1.0
18+
19+
# This is the version number of the application being deployed. This version number should be
20+
# incremented each time you make changes to the application. Versions are not expected to
21+
# follow Semantic Versioning. They should reflect the version the application is using.
22+
# It is recommended to use it with quotes.
23+
appVersion: "v0.5.1"

charts/xapi-lrs/README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# xapi-lrs
2+
3+
### version: 0.1.0<!-- x-release-please-version -->
4+
5+
![AppVersion: v0.5.1](https://img.shields.io/badge/AppVersion-v0.5.1-informational?style=flat-square)
6+
7+
xapi-lrs Learning Record Store helm chart
8+
9+
**Homepage:** <https://github.com/pelotech/xapi-lrs>
10+
11+
## Values
12+
13+
| Key | Type | Default | Description |
14+
|-----|------|---------|-------------|
15+
| affinity | object | `{}` | |
16+
| auth.adminPassword | string | `""` | |
17+
| auth.adminUser | string | `"admin"` | |
18+
| auth.apiKey | string | `""` | |
19+
| auth.apiSecret | string | `""` | |
20+
| auth.existingSecret | string | `""` | |
21+
| auth.existingSecretKeys.adminPassword | string | `"admin-password"` | |
22+
| auth.existingSecretKeys.apiSecret | string | `"api-secret"` | |
23+
| auth.existingSecretKeys.migrationPostgresqlPassword | string | `"migration-postgresql-password"` | |
24+
| auth.existingSecretKeys.postgresqlPassword | string | `"postgresql-password"` | |
25+
| auth.existingSecretKeys.sessionSecret | string | `"session-secret"` | |
26+
| auth.sessionSecret | string | `""` | |
27+
| automountServiceAccountToken | bool | `false` | |
28+
| commonAnnotations | object | `{}` | |
29+
| containerPorts.admin | int | `8091` | |
30+
| containerPorts.http | int | `8081` | |
31+
| cors.enabled | bool | `false` | |
32+
| cors.origin | string | `""` | |
33+
| database.driver | string | `"pg"` | |
34+
| database.postgresql.database | string | `"xapi_lrs"` | |
35+
| database.postgresql.host | string | `"postgres"` | |
36+
| database.postgresql.password | string | `""` | |
37+
| database.postgresql.port | int | `5432` | |
38+
| database.postgresql.url | string | `""` | |
39+
| database.postgresql.user | string | `"xapi_lrs"` | |
40+
| extraEnvVars | list | `[]` | |
41+
| extraEnvVarsCM | string | `""` | |
42+
| extraEnvVarsSecret | string | `""` | |
43+
| fullnameOverride | string | `""` | |
44+
| gateway.enabled | bool | `false` | |
45+
| gateway.hostname | string | `""` | |
46+
| gateway.httpRoute.annotations | object | `{}` | |
47+
| gateway.httpRoute.extraHostnames | list | `[]` | |
48+
| gateway.httpRoute.gatewayName | string | `""` | |
49+
| gateway.httpRoute.gatewayNamespace | string | `""` | |
50+
| gateway.httpRoute.path | string | `"/"` | |
51+
| gateway.httpRoute.pathType | string | `"PathPrefix"` | |
52+
| gateway.httpRoute.port | string | `""` | |
53+
| gateway.httpRoute.rules | list | `[]` | |
54+
| gateway.httpRoute.sectionName | string | `""` | |
55+
| gateway.listenerSet.annotations | object | `{}` | |
56+
| gateway.listenerSet.enabled | bool | `false` | |
57+
| gateway.listenerSet.gatewayName | string | `""` | |
58+
| gateway.listenerSet.gatewayNamespace | string | `""` | |
59+
| gateway.listenerSet.https.enabled | bool | `true` | |
60+
| gateway.listenerSet.https.secretName | string | `""` | |
61+
| gateway.listenerSet.listeners | list | `[]` | |
62+
| image.digest | string | `"sha256:58cbb3c5a6802773df92be0bd7f900731163ca6d3b2a6ad4385a945c05d78769"` | |
63+
| image.pullPolicy | string | `"IfNotPresent"` | |
64+
| image.pullSecrets | list | `[]` | |
65+
| image.registry | string | `"ghcr.io"` | |
66+
| image.repository | string | `"pelotech/xapi-lrs"` | |
67+
| image.tag | string | `"v0.5.1"` | |
68+
| ingress.annotations | object | `{}` | |
69+
| ingress.enabled | bool | `false` | |
70+
| ingress.extraHosts | list | `[]` | |
71+
| ingress.extraPaths | list | `[]` | |
72+
| ingress.extraTls | list | `[]` | |
73+
| ingress.hostname | string | `"xapi-lrs.local"` | |
74+
| ingress.ingressClassName | string | `""` | |
75+
| ingress.path | string | `"/"` | |
76+
| ingress.pathType | string | `"ImplementationSpecific"` | |
77+
| ingress.tls | bool | `false` | |
78+
| livenessProbe.httpGet.path | string | `"/healthz"` | |
79+
| livenessProbe.httpGet.port | string | `"admin"` | |
80+
| livenessProbe.initialDelaySeconds | int | `10` | |
81+
| livenessProbe.periodSeconds | int | `10` | |
82+
| logLevel | string | `"info"` | |
83+
| metrics.serviceMonitor.enabled | bool | `false` | |
84+
| metrics.serviceMonitor.honorLabels | bool | `false` | |
85+
| metrics.serviceMonitor.interval | string | `"30s"` | |
86+
| metrics.serviceMonitor.labels | object | `{}` | |
87+
| metrics.serviceMonitor.metricRelabelings | list | `[]` | |
88+
| metrics.serviceMonitor.namespace | string | `""` | |
89+
| metrics.serviceMonitor.path | string | `"/metrics"` | |
90+
| metrics.serviceMonitor.relabelings | list | `[]` | |
91+
| metrics.serviceMonitor.scrapeTimeout | string | `""` | |
92+
| migration.enabled | bool | `true` | |
93+
| migration.postgresql.password | string | `""` | |
94+
| migration.postgresql.url | string | `""` | |
95+
| migration.postgresql.user | string | `""` | |
96+
| nameOverride | string | `""` | |
97+
| nodeEnv | string | `"production"` | |
98+
| nodeSelector | object | `{}` | |
99+
| persistence.accessModes[0] | string | `"ReadWriteOnce"` | |
100+
| persistence.annotations | object | `{}` | |
101+
| persistence.enabled | bool | `false` | |
102+
| persistence.existingClaim | string | `""` | |
103+
| persistence.mountPath | string | `"/data/pglite"` | |
104+
| persistence.size | string | `"8Gi"` | |
105+
| persistence.storageClass | string | `""` | |
106+
| podAnnotations | object | `{}` | |
107+
| podLabels | object | `{}` | |
108+
| podSecurityContext | object | `{}` | |
109+
| readinessProbe.httpGet.path | string | `"/readyz"` | |
110+
| readinessProbe.httpGet.port | string | `"admin"` | |
111+
| readinessProbe.initialDelaySeconds | int | `5` | |
112+
| readinessProbe.periodSeconds | int | `10` | |
113+
| replicaCount | int | `1` | |
114+
| resources | object | `{}` | |
115+
| securityContext | object | `{}` | |
116+
| service.nodePorts.admin | string | `""` | |
117+
| service.nodePorts.http | string | `""` | |
118+
| service.ports.admin | int | `8091` | |
119+
| service.ports.http | int | `80` | |
120+
| service.targetPort.admin | string | `"admin"` | |
121+
| service.targetPort.http | string | `"http"` | |
122+
| service.type | string | `"ClusterIP"` | |
123+
| serviceAccount.annotations | object | `{}` | |
124+
| serviceAccount.create | bool | `true` | |
125+
| serviceAccount.name | string | `""` | |
126+
| tolerations | list | `[]` | |
127+
| updateStrategy | object | `{}` | |
128+
| volumeMounts | list | `[]` | |
129+
| volumes | list | `[]` | |
130+

charts/xapi-lrs/README.md.gotmpl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{ template "chart.header" . }}
2+
3+
### version: {{ template "chart.version" . }}<!-- x-release-please-version -->
4+
5+
{{ template "chart.appVersionBadge" . }}
6+
7+
{{ template "chart.description" . }}
8+
9+
{{ template "chart.homepageLine" . }}
10+
11+
{{ template "chart.requirementsSection" . }}
12+
13+
{{ template "chart.valuesSection" . }}
14+
15+
{{ template "helm-docs.versionFooter" . }}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
1. Get the xAPI endpoint:
2+
{{- if .Values.ingress.enabled }}
3+
http{{ if .Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.hostname }}{{ .Values.ingress.path }}
4+
{{- else if and .Values.gateway.enabled .Values.gateway.hostname }}
5+
http{{ if .Values.gateway.listenerSet.enabled }}s{{ end }}://{{ .Values.gateway.hostname }}{{ .Values.gateway.httpRoute.path }}
6+
{{- else if contains "NodePort" .Values.service.type }}
7+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[?(@.name==\"http\")].nodePort}" services {{ include "xapi-lrs.fullname" . }})
8+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
9+
echo "http://$NODE_IP:$NODE_PORT"
10+
{{- else if contains "LoadBalancer" .Values.service.type }}
11+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
12+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "xapi-lrs.fullname" . }} -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
13+
echo "http://$SERVICE_IP:{{ .Values.service.ports.http }}"
14+
{{- else }}
15+
kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "xapi-lrs.fullname" . }} 8081:{{ .Values.service.ports.http }}
16+
# http://127.0.0.1:8081
17+
{{- end }}
18+
19+
The admin UI is served under /admin on that same endpoint.
20+
21+
2. The admin port serves unauthenticated /healthz, /readyz, and /metrics, and is
22+
not routed externally. Reach it with a port-forward:
23+
24+
kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "xapi-lrs.fullname" . }} 8091:{{ .Values.service.ports.admin }}
25+
# http://127.0.0.1:8091/metrics
26+
27+
{{- if eq .Values.database.driver "pglite" }}
28+
29+
Running in PGlite mode (embedded, single-connection); not recommended for production.
30+
{{- if not .Values.persistence.enabled }}
31+
Data is in an emptyDir and will not survive a pod restart; set persistence.enabled=true
32+
to keep it on a PVC.
33+
{{- end }}
34+
{{- end }}
35+
36+
{{- if and (not .Values.auth.sessionSecret) (not .Values.auth.existingSecret) }}
37+
38+
The admin session secret was generated automatically. It stays stable across
39+
`helm upgrade`, but not under GitOps tools that render with `helm template`
40+
(Argo CD, Flux), where it is regenerated on every sync. For those, set
41+
auth.sessionSecret or auth.existingSecret.
42+
{{- end }}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{{/* vim: set filetype=mustache: */}}
2+
3+
{{/*
4+
Expand the name of the chart.
5+
*/}}
6+
{{- define "xapi-lrs.name" -}}
7+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
8+
{{- end }}
9+
10+
{{/*
11+
Create a default fully qualified app name.
12+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
13+
If release name contains chart name it will be used as a full name.
14+
*/}}
15+
{{- define "xapi-lrs.fullname" -}}
16+
{{- if .Values.fullnameOverride }}
17+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
18+
{{- else }}
19+
{{- $name := default .Chart.Name .Values.nameOverride }}
20+
{{- if contains $name .Release.Name }}
21+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
22+
{{- else }}
23+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
24+
{{- end }}
25+
{{- end }}
26+
{{- end }}
27+
28+
{{/*
29+
Create chart name and version as used by the chart label.
30+
*/}}
31+
{{- define "xapi-lrs.chart" -}}
32+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
33+
{{- end }}
34+
35+
{{/*
36+
Common labels
37+
*/}}
38+
{{- define "xapi-lrs.labels" -}}
39+
helm.sh/chart: {{ include "xapi-lrs.chart" . }}
40+
{{ include "xapi-lrs.selectorLabels" . }}
41+
{{- if .Chart.AppVersion }}
42+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
43+
{{- end }}
44+
app.kubernetes.io/component: lrs
45+
app.kubernetes.io/managed-by: {{ .Release.Service }}
46+
{{- end }}
47+
48+
{{/*
49+
Selector labels
50+
*/}}
51+
{{- define "xapi-lrs.selectorLabels" -}}
52+
app.kubernetes.io/name: {{ include "xapi-lrs.name" . }}
53+
app.kubernetes.io/instance: {{ .Release.Name }}
54+
{{- end }}
55+
56+
{{/*
57+
Create the name of the service account to use
58+
*/}}
59+
{{- define "xapi-lrs.serviceAccountName" -}}
60+
{{- if .Values.serviceAccount.create }}
61+
{{- default (include "xapi-lrs.fullname" .) .Values.serviceAccount.name }}
62+
{{- else }}
63+
{{- default "default" .Values.serviceAccount.name }}
64+
{{- end }}
65+
{{- end }}
66+
67+
{{/*
68+
Return the proper xapi-lrs image name
69+
*/}}
70+
{{- define "xapi-lrs.image" -}}
71+
{{- $registry := .Values.image.registry -}}
72+
{{- $repository := .Values.image.repository -}}
73+
{{- $ref := ternary (printf "@%s" .Values.image.digest) (printf ":%s" .Values.image.tag) (not (empty .Values.image.digest)) -}}
74+
{{- if $registry -}}
75+
{{- printf "%s/%s%s" $registry $repository $ref -}}
76+
{{- else -}}
77+
{{- printf "%s%s" $repository $ref -}}
78+
{{- end -}}
79+
{{- end -}}
80+
81+
{{/*
82+
Return the name of the Secret holding the sensitive values
83+
*/}}
84+
{{- define "xapi-lrs.secretName" -}}
85+
{{- default (include "xapi-lrs.fullname" .) .Values.auth.existingSecret -}}
86+
{{- end -}}
87+
88+
{{/*
89+
Return auth.sessionSecret if set, else the value already in the chart Secret
90+
(so upgrades don't rotate it), else a new random value.
91+
*/}}
92+
{{- define "xapi-lrs.sessionSecret" -}}
93+
{{- if .Values.auth.sessionSecret -}}
94+
{{- .Values.auth.sessionSecret -}}
95+
{{- else -}}
96+
{{- $existing := lookup "v1" "Secret" .Release.Namespace (include "xapi-lrs.fullname" .) -}}
97+
{{- if and $existing $existing.data (index $existing.data "session-secret") -}}
98+
{{- index $existing.data "session-secret" | b64dec -}}
99+
{{- else -}}
100+
{{- randAlphaNum 64 -}}
101+
{{- end -}}
102+
{{- end -}}
103+
{{- end -}}

0 commit comments

Comments
 (0)