add loop helm chart

This commit is contained in:
2025-07-22 09:25:50 +03:00
parent c00d31ebc4
commit 82b4aee59d
18 changed files with 1909 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.loopApp.ingress.enabled }}
{{- range $host := .Values.loopApp.ingress.hosts }}
http://{{ $host }}
{{- end}}
{{- else if contains "NodePort" .Values.loopApp.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.loopApp.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ include "fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.loopApp.service.externalPort }}
{{- else if contains "ClusterIP" .Values.loopApp.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "loop-enterprise-edition.name" . }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.loopApp.service.externalPort }}
{{- end }}

View File

@@ -0,0 +1,61 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "loop-enterprise-edition.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "loop-enterprise-edition.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "loop-enterprise-edition.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a fully qualified jobserver name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "loop-enterprise-edition.jobserver.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s-%s" .Release.Name $name .Values.global.features.jobserver.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for ingress. Based on
1) Helm Version (.Capabilities has been changed in v3)
2) Kubernetes Version
*/}}
{{- define "loop-enterprise-edition.ingress.apiVersion" -}}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}}
"networking.k8s.io/v1"
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
"networking.k8s.io/v1beta1"
{{- else -}}
"extensions/v1beta1"
{{- end -}}
{{- end -}}
{{- define "loop-enterprise-edition.deployment.apiVersion" -}}
"apps/v1"
{{- end -}}

View File

@@ -0,0 +1,157 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
{{- if not .Values.loopApp.autoscaling.enabled }}
replicas: {{ .Values.loopApp.replicaCount }}
{{- end }}
{{- with .Values.loopApp.strategy }}
strategy:
{{- . | toYaml | nindent 4 }}
{{- end }}
revisionHistoryLimit: {{ .Values.loopApp.revisionHistoryLimit }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.loopApp.service.metricsPort }}"
prometheus.io/path: "/metrics"
{{- if .Values.loopApp.extraPodAnnotations }}
{{- .Values.loopApp.extraPodAnnotations | toYaml | nindent 8 }}
{{- end }}
spec:
{{- if .Values.loopApp.nodeSelector }}
nodeSelector:
{{- toYaml .Values.loopApp.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.loopApp.affinity }}
affinity:
{{- toYaml .Values.loopApp.affinity | nindent 8 }}
{{- end }}
{{- if .Values.loopApp.tolerations }}
tolerations:
{{- toYaml .Values.loopApp.tolerations | nindent 6 }}
{{- end }}
{{- if .Values.loopApp.securityContext }}
securityContext:
{{- toYaml .Values.loopApp.securityContext | nindent 8 }}
{{- end }}
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }}
initContainers:
{{- if .Values.loopApp.extraInitContainers }}
{{- .Values.loopApp.extraInitContainers | toYaml | nindent 6 }}
{{- end }}
containers:
- name: {{ include "loop-enterprise-edition.name" . }}
image: "{{ .Values.loopApp.image.repository }}:{{ .Values.loopApp.image.tag }}"
imagePullPolicy: {{ .Values.loopApp.image.pullPolicy }}
ports:
- containerPort: {{ .Values.loopApp.service.internalPort }}
name: api
- containerPort: {{ .Values.loopApp.service.metricsPort }}
name: metrics
- containerPort: {{ .Values.loopApp.service.clusterPort }}
name: cluster
- containerPort: {{ .Values.loopApp.service.gossipPort }}
name: gossip
env:
- name: MM_CONFIG
valueFrom:
secretKeyRef:
{{- if .Values.global.features.database.existingDatabaseSecret }}
name: {{ .Values.global.features.database.existingDatabaseSecret.name }}
key: {{ .Values.global.features.database.existingDatabaseSecret.key }}
{{- else }}
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
key: loop.dbsecret
{{- end }}
- name: MM_SERVICESETTINGS_LICENSEFILELOCATION
value: {{ printf "/mattermost/%s" (.Values.global.existingLicenseSecret | default "loop.loop-license") | squote }}
- name: MM_SERVICESETTINGS_SITEURL
value: "{{ .Values.global.siteUrl }}"
- name: MM_SERVICESETTINGS_LISTENADDRESS
value: ":{{ .Values.loopApp.service.internalPort }}"
- name: MM_SERVICESETTINGS_ENABLELINKPREVIEWS
value: "{{ .Values.global.enableLinkPreviews }}"
- name: MM_SERVICESETTINGS_ENABLECUSTOMEMOJI
value: "{{ .Values.global.enableCustomEmoji }}"
{{- if .Values.global.features.jobserver.enabled }}
- name: MM_JOBSETTINGS_RUNJOBS
value: "false"
- name: MM_JOBSETTINGS_RUNSCHEDULER
value: "false"
{{- end }}
{{- with .Values.loopApp.extraEnv }}
{{ toYaml . | indent 8 }}
{{- end }}
livenessProbe:
initialDelaySeconds: 90
timeoutSeconds: 5
periodSeconds: 15
httpGet:
path: /api/v4/system/ping
port: {{ .Values.loopApp.service.internalPort }}
readinessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
periodSeconds: 15
httpGet:
path: /api/v4/system/ping
port: {{ .Values.loopApp.service.internalPort }}
volumeMounts:
{{- if .Values.global.existingLicenseSecret.name }}
- mountPath: /mattermost/{{.Values.global.existingLicenseSecret.key }}
name: loop-license
subPath: {{.Values.global.existingLicenseSecret.key }}
{{- else }}
- mountPath: /mattermost/loop.loop-license
name: loop-license
subPath: loop.loop-license
{{- end }}
- mountPath: /mattermost/plugins/
name: loop-plugins
- mountPath: /mattermost/client/plugins/
name: loop-plugins-client
{{- if .Values.loopApp.extraVolumeMounts }}
{{ toYaml .Values.loopApp.extraVolumeMounts | indent 8 }}
{{- end }}
resources:
{{ toYaml .Values.loopApp.resources | indent 10 }}
volumes:
- name: loop-plugins
emptyDir: {}
- name: loop-plugins-client
emptyDir: {}
- name: loop-config
emptyDir: {}
- name: loop-license
secret:
{{- if .Values.global.existingLicenseSecret.name }}
secretName: {{ .Values.global.existingLicenseSecret.name }}
{{- else }}
secretName: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
{{- end }}
{{- if .Values.loopApp.extraVolumes }}
{{ toYaml .Values.loopApp.extraVolumes | indent 6 }}
{{- end }}

View File

@@ -0,0 +1,31 @@
{{- if .Values.loopApp.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
name: {{ include "loop-enterprise-edition.fullname" . }}
spec:
scaleTargetRef:
apiVersion: {{ template "loop-enterprise-edition.deployment.apiVersion" . }}
kind: Deployment
name: {{ include "loop-enterprise-edition.fullname" . }}
minReplicas: {{ .Values.loopApp.autoscaling.minReplicas }}
maxReplicas: {{ .Values.loopApp.autoscaling.maxReplicas }}
metrics:
{{- with .Values.loopApp.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ . }}
{{- end }}
{{- with .Values.loopApp.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ . }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,101 @@
{{- if .Values.global.features.jobserver.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "loop-enterprise-edition.jobserver.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
replicas: {{ .Values.global.features.jobserver.replicaCount }}
{{- with .Values.global.features.jobserver.strategy }}
strategy:
{{- . | toYaml | nindent 4 }}
{{- end }}
revisionHistoryLimit: {{ .Values.global.features.jobserver.revisionHistoryLimit }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
{{- if .Values.loopApp.extraPodAnnotations }}
{{- .Values.loopApp.extraPodAnnotations | toYaml | nindent 8 }}
{{- end }}
spec:
{{- if .Values.global.features.jobserver.nodeSelector }}
nodeSelector:
{{- toYaml .Values.global.features.jobserver.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.global.features.jobserver.affinity }}
affinity:
{{- toYaml .Values.global.features.jobserver.affinity | nindent 8 }}
{{- end }}
{{- if .Values.global.features.jobserver.tolerations }}
tolerations:
{{- toYaml .Values.global.features.jobserver.tolerations | nindent 6 }}
{{- end }}
{{- if .Values.loopApp.securityContext }}
securityContext:
{{- toYaml .Values.loopApp.securityContext | nindent 8 }}
{{- end }}
initContainers:
- name: "init-loop-app"
image: "{{ .Values.initContainerImage.repository }}:{{ .Values.initContainerImage.tag }}"
imagePullPolicy: {{ .Values.initContainerImage.imagePullPolicy }}
command: [
"sh",
"-c",
"until curl --max-time 5 http://{{ include "loop-enterprise-edition.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.loopApp.service.internalPort }}/api/v4/system/ping ; do echo waiting for LOOP App come up; sleep 5; done; echo init-loop-app finished"
]
containers:
- name: {{ include "loop-enterprise-edition.name" . }}-jobserver
image: "{{ .Values.loopApp.image.repository }}:{{ .Values.loopApp.image.tag }}"
imagePullPolicy: {{ .Values.loopApp.image.pullPolicy }}
command: ["mattermost", "jobserver"]
env:
- name: MM_CONFIG
valueFrom:
secretKeyRef:
{{- if .Values.global.features.database.existingDatabaseSecret }}
name: {{ .Values.global.features.database.existingDatabaseSecret.name }}
key: {{ .Values.global.features.database.existingDatabaseSecret.key }}
{{- else }}
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
key: loop.dbsecret
{{- end }}
{{- with .Values.global.features.jobserver.extraEnv }}
{{- toYaml . | nindent 8 }}
{{- end }}
volumeMounts:
{{- if .Values.global.existingLicenseSecret.name }}
- mountPath: /mattermost/{{.Values.global.existingLicenseSecret.key }}
name: loop-license
subPath: {{.Values.global.existingLicenseSecret.key }}
{{- else }}
- mountPath: /mattermost/loop.loop-license
name: loop-license
subPath: loop.loop-license
{{- end }}
volumes:
- name: loop-license
secret:
{{- if .Values.global.existingLicenseSecret.name }}
secretName: {{ .Values.global.existingLicenseSecret.name }}
{{- else }}
secretName: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
{{- end }}
{{- end -}}

View File

@@ -0,0 +1,50 @@
{{- if .Values.loopApp.ingress.enabled -}}
{{- $serviceName := include "loop-enterprise-edition.fullname" . -}}
{{- $servicePort := .Values.loopApp.service.externalPort -}}
apiVersion: {{ include "loop-enterprise-edition.ingress.apiVersion" . }}
kind: Ingress
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
{{- if .Values.loopApp.ingress.annotations }}
{{ toYaml .Values.loopApp.ingress.annotations | indent 4 }}
{{- end }}
{{- if .Values.loopApp.ingress.tls }}
kubernetes.io/tls-acme: 'true'
nginx.ingress.kubernetes.io/ssl-redirect: "true"
{{- else }}
nginx.ingress.kubernetes.io/ssl-redirect: "false"
{{- end }}
spec:
{{- if .Values.loopApp.ingress.ingressClassName }}
ingressClassName: {{ .Values.loopApp.ingress.ingressClassName }}
{{- end }}
rules:
{{- range $host := .Values.loopApp.ingress.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
backend:
service:
name: {{ $serviceName }}
port:
number: {{ $servicePort }}
pathType: Prefix
{{- else }}
backend:
serviceName: {{ $serviceName }}
servicePort: {{ $servicePort }}
{{- end }}
{{- end -}}
{{- if .Values.loopApp.ingress.tls }}
tls:
{{ toYaml .Values.loopApp.ingress.tls | indent 4 }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,14 @@
{{- if not .Values.global.features.database.existingDatabaseSecret.name }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
type: Opaque
data:
loop.dbsecret: {{ tpl "{{ .Values.global.features.database.external.driver }}://{{ .Values.global.features.database.external.dataSource }}" . | b64enc }}
{{- end }}

View File

@@ -0,0 +1,14 @@
{{- if not .Values.global.existingLicenseSecret.name }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
type: Opaque
data:
loop.loop-license: {{ .Values.global.loopLicense | b64enc | quote }}
{{- end -}}

View File

@@ -0,0 +1,17 @@
{{ if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccount.name }}
namespace: {{ .Release.Namespace }}
{{- if .Values.serviceAccount.annotations }}
annotations:
{{ tpl (toYaml .Values.serviceAccount.annotations) . | indent 4 }}
{{- end }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
{{ end }}

View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
selector:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
type: {{ .Values.loopApp.service.type }}
ports:
- port: {{ .Values.loopApp.service.externalPort }}
targetPort: {{ .Values.loopApp.service.internalPort }}
protocol: TCP
name: {{ .Values.loopApp.service.name }}
- port: {{ .Values.loopApp.service.metricsPort }}
targetPort: {{ .Values.loopApp.service.metricsPort }}
protocol: TCP
name: {{ .Values.loopApp.service.metricsName }}