WIP: Loop-ENT-charts-refactor #6

Draft
stuart.armstrong wants to merge 8 commits from Loop-ENT-charts-refactor into 2.0.0
53 changed files with 2889 additions and 1037 deletions

View File

@ -15,4 +15,8 @@ maintainers:
name: stuart.armstrong
name: loop-enterprise-edition
type: application
version: 1.0.2
version: 2.0.0
dependencies:
- name: common
repository: file://subcharts/common
version: 1.10.4

View File

@ -1,7 +1,9 @@
---
## ⚠️ **This chart is not compatible with the previous loop chart and should not be used to upgrade the previous chart. All data can be lost**
## ⚠️ **Version 2.0.0 of the loop-enterprise-editon chart, has been recreated to make it more streamlined and the usage clearer. The old chart can still be used.
---
## ⚠️ **This chart is not compatible with the previous loop chart 1.x.x and should not be used to upgrade from the previous chart. All data can be lost. The migration procedure will provided in due course.**
## ⚠️ **Этот chart несовместим с предыдущим loop chart, и не должн использоваться для обновления предыдущей версии loop chart. Все данные могут быть потеряны
## ⚠️ **Этот chart несовместим с предыдущим loop chart 1.x.x, и не должн использоваться для обновления предыдущей версии loop chart. Все данные могут быть потеряны
---
Loop Enterprise Edition Helm Chart
@ -21,13 +23,10 @@ https://kubernetes.io/docs/setup/pick-right-solution/
See: https://docs.helm.sh/using_helm/#quickstart
We recommend installing Helm v2.13.1 or later.
Once Helm is installed and initialized, run the following:
```bash
helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
helm repo add incubator https://charts.helm.sh/incubator
```
## 1.3 Ingress
@ -60,8 +59,7 @@ A Minio server or ay S3 compatible object storage is required for this chart. Th
At minimum the following settings must be updated:
* `global.siteURL` - set this to the URL your users will use to access Loop, e.g. `https://loop.example.com`
* `global.loopLicense` - set this to the contents of your license file or provide an existing secret. `global.existingLicenseSecret.name` `global.existingLicenseSecret.key`
* `MM_SERVICESETTINGS_SITEURL:` - set this to the URL your users will use to access Loop, e.g. `https://loop.example.com`
* 2.4.1 Database Configuration
* 2.6.1 Object Storage Configuration
@ -71,11 +69,11 @@ Without these settings, Loop will not run correctly.
To set the Loop application version, update:
* `loopApp.image.tag` - set this to the Loop server version you wish to install (e.g. `7.10.2`)
* `image.tag` - set this to the Loop server version you wish to install (e.g. `2025-06-10-1`)
## 2.3 Ingress
If you are using nginx-ingress, set the following under `loopApp`:
If you are using nginx-ingress, set the following:
```yaml
ingress:
@ -84,13 +82,13 @@ ingress:
- loop.example.com
```
where `loop.example.com` is your domain name and matches `global.siteURL`.
where `loop.example.com` is your domain name and matches `MM_SERVICESETTINGS_SITEURL:`.
### 2.3.1 HTTPS
To run with HTTPS, add an SSL/TLS certificate as a secret to your Kubernetes cluster, either manually or [using cert-manager](#14-certificate-manager).
Set the following under `loopApp` to enable HTTPS:
Set the following to enable HTTPS:
```yaml
ingress:
@ -121,10 +119,9 @@ For database configuration, use an external database not managed by the Loop Hel
### 2.4.1 Database Configuration
* Set `global.features.database.external.driver` to `postgres`
* Set `global.features.database.external.dataSource` to your master DB connection string
* (Optional) Set `global.features.database.external.dataSourceReplicas` to an array of read replica connection strings
* (Optional) use an external provided secret. `global.features.database.external.existingDatabaseSecret.name` `global.features.database.external.existingDatabaseSecret.key`.
* Set `MM_SQLSETTINGS_DRIVERNAME:` to `postgres`
* Set `MM_SQLSETTINGS_DATASOURCE:`, in secret-values.yaml, to your master DB connection string
* (Optional) Set `MM_SQLSETTINGS_DATASOURCEREPLICAS:` to an array of read replica connection strings
## 2.5 Push Notifications
@ -161,7 +158,7 @@ $ helm install <name> loop/loop-enterprise-edition --version <version_number>
For example:
```bash
$ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
$ helm install <name> loop/loop-enterprise-edition --version v0.8.2
$ helm install <name> loop/loop-enterprise-edition --version 1.0.2
```
If no Helm Chart version is specified the latest version will be installed.
@ -235,7 +232,6 @@ https://kubernetes.io/ru/docs/setup/production-environment/
```bash
helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
helm repo add incubator https://charts.helm.sh/incubator
```
## 1.3 Настройка Ingress
@ -288,12 +284,9 @@ kubectl create secret tls your-tls-secret --cert=path/to/cert.crt --key=path/to/
**Минимально необходимые настройки:**
- `global.siteURL` — установите в адрес, по которому пользователи будут получать доступ к Loop, например:
- `MM_SERVICESETTINGS_SITEURL:` — установите в адрес, по которому пользователи будут получать доступ к Loop, например:
`https://loop.example.com`
- `global.loopLicense` — укажите содержимое лицензионного файла **или** задайте существующий Kubernetes-секрет с ключом:
`global.existingLicenseSecret.name` и `global.existingLicenseSecret.key`
- **Конфигурация базы данных (раздел 2.4.1)** — необходимо настроить подключение к PostgreSQL.
Это может быть внешний сервер или встроенный через Helm-чарт.
@ -305,16 +298,15 @@ kubectl create secret tls your-tls-secret --cert=path/to/cert.crt --key=path/to/
Укажите тег образа Loop:
```yaml
loopApp:
image:
tag: "7.10.2" # Например, 7.10.2
image:
tag: "2025-11-20-1" # Например, 2025-11-20-1
```
## 2.3 Настройка Ingress
### **2.3.1 Ingress с nginx ingress controller**
Если вы используете `nginx-ingress-controller`, добавьте следующее в секцию `loopApp`:
Если вы используете `nginx-ingress-controller`, добавьте следующее:
```yaml
ingress:
@ -323,7 +315,7 @@ ingress:
- loop.example.com
```
Здесь `loop.example.com` — это ваш домен, и он **должен совпадать с `global.siteURL`** (например, `https://loop.example.com`).
Здесь `loop.example.com` — это ваш домен, и он **должен совпадать с `MM_SERVICESETTINGS_SITEURL:`** (например, `https://loop.example.com`).
### **2.3.2 HTTPS (SSL/TLS)**
@ -365,10 +357,10 @@ ingress:
#### Обязательные параметры:
* `global.features.database.external.driver` → **установите в** `"postgres"`
* `MM_SQLSETTINGS_DRIVERNAME:` → **установите в** `"postgres"`
Это означает, что будет использоваться PostgreSQL как драйвер СУБД.
* `global.features.database.external.dataSource` → **укажите строку подключения к мастер-базе**
* `MM_SQLSETTINGS_DATASOURCE:` → **укажите строку подключения к мастер-базе**
Пример строки подключения:
```
@ -377,14 +369,9 @@ ingress:
#### Необязательные параметры:
* `global.features.database.external.dataSourceReplicas`
* `MM_SQLSETTINGS_DATASOURCEREPLICAS:`
Список строк подключения к **репликам** БД (если они используются для масштабирования чтения).
* `global.features.database.external.existingDatabaseSecret.name`
`global.features.database.external.existingDatabaseSecret.key`
Если вы не хотите **хранить логины/пароли в открытом виде**, можно использовать Kubernetes Secret, где будут зашиты данные подключения.
## 2.5 Push-уведомления
**По умолчанию**: Используется встроенная служба HPNS (High-Performance Notification Service).
@ -415,7 +402,7 @@ helm upgrade --install loop-ee loop/loop-enterprise-edition \
**Установка конкретной версии**:
```bash
helm upgrade --install loop-ee loop/loop-enterprise-edition \
--version 0.8.2 \
--version 1.0.2 \
--namespace loop --create-namespace
```

View File

@ -0,0 +1,9 @@
secrets:
secrets:
stringData:
MM_SQLSETTINGS_DATASOURCE: "postgres://<dbuser>:<dbpassword>@<postresql-server>:5432/loopdb?sslmode=disable" #Data source for connecting to the PostgreSQL database
#MM_SQLSETTINGS_DATASOURCEREPLICAS: ""
MM_FILESETTINGS_AMAZONS3ACCESSKEYID: "accesskey-minio" #Access Key ID to access s3 storage
MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY: "secretkey-minio" #The secret key for accessing S3 storage
MM_LICENSE: ""
MM_CONFIG: "postgres://<dbuser>:<dbpassword>@<postresql-server>:5432/loopdb?sslmode=disable" #Store the configuration in the database. Use MM_SQLSETTINGS_DATASOURCE

View File

@ -19,3 +19,4 @@
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,23 @@
annotations:
category: Infrastructure
apiVersion: v2
appVersion: 1.10.0
description: A Library Helm Chart for grouping common logic between bitnami charts.
This chart is not deployable by itself.
home: https://github.com/bitnami/charts/tree/master/bitnami/common
icon: https://bitnami.com/downloads/logos/bitnami-mark.png
keywords:
- common
- helper
- template
- function
- bitnami
maintainers:
- email: containers@bitnami.com
name: Bitnami
name: common
sources:
- https://github.com/bitnami/charts
- https://www.bitnami.com/
type: library
version: 1.10.4

View File

@ -0,0 +1,344 @@
# Bitnami Common Library Chart
A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts.
## TL;DR
```yaml
dependencies:
- name: common
version: 0.x.x
repository: https://charts.bitnami.com/bitnami
```
```bash
$ helm dependency update
```
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "common.names.fullname" . }}
data:
myvalue: "Hello World"
```
## Introduction
This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager.
Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This Helm chart has been tested on top of [Bitnami Kubernetes Production Runtime](https://kubeprod.io/) (BKPR). Deploy BKPR to get automated TLS certificates, logging and monitoring for your applications.
## Prerequisites
- Kubernetes 1.19+
- Helm 3.2.0+
## Parameters
The following table lists the helpers available in the library which are scoped in different sections.
### Affinities
| Helper identifier | Description | Expected Input |
|-------------------------------|------------------------------------------------------|------------------------------------------------|
| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` |
| `common.affinities.node.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` |
| `common.affinities.pod.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` |
| `common.affinities.pod.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` |
### Capabilities
| Helper identifier | Description | Expected Input |
|------------------------------------------------|------------------------------------------------------------------------------------------------|-------------------|
| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context |
| `common.capabilities.cronjob.apiVersion` | Return the appropriate apiVersion for cronjob. | `.` Chart context |
| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context |
| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context |
| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context |
| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context |
| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context |
| `common.capabilities.policy.apiVersion` | Return the appropriate apiVersion for podsecuritypolicy. | `.` Chart context |
| `common.capabilities.networkPolicy.apiVersion` | Return the appropriate apiVersion for networkpolicy. | `.` Chart context |
| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context |
### Errors
| Helper identifier | Description | Expected Input |
|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` |
### Images
| Helper identifier | Description | Expected Input |
|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. |
| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` |
| `common.images.renderPullSecrets` | Return the proper Docker Image Registry Secret Names (evaluates values as templates) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $` |
### Ingress
| Helper identifier | Description | Expected Input |
|-------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences |
| `common.ingress.supportsPathType` | Prints "true" if the pathType field is supported | `.` Chart context |
| `common.ingress.supportsIngressClassname` | Prints "true" if the ingressClassname field is supported | `.` Chart context |
### Labels
| Helper identifier | Description | Expected Input |
|-----------------------------|------------------------------------------------------|-------------------|
| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context |
| `common.labels.matchLabels` | Return the proper Docker Image Registry Secret Names | `.` Chart context |
### Names
| Helper identifier | Description | Expected Input |
|-------------------------|------------------------------------------------------------|-------------------|
| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context |
| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context |
| `common.names.chart` | Chart name plus version | `.` Chart context |
### Secrets
| Helper identifier | Description | Expected Input |
|---------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. |
| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. |
| `common.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. |
| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` |
### Storage
| Helper identifier | Description | Expected Input |
|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| `common.storage.class` | Return the proper Storage Class | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. |
### TplValues
| Helper identifier | Description | Expected Input |
|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` |
### Utils
| Helper identifier | Description | Expected Input |
|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` |
| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` |
| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` |
| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` |
### Validations
| Helper identifier | Description | Expected Input |
|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) |
| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) |
| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. |
| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. |
| `common.validations.values.redis.passwords` | This helper will ensure required password for Redis&trade; are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. |
| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. |
| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB&reg; are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. |
### Warnings
| Helper identifier | Description | Expected Input |
|------------------------------|----------------------------------|------------------------------------------------------------|
| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. |
## Special input schemas
### ImageRoot
```yaml
registry:
type: string
description: Docker registry where the image is located
example: docker.io
repository:
type: string
description: Repository and image name
example: bitnami/nginx
tag:
type: string
description: image tag
example: 1.16.1-debian-10-r63
pullPolicy:
type: string
description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
pullSecrets:
type: array
items:
type: string
description: Optionally specify an array of imagePullSecrets (evaluated as templates).
debug:
type: boolean
description: Set to true if you would like to see extra information on logs
example: false
## An instance would be:
# registry: docker.io
# repository: bitnami/nginx
# tag: 1.16.1-debian-10-r63
# pullPolicy: IfNotPresent
# debug: false
```
### Persistence
```yaml
enabled:
type: boolean
description: Whether enable persistence.
example: true
storageClass:
type: string
description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning.
example: "-"
accessMode:
type: string
description: Access mode for the Persistent Volume Storage.
example: ReadWriteOnce
size:
type: string
description: Size the Persistent Volume Storage.
example: 8Gi
path:
type: string
description: Path to be persisted.
example: /bitnami
## An instance would be:
# enabled: true
# storageClass: "-"
# accessMode: ReadWriteOnce
# size: 8Gi
# path: /bitnami
```
### ExistingSecret
```yaml
name:
type: string
description: Name of the existing secret.
example: mySecret
keyMapping:
description: Mapping between the expected key name and the name of the key in the existing secret.
type: object
## An instance would be:
# name: mySecret
# keyMapping:
# password: myPasswordKey
```
#### Example of use
When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets.
```yaml
# templates/secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: {{ include "common.names.fullname" . }}
labels:
app: {{ include "common.names.fullname" . }}
type: Opaque
data:
password: {{ .Values.password | b64enc | quote }}
# templates/dpl.yaml
---
...
env:
- name: PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }}
key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }}
...
# values.yaml
---
name: mySecret
keyMapping:
password: myPasswordKey
```
### ValidateValue
#### NOTES.txt
```console
{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}}
{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}}
{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }}
```
If we force those values to be empty we will see some alerts
```console
$ helm install test mychart --set path.to.value00="",path.to.value01=""
'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value:
export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 --decode)
'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value:
export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 --decode)
```
## Upgrading
### To 1.0.0
[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL.
**What changes were introduced in this major version?**
- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field.
- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information.
- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts
**Considerations when upgrading to this version**
- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues
- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore
- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3
**Useful links**
- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/
- https://helm.sh/docs/topics/v2_v3_migration/
- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/
## License
Copyright &copy; 2022 Bitnami
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,102 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Return a soft nodeAffinity definition
{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}}
*/}}
{{- define "common.affinities.nodes.soft" -}}
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: {{ .key }}
operator: In
values:
{{- range .values }}
- {{ . | quote }}
{{- end }}
weight: 1
{{- end -}}
{{/*
Return a hard nodeAffinity definition
{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}}
*/}}
{{- define "common.affinities.nodes.hard" -}}
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: {{ .key }}
operator: In
values:
{{- range .values }}
- {{ . | quote }}
{{- end }}
{{- end -}}
{{/*
Return a nodeAffinity definition
{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}}
*/}}
{{- define "common.affinities.nodes" -}}
{{- if eq .type "soft" }}
{{- include "common.affinities.nodes.soft" . -}}
{{- else if eq .type "hard" }}
{{- include "common.affinities.nodes.hard" . -}}
{{- end -}}
{{- end -}}
{{/*
Return a soft podAffinity/podAntiAffinity definition
{{ include "common.affinities.pods.soft" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "context" $) -}}
*/}}
{{- define "common.affinities.pods.soft" -}}
{{- $component := default "" .component -}}
{{- $extraMatchLabels := default (dict) .extraMatchLabels -}}
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }}
{{- if not (empty $component) }}
{{ printf "app.kubernetes.io/component: %s" $component }}
{{- end }}
{{- range $key, $value := $extraMatchLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
namespaces:
- {{ .context.Release.Namespace | quote }}
topologyKey: kubernetes.io/hostname
weight: 1
{{- end -}}
{{/*
Return a hard podAffinity/podAntiAffinity definition
{{ include "common.affinities.pods.hard" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "context" $) -}}
*/}}
{{- define "common.affinities.pods.hard" -}}
{{- $component := default "" .component -}}
{{- $extraMatchLabels := default (dict) .extraMatchLabels -}}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }}
{{- if not (empty $component) }}
{{ printf "app.kubernetes.io/component: %s" $component }}
{{- end }}
{{- range $key, $value := $extraMatchLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
namespaces:
- {{ .context.Release.Namespace | quote }}
topologyKey: kubernetes.io/hostname
{{- end -}}
{{/*
Return a podAffinity/podAntiAffinity definition
{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}}
*/}}
{{- define "common.affinities.pods" -}}
{{- if eq .type "soft" }}
{{- include "common.affinities.pods.soft" . -}}
{{- else if eq .type "hard" }}
{{- include "common.affinities.pods.hard" . -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,128 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Return the target Kubernetes version
*/}}
{{- define "common.capabilities.kubeVersion" -}}
{{- if .Values.global }}
{{- if .Values.global.kubeVersion }}
{{- .Values.global.kubeVersion -}}
{{- else }}
{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}}
{{- end -}}
{{- else }}
{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for poddisruptionbudget.
*/}}
{{- define "common.capabilities.policy.apiVersion" -}}
{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "policy/v1beta1" -}}
{{- else -}}
{{- print "policy/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for networkpolicy.
*/}}
{{- define "common.capabilities.networkPolicy.apiVersion" -}}
{{- if semverCompare "<1.7-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "extensions/v1beta1" -}}
{{- else -}}
{{- print "networking.k8s.io/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for cronjob.
*/}}
{{- define "common.capabilities.cronjob.apiVersion" -}}
{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "batch/v1beta1" -}}
{{- else -}}
{{- print "batch/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for deployment.
*/}}
{{- define "common.capabilities.deployment.apiVersion" -}}
{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "extensions/v1beta1" -}}
{{- else -}}
{{- print "apps/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for statefulset.
*/}}
{{- define "common.capabilities.statefulset.apiVersion" -}}
{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "apps/v1beta1" -}}
{{- else -}}
{{- print "apps/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for ingress.
*/}}
{{- define "common.capabilities.ingress.apiVersion" -}}
{{- if .Values.ingress -}}
{{- if .Values.ingress.apiVersion -}}
{{- .Values.ingress.apiVersion -}}
{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "extensions/v1beta1" -}}
{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "networking.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "networking.k8s.io/v1" -}}
{{- end }}
{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "extensions/v1beta1" -}}
{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "networking.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "networking.k8s.io/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for RBAC resources.
*/}}
{{- define "common.capabilities.rbac.apiVersion" -}}
{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "rbac.authorization.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "rbac.authorization.k8s.io/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for CRDs.
*/}}
{{- define "common.capabilities.crd.apiVersion" -}}
{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "apiextensions.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "apiextensions.k8s.io/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Returns true if the used Helm version is 3.3+.
A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure.
This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error.
**To be removed when the catalog's minimun Helm version is 3.3**
*/}}
{{- define "common.capabilities.supportsHelmVersion" -}}
{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }}
{{- true -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,23 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Through error when upgrading using empty passwords values that must not be empty.
Usage:
{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}}
{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}}
{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }}
Required password params:
- validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error.
- context - Context - Required. Parent context.
*/}}
{{- define "common.errors.upgrade.passwords.empty" -}}
{{- $validationErrors := join "" .validationErrors -}}
{{- if and $validationErrors .context.Release.IsUpgrade -}}
{{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}}
{{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}}
{{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}}
{{- $errorString = print $errorString "\n%s" -}}
{{- printf $errorString $validationErrors | fail -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,75 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Return the proper image name
{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }}
*/}}
{{- define "common.images.image" -}}
{{- $registryName := .imageRoot.registry -}}
{{- $repositoryName := .imageRoot.repository -}}
{{- $tag := .imageRoot.tag | toString -}}
{{- if .global }}
{{- if .global.imageRegistry }}
{{- $registryName = .global.imageRegistry -}}
{{- end -}}
{{- end -}}
{{- if $registryName }}
{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
{{- else -}}
{{- printf "%s:%s" $repositoryName $tag -}}
{{- end -}}
{{- end -}}
{{/*
Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead)
{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }}
*/}}
{{- define "common.images.pullSecrets" -}}
{{- $pullSecrets := list }}
{{- if .global }}
{{- range .global.imagePullSecrets -}}
{{- $pullSecrets = append $pullSecrets . -}}
{{- end -}}
{{- end -}}
{{- range .images -}}
{{- range .pullSecrets -}}
{{- $pullSecrets = append $pullSecrets . -}}
{{- end -}}
{{- end -}}
{{- if (not (empty $pullSecrets)) }}
imagePullSecrets:
{{- range $pullSecrets }}
- name: {{ . }}
{{- end }}
{{- end }}
{{- end -}}
{{/*
Return the proper Docker Image Registry Secret Names evaluating values as templates
{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }}
*/}}
{{- define "common.images.renderPullSecrets" -}}
{{- $pullSecrets := list }}
{{- $context := .context }}
{{- if $context.Values.global }}
{{- range $context.Values.global.imagePullSecrets -}}
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}}
{{- end -}}
{{- end -}}
{{- range .images -}}
{{- range .pullSecrets -}}
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}}
{{- end -}}
{{- end -}}
{{- if (not (empty $pullSecrets)) }}
imagePullSecrets:
{{- range $pullSecrets }}
- name: {{ . }}
{{- end }}
{{- end }}
{{- end -}}

View File

@ -0,0 +1,55 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Generate backend entry that is compatible with all Kubernetes API versions.
Usage:
{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }}
Params:
- serviceName - String. Name of an existing service backend
- servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer.
- context - Dict - Required. The context for the template evaluation.
*/}}
{{- define "common.ingress.backend" -}}
{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}}
{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}}
serviceName: {{ .serviceName }}
servicePort: {{ .servicePort }}
{{- else -}}
service:
name: {{ .serviceName }}
port:
{{- if typeIs "string" .servicePort }}
name: {{ .servicePort }}
{{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }}
number: {{ .servicePort | int }}
{{- end }}
{{- end -}}
{{- end -}}
{{/*
Print "true" if the API pathType field is supported
Usage:
{{ include "common.ingress.supportsPathType" . }}
*/}}
{{- define "common.ingress.supportsPathType" -}}
{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}}
{{- print "false" -}}
{{- else -}}
{{- print "true" -}}
{{- end -}}
{{- end -}}
{{/*
Returns true if the ingressClassname field is supported
Usage:
{{ include "common.ingress.supportsIngressClassname" . }}
*/}}
{{- define "common.ingress.supportsIngressClassname" -}}
{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}}
{{- print "false" -}}
{{- else -}}
{{- print "true" -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,18 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Kubernetes standard labels
*/}}
{{- define "common.labels.standard" -}}
app.kubernetes.io/name: {{ include "common.names.name" . }}
helm.sh/chart: {{ include "common.names.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector
*/}}
{{- define "common.labels.matchLabels" -}}
app.kubernetes.io/name: {{ include "common.names.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

View File

@ -0,0 +1,52 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "common.names.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "common.names.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | 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 "common.names.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 a default fully qualified dependency 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.
Usage:
{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }}
*/}}
{{- define "common.names.dependency.fullname" -}}
{{- if .chartValues.fullnameOverride -}}
{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .chartName .chartValues.nameOverride -}}
{{- if contains $name .context.Release.Name -}}
{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,129 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Generate secret name.
Usage:
{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }}
Params:
- existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user
to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility.
+info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret
- defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment.
- context - Dict - Required. The context for the template evaluation.
*/}}
{{- define "common.secrets.name" -}}
{{- $name := (include "common.names.fullname" .context) -}}
{{- if .defaultNameSuffix -}}
{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- with .existingSecret -}}
{{- if not (typeIs "string" .) -}}
{{- with .name -}}
{{- $name = . -}}
{{- end -}}
{{- else -}}
{{- $name = . -}}
{{- end -}}
{{- end -}}
{{- printf "%s" $name -}}
{{- end -}}
{{/*
Generate secret key.
Usage:
{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }}
Params:
- existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user
to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility.
+info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret
- key - String - Required. Name of the key in the secret.
*/}}
{{- define "common.secrets.key" -}}
{{- $key := .key -}}
{{- if .existingSecret -}}
{{- if not (typeIs "string" .existingSecret) -}}
{{- if .existingSecret.keyMapping -}}
{{- $key = index .existingSecret.keyMapping $.key -}}
{{- end -}}
{{- end }}
{{- end -}}
{{- printf "%s" $key -}}
{{- end -}}
{{/*
Generate secret password or retrieve one if already created.
Usage:
{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }}
Params:
- secret - String - Required - Name of the 'Secret' resource where the password is stored.
- key - String - Required - Name of the key in the secret.
- providedValues - List<String> - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value.
- length - int - Optional - Length of the generated random password.
- strong - Boolean - Optional - Whether to add symbols to the generated random password.
- chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart.
- context - Context - Required - Parent context.
*/}}
{{- define "common.secrets.passwords.manage" -}}
{{- $password := "" }}
{{- $subchart := "" }}
{{- $chartName := default "" .chartName }}
{{- $passwordLength := default 10 .length }}
{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }}
{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }}
{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }}
{{- if $secret }}
{{- if index $secret.data .key }}
{{- $password = index $secret.data .key }}
{{- end -}}
{{- else if $providedPasswordValue }}
{{- $password = $providedPasswordValue | toString | b64enc | quote }}
{{- else }}
{{- if .context.Values.enabled }}
{{- $subchart = $chartName }}
{{- end -}}
{{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}}
{{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}}
{{- $passwordValidationErrors := list $requiredPasswordError -}}
{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}}
{{- if .strong }}
{{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }}
{{- $password = randAscii $passwordLength }}
{{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }}
{{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }}
{{- else }}
{{- $password = randAlphaNum $passwordLength | b64enc | quote }}
{{- end }}
{{- end -}}
{{- printf "%s" $password -}}
{{- end -}}
{{/*
Returns whether a previous generated secret already exists
Usage:
{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }}
Params:
- secret - String - Required - Name of the 'Secret' resource where the password is stored.
- context - Context - Required - Parent context.
*/}}
{{- define "common.secrets.exists" -}}
{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }}
{{- if $secret }}
{{- true -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,23 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Return the proper Storage Class
{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }}
*/}}
{{- define "common.storage.class" -}}
{{- $storageClass := .persistence.storageClass -}}
{{- if .global -}}
{{- if .global.storageClass -}}
{{- $storageClass = .global.storageClass -}}
{{- end -}}
{{- end -}}
{{- if $storageClass -}}
{{- if (eq "-" $storageClass) -}}
{{- printf "storageClassName: \"\"" -}}
{{- else }}
{{- printf "storageClassName: %s" $storageClass -}}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,13 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Renders a value that contains template.
Usage:
{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }}
*/}}
{{- define "common.tplvalues.render" -}}
{{- if typeIs "string" .value }}
{{- tpl .value .context }}
{{- else }}
{{- tpl (.value | toYaml) .context }}
{{- end }}
{{- end -}}

View File

@ -0,0 +1,62 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Print instructions to get a secret value.
Usage:
{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }}
*/}}
{{- define "common.utils.secret.getvalue" -}}
{{- $varname := include "common.utils.fieldToEnvVar" . -}}
export {{ $varname }}=$(kubectl get secret --namespace {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode)
{{- end -}}
{{/*
Build env var name given a field
Usage:
{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }}
*/}}
{{- define "common.utils.fieldToEnvVar" -}}
{{- $fieldNameSplit := splitList "-" .field -}}
{{- $upperCaseFieldNameSplit := list -}}
{{- range $fieldNameSplit -}}
{{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}}
{{- end -}}
{{ join "_" $upperCaseFieldNameSplit }}
{{- end -}}
{{/*
Gets a value from .Values given
Usage:
{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }}
*/}}
{{- define "common.utils.getValueFromKey" -}}
{{- $splitKey := splitList "." .key -}}
{{- $value := "" -}}
{{- $latestObj := $.context.Values -}}
{{- range $splitKey -}}
{{- if not $latestObj -}}
{{- printf "please review the entire path of '%s' exists in values" $.key | fail -}}
{{- end -}}
{{- $value = ( index $latestObj . ) -}}
{{- $latestObj = $value -}}
{{- end -}}
{{- printf "%v" (default "" $value) -}}
{{- end -}}
{{/*
Returns first .Values key with a defined value or first of the list if all non-defined
Usage:
{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }}
*/}}
{{- define "common.utils.getKeyFromList" -}}
{{- $key := first .keys -}}
{{- $reverseKeys := reverse .keys }}
{{- range $reverseKeys }}
{{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }}
{{- if $value -}}
{{- $key = . }}
{{- end -}}
{{- end -}}
{{- printf "%s" $key -}}
{{- end -}}

View File

@ -0,0 +1,14 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Warning about using rolling tag.
Usage:
{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }}
*/}}
{{- define "common.warnings.rollingTag" -}}
{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }}
WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/
{{- end }}
{{- end -}}

View File

@ -0,0 +1,72 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate Cassandra required passwords are not empty.
Usage:
{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }}
Params:
- secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret"
- subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false
*/}}
{{- define "common.validations.values.cassandra.passwords" -}}
{{- $existingSecret := include "common.cassandra.values.existingSecret" . -}}
{{- $enabled := include "common.cassandra.values.enabled" . -}}
{{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}}
{{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}}
{{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}}
{{- $requiredPasswords := list -}}
{{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredPassword -}}
{{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for existingSecret.
Usage:
{{ include "common.cassandra.values.existingSecret" (dict "context" $) }}
Params:
- subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false
*/}}
{{- define "common.cassandra.values.existingSecret" -}}
{{- if .subchart -}}
{{- .context.Values.cassandra.dbUser.existingSecret | quote -}}
{{- else -}}
{{- .context.Values.dbUser.existingSecret | quote -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled cassandra.
Usage:
{{ include "common.cassandra.values.enabled" (dict "context" $) }}
*/}}
{{- define "common.cassandra.values.enabled" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.cassandra.enabled -}}
{{- else -}}
{{- printf "%v" (not .context.Values.enabled) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for the key dbUser
Usage:
{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false
*/}}
{{- define "common.cassandra.values.key.dbUser" -}}
{{- if .subchart -}}
cassandra.dbUser
{{- else -}}
dbUser
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,103 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate MariaDB required passwords are not empty.
Usage:
{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }}
Params:
- secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret"
- subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false
*/}}
{{- define "common.validations.values.mariadb.passwords" -}}
{{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}}
{{- $enabled := include "common.mariadb.values.enabled" . -}}
{{- $architecture := include "common.mariadb.values.architecture" . -}}
{{- $authPrefix := include "common.mariadb.values.key.auth" . -}}
{{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}}
{{- $valueKeyUsername := printf "%s.username" $authPrefix -}}
{{- $valueKeyPassword := printf "%s.password" $authPrefix -}}
{{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}}
{{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}}
{{- $requiredPasswords := list -}}
{{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}}
{{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }}
{{- if not (empty $valueUsername) -}}
{{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredPassword -}}
{{- end -}}
{{- if (eq $architecture "replication") -}}
{{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}}
{{- end -}}
{{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for existingSecret.
Usage:
{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false
*/}}
{{- define "common.mariadb.values.auth.existingSecret" -}}
{{- if .subchart -}}
{{- .context.Values.mariadb.auth.existingSecret | quote -}}
{{- else -}}
{{- .context.Values.auth.existingSecret | quote -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled mariadb.
Usage:
{{ include "common.mariadb.values.enabled" (dict "context" $) }}
*/}}
{{- define "common.mariadb.values.enabled" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.mariadb.enabled -}}
{{- else -}}
{{- printf "%v" (not .context.Values.enabled) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for architecture
Usage:
{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false
*/}}
{{- define "common.mariadb.values.architecture" -}}
{{- if .subchart -}}
{{- .context.Values.mariadb.architecture -}}
{{- else -}}
{{- .context.Values.architecture -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for the key auth
Usage:
{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false
*/}}
{{- define "common.mariadb.values.key.auth" -}}
{{- if .subchart -}}
mariadb.auth
{{- else -}}
auth
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,108 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate MongoDB&reg; required passwords are not empty.
Usage:
{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }}
Params:
- secret - String - Required. Name of the secret where MongoDB&reg; values are stored, e.g: "mongodb-passwords-secret"
- subchart - Boolean - Optional. Whether MongoDB&reg; is used as subchart or not. Default: false
*/}}
{{- define "common.validations.values.mongodb.passwords" -}}
{{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}}
{{- $enabled := include "common.mongodb.values.enabled" . -}}
{{- $authPrefix := include "common.mongodb.values.key.auth" . -}}
{{- $architecture := include "common.mongodb.values.architecture" . -}}
{{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}}
{{- $valueKeyUsername := printf "%s.username" $authPrefix -}}
{{- $valueKeyDatabase := printf "%s.database" $authPrefix -}}
{{- $valueKeyPassword := printf "%s.password" $authPrefix -}}
{{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}}
{{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}}
{{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}}
{{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") (eq $authEnabled "true") -}}
{{- $requiredPasswords := list -}}
{{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}}
{{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }}
{{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }}
{{- if and $valueUsername $valueDatabase -}}
{{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredPassword -}}
{{- end -}}
{{- if (eq $architecture "replicaset") -}}
{{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}}
{{- end -}}
{{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for existingSecret.
Usage:
{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false
*/}}
{{- define "common.mongodb.values.auth.existingSecret" -}}
{{- if .subchart -}}
{{- .context.Values.mongodb.auth.existingSecret | quote -}}
{{- else -}}
{{- .context.Values.auth.existingSecret | quote -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled mongodb.
Usage:
{{ include "common.mongodb.values.enabled" (dict "context" $) }}
*/}}
{{- define "common.mongodb.values.enabled" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.mongodb.enabled -}}
{{- else -}}
{{- printf "%v" (not .context.Values.enabled) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for the key auth
Usage:
{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MongoDB&reg; is used as subchart or not. Default: false
*/}}
{{- define "common.mongodb.values.key.auth" -}}
{{- if .subchart -}}
mongodb.auth
{{- else -}}
auth
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for architecture
Usage:
{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false
*/}}
{{- define "common.mongodb.values.architecture" -}}
{{- if .subchart -}}
{{- .context.Values.mongodb.architecture -}}
{{- else -}}
{{- .context.Values.architecture -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,129 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate PostgreSQL required passwords are not empty.
Usage:
{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }}
Params:
- secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret"
- subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false
*/}}
{{- define "common.validations.values.postgresql.passwords" -}}
{{- $existingSecret := include "common.postgresql.values.existingSecret" . -}}
{{- $enabled := include "common.postgresql.values.enabled" . -}}
{{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}}
{{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}}
{{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}}
{{- $requiredPasswords := list -}}
{{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}}
{{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}}
{{- if (eq $enabledReplication "true") -}}
{{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}}
{{- end -}}
{{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to decide whether evaluate global values.
Usage:
{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }}
Params:
- key - String - Required. Field to be evaluated within global, e.g: "existingSecret"
*/}}
{{- define "common.postgresql.values.use.global" -}}
{{- if .context.Values.global -}}
{{- if .context.Values.global.postgresql -}}
{{- index .context.Values.global.postgresql .key | quote -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for existingSecret.
Usage:
{{ include "common.postgresql.values.existingSecret" (dict "context" $) }}
*/}}
{{- define "common.postgresql.values.existingSecret" -}}
{{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}}
{{- if .subchart -}}
{{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}}
{{- else -}}
{{- default (.context.Values.existingSecret | quote) $globalValue -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled postgresql.
Usage:
{{ include "common.postgresql.values.enabled" (dict "context" $) }}
*/}}
{{- define "common.postgresql.values.enabled" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.postgresql.enabled -}}
{{- else -}}
{{- printf "%v" (not .context.Values.enabled) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for the key postgressPassword.
Usage:
{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false
*/}}
{{- define "common.postgresql.values.key.postgressPassword" -}}
{{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}}
{{- if not $globalValue -}}
{{- if .subchart -}}
postgresql.postgresqlPassword
{{- else -}}
postgresqlPassword
{{- end -}}
{{- else -}}
global.postgresql.postgresqlPassword
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled.replication.
Usage:
{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false
*/}}
{{- define "common.postgresql.values.enabled.replication" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.postgresql.replication.enabled -}}
{{- else -}}
{{- printf "%v" .context.Values.replication.enabled -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for the key replication.password.
Usage:
{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false
*/}}
{{- define "common.postgresql.values.key.replicationPassword" -}}
{{- if .subchart -}}
postgresql.replication.password
{{- else -}}
replication.password
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,76 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate Redis&trade; required passwords are not empty.
Usage:
{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }}
Params:
- secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret"
- subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false
*/}}
{{- define "common.validations.values.redis.passwords" -}}
{{- $enabled := include "common.redis.values.enabled" . -}}
{{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}}
{{- $standarizedVersion := include "common.redis.values.standarized.version" . }}
{{- $existingSecret := ternary (printf "%s%s" $valueKeyPrefix "auth.existingSecret") (printf "%s%s" $valueKeyPrefix "existingSecret") (eq $standarizedVersion "true") }}
{{- $existingSecretValue := include "common.utils.getValueFromKey" (dict "key" $existingSecret "context" .context) }}
{{- $valueKeyRedisPassword := ternary (printf "%s%s" $valueKeyPrefix "auth.password") (printf "%s%s" $valueKeyPrefix "password") (eq $standarizedVersion "true") }}
{{- $valueKeyRedisUseAuth := ternary (printf "%s%s" $valueKeyPrefix "auth.enabled") (printf "%s%s" $valueKeyPrefix "usePassword") (eq $standarizedVersion "true") }}
{{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}}
{{- $requiredPasswords := list -}}
{{- $useAuth := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUseAuth "context" .context) -}}
{{- if eq $useAuth "true" -}}
{{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}}
{{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}}
{{- end -}}
{{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right value for enabled redis.
Usage:
{{ include "common.redis.values.enabled" (dict "context" $) }}
*/}}
{{- define "common.redis.values.enabled" -}}
{{- if .subchart -}}
{{- printf "%v" .context.Values.redis.enabled -}}
{{- else -}}
{{- printf "%v" (not .context.Values.enabled) -}}
{{- end -}}
{{- end -}}
{{/*
Auxiliary function to get the right prefix path for the values
Usage:
{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }}
Params:
- subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false
*/}}
{{- define "common.redis.values.keys.prefix" -}}
{{- if .subchart -}}redis.{{- else -}}{{- end -}}
{{- end -}}
{{/*
Checks whether the redis chart's includes the standarizations (version >= 14)
Usage:
{{ include "common.redis.values.standarized.version" (dict "context" $) }}
*/}}
{{- define "common.redis.values.standarized.version" -}}
{{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}}
{{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }}
{{- if $standarizedAuthValues -}}
{{- true -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,46 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Validate values must not be empty.
Usage:
{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}}
{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}}
{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }}
Validate value params:
- valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password"
- secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret"
- field - String - Optional. Name of the field in the secret data, e.g: "mysql-password"
*/}}
{{- define "common.validations.values.multiple.empty" -}}
{{- range .required -}}
{{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}}
{{- end -}}
{{- end -}}
{{/*
Validate a value must not be empty.
Usage:
{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }}
Validate value params:
- valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password"
- secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret"
- field - String - Optional. Name of the field in the secret data, e.g: "mysql-password"
- subchart - String - Optional - Name of the subchart that the validated password is part of.
*/}}
{{- define "common.validations.values.single.empty" -}}
{{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }}
{{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }}
{{- if not $value -}}
{{- $varname := "my-value" -}}
{{- $getCurrentValue := "" -}}
{{- if and .secret .field -}}
{{- $varname = include "common.utils.fieldToEnvVar" . -}}
{{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}}
{{- end -}}
{{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,5 @@
## bitnami/common
## It is required by CI/CD tools and processes.
## @skip exampleValue
##
exampleValue: common-chart

View File

@ -1,19 +1 @@
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 }}
{{- include "app.validateValues" . }}

View File

@ -1,61 +1,56 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
Return the proper App image name
*/}}
{{- define "loop-enterprise-edition.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- define "app.image" -}}
{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }}
{{- 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.
Create the name of the service account to use
*/}}
{{- define "loop-enterprise-edition.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- define "app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "common.names.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Return the proper Docker Image Registry Secret Names
*/}}
{{- define "app.imagePullSecrets" -}}
{{- include "common.images.pullSecrets" (dict "images" (list .Values.image) "global" .Values.global) -}}
{{- end -}}
{{/*
Set App PVC
*/}}
{{- define "app.pvc" -}}
{{- .Values.persistence.existingClaim | default (include "common.names.fullname" .) -}}
{{- end -}}
{{/*
Return the proper Storage Class
*/}}
{{- define "app.storageClass" -}}
{{- include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) -}}
{{- end -}}
{{/*
Choose port name based on .port .targetPort. Fallbacks to "app".
Examples:
{{- include "app.service.defaultPortName" (dict "port" "80" "targetPort" "8080") }}
{{- include "app.service.defaultPortName" (dict "port" "80" "targetPort" "api") }}
*/}}
{{- define "app.service.defaultPortName" -}}
{{- $targetPort := .targetPort | default .port -}}
{{- if regexMatch "^[0-9]+$" ($targetPort | toString) -}}
app
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{ $targetPort }}
{{- 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,20 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Ingress annotations
*/}}
{{- define "app.ingress.annotations" -}}
{{- if .Values.ingress.certManager }}
kubernetes.io/tls-acme: "true"
{{- end }}
{{- with .Values.ingress.nginx.configurationSnippet }}
nginx.ingress.kubernetes.io/configuration-snippet: |
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 2 }}
{{- end }}
{{- with .Values.ingress.nginx.serverSnippet }}
nginx.ingress.kubernetes.io/server-snippet: |
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 2 }}
{{- end }}
{{- end -}}

View File

@ -0,0 +1,39 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "common.names.name" -}}
{{- default (.Values.name | default "") .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "common.names.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | 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 "common.names.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default (.Values.name | default "") .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 -}}
{{/*
Short for "common.name.fullname"
*/}}
{{- define "app.fullname" -}}
{{ template "common.names.fullname" . }}
{{- end -}}

View File

@ -0,0 +1,75 @@
{{/*
Compile all warnings into a single message, and call fail.
*/}}
{{- define "app.validateValues" -}}
{{- $messages := list -}}
{{- $messages := append $messages (include "app.validateValues.nameGiven" .) -}}
{{- $messages := append $messages (include "app.validateValues.imageRepositoryGiven" .) -}}
{{- $messages := append $messages (include "app.validateValues.noSecretsData" .) -}}
{{- $messages := append $messages (include "app.validateValues.noConfigMapsData" .) -}}
{{- $messages := append $messages (include "app.validateValues.ingressHasPortsProvided" .) -}}
{{- $messages := without $messages "" -}}
{{- $message := join "\n" $messages -}}
{{- if $message -}}
{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}}
{{- end -}}
{{- end -}}
{{/* Validate value name is given */}}
{{- define "app.validateValues.nameGiven" -}}
{{- if not .Values.name -}}
app: no-name
You did not specify the application name for the generic app chart. Please
set name.
{{- end -}}
{{- end -}}
{{/* Validate that the container repository name is given */}}
{{- define "app.validateValues.imageRepositoryGiven" -}}
{{- if and (not .Values.image.repository) (has .Values.appKind (list "Deployment" "StatefulSet" "DaemonSet")) -}}
app: no-image-repository
You did not specify the application image repository. Please
set image.repository.
{{- end -}}
{{- end -}}
{{/* Validate that the container has command or args given */}}
{{- define "app.validateValues.containerCommandOrArgsGiven" -}}
{{- if not (or .Values.command .Values.args) -}}
app: no-command-or-args
You did not specify command or args for the application default
container. Please set command or args.
{{- end -}}
{{- end -}}
{{/* Validate that secrets have data or stringData given */}}
{{- define "app.validateValues.noSecretsData" -}}
{{- range $_, $values := .Values.secrets -}}
{{- if not (or $values.data $values.stringData) -}}
app: no-secrets-data-or-stringdata
Each item of .secrets must have data or stringData field. Please
check the input configuration.
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Validate that configMaps have data given */}}
{{- define "app.validateValues.noConfigMapsData" -}}
{{- range $_, $values := .Values.configMaps -}}
{{- if not $values.data -}}
app: no-configmaps-data
Each item of .configMaps must have data field. Please
check the input configuration.
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Validate that the enabled ingress has service ports provided */}}
{{- define "app.validateValues.ingressHasPortsProvided" -}}
{{- if and .Values.ingress.enabled (not (or .Values.service.port .Values.service.ports)) -}}
app: no-service-ports-for-ingress
You enabled ingress, but did not specify service port or ports. Please set
service.port or service.ports.
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,26 @@
{{- range $nameSuffix, $values := .Values.configMaps }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "common.names.fullname" $ }}-{{ $nameSuffix }}
annotations:
{{- with $values.annotations }}
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }}
{{- end }}
{{- if $.Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
labels:
{{- include "common.labels.standard" $ | nindent 4 }}
{{- if $.Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
{{- with $values.labels }}
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }}
{{- end }}
{{- with $values.data }}
data:
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 2 }}
{{- end }}
{{- end }}

View File

@ -1,157 +0,0 @@
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

@ -1,31 +0,0 @@
{{- 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

@ -1,101 +0,0 @@
{{- 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,129 @@
{{- if eq .Values.appKind "Deployment" }}
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
kind: Deployment
metadata:
name: {{ include "common.names.fullname" . }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
annotations:
{{- if .Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
{{- if .Values.updateStrategy }}
strategy: {{- tpl (toYaml .Values.updateStrategy) $ | nindent 4 }}
{{- end }}
selector:
matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }}
template:
metadata:
{{- if or .Values.checksums .Values.podAnnotations }}
annotations:
{{- range .Values.checksums }}
checksum/{{ . | trimPrefix "/" }}: {{ include (print $.Template.BasePath "/" (. | trimPrefix "/")) $ | sha256sum }}
{{- end }}
{{- if .Values.podAnnotations }}
{{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
{{- end }}
{{- end }}
labels: {{- include "common.labels.standard" . | nindent 8 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 8 }}
{{- end }}
{{- if .Values.podLabels }}
{{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ template "app.serviceAccountName" . }}
{{- include "app.imagePullSecrets" . | nindent 6 }}
dnsPolicy: {{ .Values.dnsPolicy }}
{{- if .Values.priorityClassName }}
priorityClassName: {{ .Values.priorityClassName | quote }}
{{- end }}
{{- if .Values.hostAliases }}
hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }}
{{- end }}
{{- if .Values.hostNetwork | toString }}
hostNetwork: {{ .Values.hostNetwork }}
{{- end }}
{{- if .Values.affinity }}
affinity: {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }}
{{- else }}
affinity:
podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "context" $) | nindent 10 }}
podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "context" $) | nindent 10 }}
nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }}
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" $) | nindent 8 }}
{{- end }}
{{- if .Values.schedulerName }}
schedulerName: {{ .Values.schedulerName | quote }}
{{- end }}
{{- if .Values.podSecurityContext.enabled }}
securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- if .Values.initContainers }}
initContainers: {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 10 }}
{{- end }}
containers:
- name: app
image: {{ include "app.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.containerSecurityContext.enabled }}
securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
{{- if .Values.command }}
command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }}
{{- end }}
{{- if .Values.args }}
args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }}
{{- end }}
{{- if .Values.env }}
env:
{{- include "common.tplvalues.render" (dict "value" .Values.env "context" $) | nindent 12 }}
{{- end }}
{{- if .Values.envFrom }}
envFrom: {{- include "common.tplvalues.render" (dict "value" .Values.envFrom "context" $) | nindent 12 }}
{{- end }}
{{- if .Values.containerPorts }}
ports: {{ .Values.containerPorts | toYaml | nindent 12 }}
{{- end }}
resources: {{- include "common.tplvalues.render" (dict "value" .Values.resources "context" $) | nindent 12 }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.livenessProbe "enabled") "context" $) | nindent 12 }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.readinessProbe "enabled") "context" $) | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.persistence.enabled }}
- name: data
mountPath: {{ .Values.persistence.mountPath }}
{{- end }}
{{- if .Values.volumeMounts }}
{{- include "common.tplvalues.render" (dict "value" .Values.volumeMounts "context" $) | nindent 12 }}
{{- end }}
{{- range $name, $values := .Values.extraContainers }}
- name: {{ $name }}
{{- with $values}}
{{- toYaml . | nindent 10 }}
{{- end }}
{{- end }}
volumes:
{{- if .Values.persistence.enabled }}
- name: data
persistentVolumeClaim:
claimName: {{ include "app.pvc" . }}
{{- end }}
{{- if .Values.volumes }}
{{- include "common.tplvalues.render" (dict "value" .Values.volumes "context" $) | nindent 8 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,4 @@
{{- range .Values.extraDeploy }}
---
{{ include "common.tplvalues.render" (dict "value" . "context" $) }}
{{- end }}

View File

@ -0,0 +1,35 @@
{{- if and .Values.autoscaling.enabled (eq .Values.appKind "Deployment") }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ template "common.names.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
app.kubernetes.io/component: controller
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
spec:
scaleTargetRef:
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
kind: Deployment
name: {{ template "common.names.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPU }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPU }}
{{- end }}
{{- if .Values.autoscaling.targetMemory }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemory }}
{{- end }}
{{- end }}

View File

@ -1,50 +0,0 @@
{{- 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,58 @@
{{- if and .Values.ingress.enabled (or .Values.service.port .Values.service.ports) }}
apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }}
kind: Ingress
metadata:
name: {{ include "common.names.fullname" . }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
annotations:
{{- include "app.ingress.annotations" . | nindent 4 }}
{{- if .Values.ingress.annotations }}
{{- include "common.tplvalues.render" (dict "value" .Values.ingress.annotations "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.ingressClassName (include "common.ingress.supportsIngressClassname" .) }}
ingressClassName: {{ .Values.ingress.ingressClassName }}
{{- end }}
rules:
{{- if and .Values.ingress.hostname }}
- host: {{ .Values.ingress.hostname }}
http:
paths:
{{- if .Values.ingress.extraPaths }}
{{- toYaml .Values.ingress.extraPaths | nindent 10 }}
{{- end }}
- path: {{ .Values.ingress.path }}
{{- if eq "true" (include "common.ingress.supportsPathType" .) }}
pathType: {{ .Values.ingress.pathType }}
{{- end }}
{{- $svcPort := include "app.service.defaultPortName" (pick .Values.service "port" "targetPort") }}
backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" $svcPort "context" .) | nindent 14 }}
{{- end }}
{{- range .Values.ingress.extraHosts }}
- host: {{ .name }}
http:
paths:
- path: {{ default "/" .path }}
{{- if eq "true" (include "common.ingress.supportsPathType" $) }}
pathType: {{ default "ImplementationSpecific" .pathType }}
{{- end }}
backend: {{- include "common.ingress.backend" (dict "serviceName" .serviceName "servicePort" .servicePort "context" $) | nindent 14 }}
{{- end }}
{{- if or .Values.ingress.tls .Values.ingress.extraTls }}
tls:
{{- if .Values.ingress.tls }}
- hosts:
- {{ .Values.ingress.hostname }}
secretName: {{ printf "%s-tls" .Values.ingress.hostname }}
{{- end }}
{{- if .Values.ingress.extraTls }}
{{- include "common.tplvalues.render" ( dict "value" .Values.ingress.extraTls "context" $ ) | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,22 @@
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ template "common.names.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
annotations:
{{- if .Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{ include "app.storageClass" . }}
{{- end -}}

View File

@ -1,14 +0,0 @@
{{- 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

@ -1,14 +0,0 @@
{{- 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,30 @@
{{- range $nameSuffix, $values := .Values.secrets }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ include "common.names.fullname" $ }}-{{ $nameSuffix }}
annotations:
{{- with $values.annotations }}
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }}
{{- end }}
{{- if $.Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
labels:
{{- include "common.labels.standard" $ | nindent 4 }}
{{- if $.Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
{{- with $values.labels }}
{{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }}
{{- end }}
type: Opaque
{{- with $values.data }}
data:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with $values.stringData }}
stringData: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 2 }}
{{- end }}
{{- end }}

View File

@ -1,17 +0,0 @@
{{ 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

@ -1,25 +0,0 @@
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 }}

View File

@ -0,0 +1,50 @@
{{- if or .Values.service.port .Values.service.ports -}}
{{- $service := .Values.service -}}
apiVersion: v1
kind: Service
metadata:
name: {{ template "common.names.fullname" . }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
{{- if or .Values.service.annotations .Values.commonAnnotations }}
annotations:
{{- if .Values.service.annotations }}
{{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }}
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }}
{{- end }}
{{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
ports:
{{- if .Values.service.port }}
- name: {{ include "app.service.defaultPortName" (pick .Values.service "port" "targetPort") }}
port: {{ .Values.service.port }}
{{- if .Values.service.targetPort }}
targetPort: {{ .Values.service.targetPort }}
{{- end }}
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
{{- end }}
{{- range .Values.service.ports }}
- name: {{ .name }}
port: {{ .port }}
{{- if .targetPort }}
targetPort: {{ .targetPort }}
{{- end }}
{{- if and .nodePort (or (eq $service.type "NodePort") (eq $service.type "LoadBalancer")) }}
nodePort: {{ .nodePort }}
{{- end }}
{{- end }}
selector: {{- include "common.labels.matchLabels" . | nindent 4 }}
{{- end -}}

View File

@ -0,0 +1,19 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "app.serviceAccountName" . }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- end }}
{{- if or .Values.serviceAccount.annotations .Values.commonAnnotations }}
annotations:
{{- if .Values.serviceAccount.annotations }}
{{- toYaml .Values.serviceAccount.annotations | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
{{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }}
{{- end }}
{{- end -}}

View File

@ -1,248 +1,289 @@
global:
siteUrl: "" # ОБЯЗАТЕЛЬНЫЙ ПАРАМЕТР, НЕОБХОДИМО УКАЗАТЬ: например, "https://loop.example.com"
# Использовать уже существующий секрет для лицензии (см. secret-loop-license.yaml для необходимых полей)
existingLicenseSecret: {}
# name: ""
# key: ""
# ОБЯЗАТЕЛЬНЫЙ ПАРАМЕТР, если не указан существующий секрет.
loopLicense: ""
# Настройки конфигурации LOOP
# Подробнее: https://docs.loop.ru/administration/config-settings
siteName: ""
enableTeamCreation: true # Разрешить создание команд
enableUserCreation: true # Разрешить создание пользователей
enableOpenServer: true # Открытый доступ к серверу
enableLinkPreviews: true # Предпросмотр ссылок
enableCustomEmoji: true # Поддержка пользовательских эмодзи
# Настройки email-уведомлений
sendEmailNotifications: false # Включить email-уведомления
requireEmailVerification: false # Требовать подтверждение email
feedbackName: "" # Имя отправителя отзывов
feedbackEmail: "" # Email для отзывов
feedbackOrganization: "" # Организация
smtpUsername: "" # SMTP логин
smtpPassword: "" # SMTP пароль
smtpServer: "" # SMTP сервер
smtpPort: "" # SMTP порт
connectionSecurity: "" # тип подключения: пусто, TLS или STARTTLS
features:
database:
external:
driver: "postgresql"
dataSource: "" # <username>:<password>@<postgresql-server>:5432/<dbname>?connect_timeout=10&sslmode=disable
dataSourceReplicas: ""
existingDatabaseSecret: {}
# name: ""
# key: ""
jobserver:
name: jobserver
enabled: true
replicaCount: 1
strategy:
type: Recreate
rollingUpdate: null
revisionHistoryLimit: 2
service:
name: loop-app-jobserver
type: ClusterIP
nodeSelector: {}
affinity: {}
tolerations: []
extraEnv: {}
notifications:
push:
enabled: true
# Хостинговый сервис push-уведомлений. Требует корпоративную лицензию.
# Подробнее: https://docs.loop.ru/mobile/mobile-hpns
useHPNS: true
initContainerImage:
repository: appropriate/curl
tag: latest
imagePullPolicy: IfNotPresent
# Секция деплоя LOOP
# Используется для настройки развертывания серверов LOOP
loopApp:
replicaCount: 1
image:
repository: registry.loop.ru/loop/server
tag: "2025-06-10-1"
image:
registry: registry.loop.ru
repository: loop/server
tag: 2025-11-20-1
pullPolicy: IfNotPresent
# pullSecrets:
# - myRegistryKeySecretName
strategy:
name: loop-server
appKind: Deployment
args: []
command: []
# nameOverride:
# fullnameOverride:
hostAliases: []
hostNetwork: false
serviceAccount:
create: false
annotations: {}
name:
commonLabels: {}
commonAnnotations: {}
podAnnotations: {}
podLabels: {}
initContainers: []
## e.g.
# - name: your-image-name
# image: your-image
# imagePullPolicy: Always
# ports:
# - name: portname
# containerPort: 1234
extraContainers: {}
## e.g.
# containerName:
# image: your-image
# imagePullPolicy: Always
# ports:
# - name: portname
# containerPort: 1234
priorityClassName: ''
dnsPolicy: ClusterFirst
podSecurityContext:
enabled: true
fsGroup: 2000
containerSecurityContext:
enabled: false
runAsUser: 1001
containerPorts:
- name: api
containerPort: 8065
protocol: TCP
- name: metrics
containerPort: 8067
protocol: TCP
- name: cluster
containerPort: 8075
protocol: TCP
- name: gossip
containerPort: 8074
protocol: TCP
service:
type: ClusterIP
port: 8065
targetPort: api
annotations: {}
labels: {}
ports:
- name: metrics
port: 8067
targetPort: metrics
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
revisionHistoryLimit: 2
service:
name: loop-app
type: ClusterIP
externalPort: 8065
internalPort: 8065
metricsPort: 8067
clusterPort: 8075
gossipPort: 8074
metricsName: loop-app-metrics
ingress:
# Используется для создания записи Ingress (работает с service.type: ClusterIP).
enabled: false
hosts: []
tls:
# Секреты должны быть созданы вручную в пространстве имён.
# - secretName: chart-example-tls
# hosts:
# - loop.example.com
ingress:
enabled: true
certManager: false
apiVersion:
ingressClassName: traefik
hostname: loop.example.com
path: /
pathType: ImplementationSpecific
annotations: {}
# kubernetes.io/ingress.class: nginx
# nginx.ingress.kubernetes.io/proxy-buffering: "on"
# nginx.ingress.kubernetes.io/proxy-body-size: 50m
# nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
# nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
# nginx.ingress.kubernetes.io/configuration-snippet: |
# proxy_cache loop_cache;
# proxy_cache_revalidate on;
# proxy_cache_min_uses 2;
# proxy_cache_use_stale timeout;
# proxy_cache_lock on;
ingressClassName: nginx
# cert-manager.io/cluster-issuer: ""
tls: false
autoscaling:
## extraHosts:
## - name: app.local
## path: /
## - name: app.local
## path: /
## servicePort: http
##
## extraPaths:
## - path: /*
## backend:
## serviceName: ssl-redirect
## servicePort: use-annotation
##
## extraTls:
## - hosts:
## - app.local
## secretName: app.local-tls
##
nginx:
configurationSnippet:
serverSnippet:
resources: {}
## resources:
## limits:
## cpu: 100m
## memory: 128Mi
## requests:
## cpu: 100m
## memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 1
targetMemoryUtilizationPercentage: 50
targetCPUUtilizationPercentage: 50
maxReplicas: 10
# targetCPU: 50
# targetMemory: 50
nodeSelector: {}
nodeSelector: {}
tolerations: []
topologySpreadConstraints: []
affinity: {}
## Allowed values: soft, hard
##
podAffinityPreset: ""
tolerations: []
## Allowed values: soft, hard
##
podAntiAffinityPreset: soft
securityContext:
fsGroup: 2000
## Allowed values: soft, hard
##
nodeAffinityPreset:
## Node affinity type
## Allowed values: soft, hard
##
type: ""
## Node label key to match
## E.g.
## key: "kubernetes.io/e2e-az-name"
##
key: ""
## Node label values to match
## E.g.
## values:
## - e2e-az1
## - e2e-az2
##
values: []
resources: {}
# limits:
# cpu: 100m
# memory: 300Mi
# requests:
# cpu: 100m
# memory: 300Mi
## Affinity for pod assignment
## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set
##
affinity: {}
extraInitContainers: []
# Дополнительный контейнер для загрузки и установки плагинов
# - name: init-plugins-config
# image: busybox
# imagePullPolicy: IfNotPresent
# command:
# - sh
# - "-c"
# - |
# PLUGINS_TAR="hovercardexample.tar.gz"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_1>"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_2>"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_N>"
env: {}
# for plugin_tar in ${PLUGINS_TAR};
# do
# wget ${plugin_tar} -P /loop/plugins
# cd /loop/plugins
# tar -xzvf ${plugin_tar##*/} # распаковка
# rm -f ${plugin_tar##*/} # удаление архива
# done
# volumeMounts:
# # Должны соответствовать volume'ам для плагинов
# - name: loop-plugins
# mountPath: loop/plugins/
# - name: loop-plugins-client
# mountPath: /loop/client/plugins/
## Array of sources to populate environment variables in the container from
## Ref: https://v1-18.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#envfromsource-v1-core
##
## envFrom:
## - configMapRef:
## name: special-config
## - secretRef:
## name: '{{ include "app.fullname" . }}-env'
##
envFrom:
- configMapRef:
name: '{{ template "app.fullname" . }}-configs'
- secretRef:
name: '{{ template "app.fullname" . }}-secrets'
# Добавление дополнительных томов и точек монтирования — например, для SAML ключей или других файлов
extraVolumes: []
# - hostPath:
# path: /var/log
# name: varlog
extraVolumeMounts: []
# - name: varlog
# mountPath: /host/var/log
# readOnly: true
checksums:
- secrets.yaml
- configmaps.yaml
## Дополнительные переменные окружения для LOOP
extraEnv:
- name: MM_ELASTICSEARCHSETTINGS_USERNAME
value: "" # Username Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_PASSWORD
value: "" # Password Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL
value: "" # URL к Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING
value: "false" # Использовать Elasticsearch для индексации
- name: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING
value: "false" # Использовать Elasticsearch для поиска
- name: MM_FILESETTINGS_AMAZONS3SSE
value: "false" # Использовать шифрование SSE для S3
- name: MM_FILESETTINGS_AMAZONS3SSL
value: "false" # Использовать SSL-подключение к S3
- name: MM_FILESETTINGS_DRIVERNAME
value: "amazons3" # Драйвер для файлового хранилища
- name: MM_FILESETTINGS_AMAZONS3ACCESSKEYID
value: "" # Access key для S3
- name: MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY
value: "" # Secret key для S3
- name: MM_FILESETTINGS_AMAZONS3BUCKET
value: "" # Название бакета S3
- name: MM_FILESETTINGS_AMAZONS3ENDPOINT
value: "" # Endpoint для доступа к S3
- name: MM_FILESETTINGS_MAXFILESIZE
value: "1048576000" # Максимальный размер файла в байтах
- name: MM_LOGSETTINGS_CONSOLEJSON
value: "false" # Вывод логов в JSON-формате в консоль
- name: MM_LOGSETTINGS_CONSOLELEVEL
value: "DEBUG" # Уровень логирования в консоли
- name: MM_LOGSETTINGS_FILELEVEL
value: "DEBUG" # Уровень логирования в файл
- name: MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS
value: "true" # Автоматическая загрузка встроенных плагинов
- name: MM_PLUGINSETTINGS_ENABLEUPLOADS
value: "true" # Разрешить загрузку плагинов
- name: MM_SERVICEENVIRONMENT
value: "production" # Среда окружения, например "production"
- name: MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION
value: "true" # Разрешить создание бот-аккаунтов
- name: MM_SERVICESETTINGS_ENABLEOAUTHSERVICEPROVIDER
value: "true" # Разрешить использование внешних OAuth-провайдеров
- name: MM_SERVICESETTINGS_ENABLETESTING
value: "true" # Разрешить тестирование
- name: MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS
value: "true" # Разрешить создание access токенов для пользователей
- name: MM_SERVICESETTINGS_GIPHYSDKKEY
value: "" # Ключ SDK Giphy для интеграции с Giphy
volumes: []
volumeMounts: []
## Следующие переменные требуются для запуска в кластерном режиме:
# - name: MM_CLUSTERSETTINGS_ENABLE
# value: "true"
# - name: MM_CLUSTERSETTINGS_CLUSTERNAME
# value: "loop-example-cluster"
# - name: MM_CLUSTERSETTINGS_ENABLEEXPERIMENTALGOSSIPENCRYPTION
# value: "true"
# - name: MM_CLUSTERSETTINGS_ENABLEGOSSIPCOMPRESSION
# value: "false"
# - name: MM_CLUSTERSETTINGS_READONLYCONFIG
# value: "false"
livenessProbe:
enabled: true
initialDelaySeconds: 90
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
httpGet:
path: /api/v4/system/ping
port: 8065
readinessProbe:
enabled: true
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
httpGet:
path: /api/v4/system/ping
port: 8065
## Дополнительные аннотации для Pod'а
extraPodAnnotations: {}
configMaps:
configs:
data:
MM_EMAILSETTINGS_PUSHNOTIFICATIONSERVER: "https://push.loop.ru" #Этот параметр указывает сервер, который отправляет push-уведомления
MM_EMAILSETTINGS_SENDPUSHNOTIFICATIONS: "true" # Включить email-уведомления
MM_EXPERIMENTALSETTINGS_ENABLEAPPBAR: "true" #Этот параметр указывает, включена ли панель приложения
MM_FEATUREFLAGS_AppsEnabled: "true" #Указывает на доступность и возможность использования определенных приложений или функций, например, доступ к галерее для загрузки изображений
MM_FILESETTINGS_AMAZONS3BUCKET: "looponprem-bucket" #Имя корзины S3, где хранятся файлы
MM_FILESETTINGS_AMAZONS3ENDPOINT: "loop-minio:9000" #URL конечной точки хранилища S3
MM_FILESETTINGS_AMAZONS3PATHPREFIX: "loop" #Эта опция позволяет указать путь к файлам на S3
MM_FILESETTINGS_AMAZONS3SSE: "false" #Параметр указывает, включено ли шифрование на стороне сервера (SSL) для S3
MM_FILESETTINGS_AMAZONS3SSL: "false" #Параметр указывает, должно ли подключение к Amazon S3 осуществляться через SSL
MM_FILESETTINGS_DRIVERNAME: "amazons3" #Имя драйвера для работы с файлами
MM_FILESETTINGS_MAXFILESIZE: "1048576000" #Максимальный размер файла для загрузки, указан в байтах
MM_JOBSETTINGS_RUNJOBS: "false" #Указывает, должны ли выполняться задания
MM_JOBSETTINGS_RUNSCHEDULER: "false" #Флаг указывает, должен ли быть запущен планировщик задач
MM_LOGSETTINGS_CONSOLEJSON: "false" #Флаг указывает, должен ли вывод в консоль быть в формате JSON
MM_LOGSETTINGS_CONSOLELEVEL: "DEBUG" #Уровень логирования для вывода в консоль
MM_LOGSETTINGS_FILELEVEL: "DEBUG" #Уровень логирования для записи в файл
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: "true" #Флаг указывает, разрешена ли автоматическая загрузка предустановленных плагинов
MM_PLUGINSETTINGS_ENABLEUPLOADS: "true" #Флаг указывает, разрешена ли загрузка плагинов
MM_SERVICEENVIRONMENT: "production" #Среда, в которой работает приложение (например, "production")
MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION: "true" #Флаг указывает, разрешено ли создание аккаунтов ботов
MM_SERVICESETTINGS_ENABLETEAMCREATION: "true" # Разрешить создание команд
MM_SERVICESETTINGS_ENABLEUSERCREATION: "true" # Разрешить создание пользователей
MM_SERVICESETTINGS_ENABLEOPENSERVER: "true" # Открытый доступ к серверу
MM_SERVICESETTINGS_ENABLECUSTOMEMOJI: "true" # Поддержка пользовательских эмодзи
MM_SERVICESETTINGS_ENABLELINKPREVIEWS: "true" # Предпросмотр ссылок
MM_SERVICESETTINGS_ENABLEOAUTHSERVICEPROVIDER: "true" #Используется для определения, разрешено ли использование сторонних провайдеров OAuth-сервисов
MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS: "true" #Флаг указывает, разрешены ли токены доступа пользователей
MM_SERVICESETTINGS_GIPHYSDKKEY: "<GiphySDKkey>" #Ключ Giphy SDK для интеграции с Giphy
MM_SERVICESETTINGS_LISTENADDRESS: ":8065" #Адрес и порт, на котором служба прослушивает
MM_SERVICESETTINGS_SITEURL: "https://loop.example.com" #Публичный URL службы
MM_SQLSETTINGS_DRIVERNAME: "postgres" #Имя драйвера базы данных
MM_TEAMSETTINGS_MAXUSERSPERTEAM: "500" #Максимальное количество пользователей в команде
MM_EMAILSETTINGS_REQUIREMAILVERIFICATION: "true" # Требовать подтверждение email
MM_EMAILSETTINGS_FEEDBACKEMAIL: "" # Email-адрес для сбора отзывов и обратной связи от пользователей
MM_EMAILSETTINGS_FEEDBACKNAME: "" # Имя отправителя, которое будет отображаться в письмах с отзывами
MM_EMAILSETTINGS_FEEDBACKORGANIZATION: "" # Название организации/компании, от имени которой отправляются письма с отзывами
MM_EMAILSETTINGS_SMTPUSERNAME: "" # SMTP логин
MM_EMAILSETTINGS_CONNECTIONSECURITY: "" # тип подключения: пусто, TLS или STARTTLS
MM_EMAILSETTINGS_SMTPPASSWORD: "" # SMTP пароль для аутентификации
MM_EMAILSETTINGS_ENABLESMTPAUTH: "" # Включить SMTP-аутентификацию (true/false)
MM_EMAILSETTINGS_SMTPPORT: "" # SMTP порт для подключения (например, 25, 465, 587)
MM_EMAILSETTINGS_REPLYTOADDRESS: "" # Адрес для ответа (куда будут приходить ответы на письма)
MM_EMAILSETTINGS_SMTPSERVER: "" # SMTP сервер для отправки писем (например, smtp.gmail.com)
## Для запуска Loop Calls OnPrem
MM_CLOUD_INSTALLATION_ID: "loop.example.com" # В качестве значения укажите домен вашего рабочего пространства, MM_SERVICESETTINGS_SITEURL.
serviceAccount:
create: false
name:
annotations: {}
## The following environment variables are required to use Elasticsearch for searching in loop.
# MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: ""
# MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING: "false"
# MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING: "false"
## The following environment variables are required to run loop in clustered mode.
# MM_CLUSTERSETTINGS_ENABLE: "true"
# MM_CLUSTERSETTINGS_CLUSTERNAME: "loop-example-cluster"
# MM_CLUSTERSETTINGS_ENABLEEXPERIMENTALGOSSIPENCRYPTION: "true"
# MM_CLUSTERSETTINGS_ENABLEGOSSIPCOMPRESSION: "false"
# MM_CLUSTERSETTINGS_READONLYCONFIG: "false"

View File

@ -3,17 +3,16 @@ appVersion: 9.11.1
dependencies:
- name: loop-enterprise-edition
repository: https://artifacts.wilix.dev/repository/helm-loop
version: 1.0.2
alias: loop-enterprise-edition
version: 2.0.0
- condition: postgresql.enabled
name: postgresql
repository: oci://registry-1.docker.io/bitnamicharts
version: 12.12.10
name: postgres
version: "1.5.12"
repository: https://groundhog2k.github.io/helm-charts/
alias: postgresql
- condition: minio.enabled
name: minio
repository: oci://registry-1.docker.io/bitnamicharts
version: 12.8.9
alias: loop-minio
version: "5.4.0"
repository: https://charts.min.io/
description: LOOP Enterprise server with high availability.
home: https://loop.ru
icon: https://artifacts.wilix.dev/repository/loop-files/assets/loop-icon.png
@ -29,4 +28,4 @@ maintainers:
name: stuart.armstrong
name: loop-enterprise-stack
type: application
version: 1.0.2
version: 2.0.0

View File

@ -1,13 +1,12 @@
Loop Enterprise Stack
====================================================
## ⚠️ **Version 2.0.0 of the loop-enterprise-stack chart, removes Bitnami Postgresql and Minio Helm charts and Bitnami image dependencies. The old chart can still be used.
---
## ⚠️ **This chart is not compatible with the previous loop chart and should not be used to upgrade the previous chart. All data can be lost**
## ⚠️ **This chart is not compatible with the previous loop chart 1.x.x and should not be used to upgrade the previous chart. All data can be lost. The migration procedure will provided in due course.**
## ⚠️ **The default values for minio and postgres are specified in the chart, they must be changed by you.
## ⚠️ **Этот chart несовместим с предыдущим loop chart, и не должн использоваться для обновления предыдущей версии loop chart. Все данные могут быть потеряны
## ⚠️ **Этот chart несовместим с предыдущим loop chart 1.x.x, и не должн использоваться для обновления предыдущей версии loop chart. Все данные могут быть потеряны
## ⚠️ **В chart указаны значения для minio и postgres по умолчанию, они должны быть изменены вами
---
@ -56,8 +55,7 @@ To run with HTTPS you will need to add a Kubernetes secret for your SSL/TLS cert
At minimum the following settings must be updated:
* `global.siteURL` - set this to the URL your users will use to access Loop, e.g. `https://loop.example.com`
* `global.loopLicense` - set this to the contents of your license file or provide an existing secret. `global.existingLicenseSecret.name` `global.existingLicenseSecret.key`
* `MM_SERVICESETTINGS_SITEURL:` - set this to the URL your users will use to access Loop, e.g. `https://loop.example.com`
* Database Configuration
* Object Storage Configuration
@ -67,11 +65,11 @@ Without these settings, Loop will not run correctly.
To set the Loop application version, update:
* `loopApp.image.tag` - set this to the Loop server version you wish to install (e.g. `7.10.2`)
* `image.tag` - set this to the Loop server version you wish to install (e.g. `2025-06-10-1`)
## 2.3 Ingress
If you are using nginx-ingress, set the following under `loopApp`:
If you are using nginx-ingress, set the following:
```yaml
ingress:
@ -80,13 +78,13 @@ ingress:
- loop.example.com
```
where `loop.example.com` is your domain name and matches `global.siteURL`.
where `loop.example.com` is your domain name and matches `MM_SERVICESETTINGS_SITEURL:`.
### 2.3.1 HTTPS
To run with HTTPS, add an SSL/TLS certificate as a secret to your Kubernetes cluster, either manually or [using cert-manager](#14-certificate-manager).
Set the following under `loopApp` to enable HTTPS:
Set the following to enable HTTPS:
```yaml
ingress:
@ -115,13 +113,19 @@ Depending on the DNS service and Ingress you're using, the steps can vary. If yo
### 2.4.1 Database
We use the Bitnami Postgresql chart.
We recommend updating the following settings:
* `postgresql.global.postgresql.auth.password`
* `postgresql.global.postgresql.auth.user`
* `postgresql.global.postgresql.auth.database`
Loop:
* Set `MM_SQLSETTINGS_DRIVERNAME:` to `postgres`
* Set `MM_SQLSETTINGS_DATASOURCE:`, in secret-values.yaml, to your master DB connection string
* (Optional) Set `MM_SQLSETTINGS_DATASOURCEREPLICAS:` to an array of read replica connection strings
Postgresql:
* `postgresql.userDatabase.name`
* `postgresql.userDatabase.user`
* `postgresql.userDatabase.password`
### 2.4.2 Storage
@ -129,10 +133,20 @@ We use Minio for file storage.
We recommend updating the following settings:
* `loop-minio.accessKey`
* `loop-minio.secretKey`
* `loop-minio.defaultBuckets`
* `loop-minio.provisioning.users`
Loop:
* `MM_FILESETTINGS_AMAZONS3ACCESSKEYID:`
* `MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY:`
* `MM_FILESETTINGS_AMAZONS3BUCKET:`
* `MM_FILESETTINGS_AMAZONS3ENDPOINT:`
Minio:
* `rootUser:`
* `rootPassword:`
```yaml
users:
- accessKey:
secretKey:
```
## 2.5 Push Notifications
@ -154,7 +168,7 @@ $ helm install <name> loop/loop-enterprise-stack --version <version_number>
For example:
```bash
$ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
$ helm install <name> loop/loop-enterprise-stack --version v0.8.2
$ helm install <name> loop/loop-enterprise-stack --version 2.0.0
```
If no Helm Chart version is specified the latest version will be installed.
@ -220,27 +234,31 @@ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
## 2.1 Обязательные настройки
- `global.siteURL` — установите в адрес, по которому пользователи будут получать доступ к Loop, например:
**Минимально необходимые настройки:**
-- `MM_SERVICESETTINGS_SITEURL:` — установите в адрес, по которому пользователи будут получать доступ к Loop, например:
`https://loop.example.com`
- `global.loopLicense` — укажите содержимое лицензионного файла **или** задайте существующий Kubernetes-секрет с ключом:
`global.existingLicenseSecret.name` и `global.existingLicenseSecret.key`
- **Конфигурация базы данных (раздел 2.4.1)** — необходимо настроить подключение к PostgreSQL.
Это может быть внешний сервер или встроенный через Helm-чарт.
- **Конфигурация базы данных** — необходимо настроить подключение к PostgreSQL.
- **Конфигурация объектного хранилища (раздел 2.6.1)** — нужно подключить MinIO или другое совместимое S3-хранилище.
- **Конфигурация объектного хранилища** — нужно подключить MinIO или другое совместимое S3-хранилище.
Без этих параметров Loop **не будет работать корректно**
Без этих параметров Loop **не будет работать корректно** — сервис не сможет ни загрузить данные, ни сохранить файлы.
## 2.2 Версия приложения
Чтобы указать версию Loop, установите:
Укажите тег образа Loop:
```yaml
image:
tag: "2025-06-10-1" # Например, 2025-06-10-1
```
* `loopApp.image.tag` — укажите версию Loop-сервера, которую хотите установить (например: `7.10.2`)
## 2.3 Настройка Ingress
## 2.3 Ingress
### **2.3.1 Ingress с nginx ingress controller**
Если вы используете nginx-ingress, настройте следующее внутри `loopApp`:
Если вы используете `nginx-ingress-controller`, добавьте следующее:
```yaml
ingress:
@ -249,13 +267,14 @@ ingress:
- loop.example.com
```
где `loop.example.com` — это ваше доменное имя, совпадающее с `global.siteURL`.
Здесь `loop.example.com` — это ваш домен, и он **должен совпадать с `MM_SERVICESETTINGS_SITEURL:`** (например, `https://loop.example.com`).
### 2.3.1 HTTPS
### **2.3.2 HTTPS (SSL/TLS)**
Для работы по HTTPS добавьте SSL/TLS сертификат в виде секрета в кластер Kubernetes вручную или [с помощью cert-manager](#14-certificate-manager).
Чтобы использовать HTTPS:
Чтобы включить HTTPS, укажите следующее в `loopApp`:
1. Создайте TLS-секрет в Kubernetes **вручную** или с помощью **cert-manager**.
2. Укажите его в `values.yaml` Helm-чарта:
```yaml
ingress:
@ -268,6 +287,8 @@ ingress:
- loop.example.com
```
`secretName` — это имя созданного TLS-секрета с ключом и сертификатом (например, от Let's Encrypt или корпоративного CA).
### 2.3.2 DNS
Чтобы направить пользователей с вашего домена на установку Loop, укажите домен на внешний IP или имя хоста, которое предоставляет ваш ingress.
@ -285,13 +306,21 @@ ingress:
### 2.4.1 База данных
Мы используем чарт Bitnami Postgresql.
Мы используем чарт Postgresql.
Рекомендуется обновить следующие параметры:
* `postgresql.global.postgresql.auth.password`
* `postgresql.global.postgresql.auth.user`
* `postgresql.global.postgresql.auth.database`
Loop:
* Установите `MM_SQLSETTINGS_DRIVERNAME:` в значение postgres
* Настройте `MM_SQLSETTINGS_DATASOURCE:` в файле secret-values.yaml, указав строку подключения к основной базе данных
* (Опционально) Настройте `MM_SQLSETTINGS_DATASOURCEREPLICAS:` как массив строк подключения к репликам для чтения
Postgresql:
* `postgresql.userDatabase.name`
* `postgresql.userDatabase.user`
* `postgresql.userDatabase.password`
### 2.4.2 Хранилище
@ -299,10 +328,20 @@ ingress:
Рекомендуется обновить следующие параметры:
* `loop-minio.accessKey`
* `loop-minio.secretKey`
* `loop-minio.defaultBuckets`
* `loop-minio.provisioning.users`
Loop:
* `MM_FILESETTINGS_AMAZONS3ACCESSKEYID:`
* `MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY:`
* `MM_FILESETTINGS_AMAZONS3BUCKET:`
* `MM_FILESETTINGS_AMAZONS3ENDPOINT:`
Minio:
* `rootUser:`
* `rootPassword:`
```yaml
users:
- accessKey:
secretKey:
```
## 2.5 Push-уведомления
@ -321,7 +360,7 @@ $ helm install <name> loop/loop-enterprise-stack --version <version_number>
```bash
$ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
$ helm install <name> loop/loop-enterprise-stack --version v0.8.2
$ helm install <name> loop/loop-enterprise-stack --version 2.0.0
```
Если версия чарта не указана, будет установлена последняя доступная.

View File

@ -0,0 +1,10 @@
loop-enterprise-edition:
secrets:
secrets:
stringData:
MM_SQLSETTINGS_DATASOURCE: "postgres://loopuser:looppassword@loop-database.<namespace>.svc.cluster.local:5432/loopdb?sslmode=disable" #Data source for connecting to the PostgreSQL database
#MM_SQLSETTINGS_DATASOURCEREPLICAS: ""
MM_FILESETTINGS_AMAZONS3ACCESSKEYID: "accesskey-minio" #Access Key ID to access s3 storage
MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY: "secretkey-minio" #The secret key for accessing S3 storage
MM_LICENSE: ""
MM_CONFIG: "postgres://loopuser:looppassword@loop-database.<namespace>.svc.cluster.local:5432/loopdb?sslmode=disable" #Store the configuration in the database. Use MM_SQLSETTINGS_DATASOURCE

View File

@ -0,0 +1,120 @@
{{- if .Values.mcJob.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: loop-minio-mc-job
labels:
app: loop-minio
spec:
template:
metadata:
labels:
app: loop-minio
spec:
containers:
- name: mc-client
image: "minio/mc:RELEASE.2025-01-17T23-25-50Z"
command: ["/bin/sh", "-c"]
args:
- |
until mc alias set myminio http://loop-minio:9000 {{ .Values.minio.rootUser }} {{ .Values.minio.rootPassword }}; do
echo "Waiting for MinIO to be ready..."
sleep 5
done
echo "MinIO is ready and alias is set."
# Создание пользователя
#if ! mc admin user add myminio {{ .Values.minio.customUser }} {{ .Values.minio.customAccessKey }}; then
# echo "User {{ .Values.minio.customUser }} already exists or failed to create."
#else
# echo "User {{ .Values.minio.customUser }} created successfully."
#fi
# Назначение политики для нового пользователя
cat <<EOF > /tmp/minio-user-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor",
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::loop-bucket/*"
]
}
]
}
EOF
echo "User policy JSON file created."
mc admin policy create myminio loop-policy /tmp/minio-user-policy.json
echo "User policy created and applied."
# Создание бакета
if ! mc ls myminio/loop-bucket; then
mc mb myminio/loop-bucket
echo "Bucket loop-bucket created successfully."
else
echo "Bucket loop-bucket already exists."
fi
# Установка политик для бакета
cat <<EOF > /tmp/minio-bucket-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::loop-bucket"
]
},
{
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::loop-bucket/*"
]
}
]
}
EOF
echo "Bucket policy JSON file created."
mc anonymous set-json /tmp/minio-bucket-policy.json myminio/loop-bucket
echo "Bucket policy applied."
mc admin policy attach myminio loop-policy --user={{ .Values.minio.customUser }}
echo "Policy attached to user {{ .Values.minio.customUser }}."
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
restartPolicy: OnFailure
backoffLimit: 5
ttlSecondsAfterFinished: 180
{{- end }}

View File

@ -1,265 +1,232 @@
global:
siteUrl: "loop.example.com" # REQUIRED SETTING, PLEASE UPDATE: "https://loop.example.com"
# Use an existing secret for License credential (see secret-loop-license.yaml for required fields)
existingLicenseSecret: {}
# name: ""
# key: ""
# REQUIRED SETTING, if no existing secret is provided.
loopLicense: ""
# Settings for configuration of LOOP
# For more details: https://docs.loop.ru/administration/config-settings
siteName: ""
enableTeamCreation: true
enableUserCreation: true
enableOpenServer: true
enableLinkPreviews: true
enableCustomEmoji: true
# Settings to enable email notifications
sendEmailNotifications: false
requireEmailVerification: false
feedbackName: ""
feedbackEmail: ""
feedbackOrganization: ""
smtpUsername: ""
smtpPassword: ""
smtpServer: ""
smtpPort: ""
connectionSecurity: "" # empty, TLS, or STARTTLS
features:
database:
external:
driver: "postgres"
dataSource: "loopuser:loop-password@loop-database:5432/loop-db?sslmode=disable" # <username>:<password>@@loop-database.<namespace>.svc.cluster.local:5432/<dbname>?connect_timeout=10&sslmode=disable
dataSourceReplicas: ""
existingDatabaseSecret: {}
jobserver:
name: jobserver
enabled: true
replicaCount: 1
strategy:
type: Recreate
rollingUpdate: null
revisionHistoryLimit: 2
service:
name: loop-app-jobserver
type: ClusterIP
nodeSelector: {}
affinity: {}
tolerations: []
extraEnv: {}
notifications:
# Push proxy must be configured or useHPNS must be true for push noticiations to work.
push:
enabled: true
# Hosted push notification service. Requires an enterprise license.
# More details at https://docs.loop.ru/mobile/mobile-hpns.
useHPNS: true
initContainerImage:
repository: appropriate/curl
tag: latest
imagePullPolicy: IfNotPresent
# LOOP deployment section.
# Use to configure the deploymento the LOOP servers.
loop-enterprise-edition:
loopApp:
replicaCount: 1
image:
repository: registry.loop.ru/loop/server
tag: 2025-06-10-1
registry: registry.loop.ru
repository: loop/server
tag: 2025-11-20-1
pullPolicy: IfNotPresent
strategy:
name: loop-server
appKind: Deployment
containerPorts:
- name: api
containerPort: 8065
protocol: TCP
- name: metrics
containerPort: 8067
protocol: TCP
- name: cluster
containerPort: 8075
protocol: TCP
- name: gossip
containerPort: 8074
protocol: TCP
service:
type: ClusterIP
port: 8065
targetPort: api
annotations: {}
labels: {}
ports:
- name: metrics
port: 8067
targetPort: metrics
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
revisionHistoryLimit: 2
service:
name: loop-app
type: ClusterIP
externalPort: 8065
internalPort: 8065
metricsPort: 8067
clusterPort: 8075
gossipPort: 8074
metricsName: loop-app-metrics
ingress:
# Used to create Ingress record (should be used with service.type: ClusterIP).
enabled: true
hosts:
- loop.example.com
tls:
# Secrets must be manually created in the namespace.
# - secretName: chart-example-tls
# hosts:
# - loop.example.com
# annotations:
# cert-manager.io/cluster-issuer: ""
certManager: false
apiVersion:
ingressClassName: traefik
hostname: loop.example.com
path: /
pathType: ImplementationSpecific
annotations: {}
ingressClassName: nginx
tls: false
resources: {}
## resources:
## limits:
## cpu: 100m
## memory: 128Mi
## requests:
## cpu: 100m
## memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 1
targetMemoryUtilizationPercentage: 50
targetCPUUtilizationPercentage: 50
maxReplicas: 10
# targetCPU: 50
# targetMemory: 50
nodeSelector: {}
envFrom:
- configMapRef:
name: '{{ template "app.fullname" . }}-configs'
- secretRef:
name: '{{ template "app.fullname" . }}-secrets'
affinity: {}
checksums:
- secrets.yaml
- configmaps.yaml
tolerations: []
livenessProbe:
enabled: true
initialDelaySeconds: 90
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
httpGet:
path: /api/v4/system/ping
port: 8065
readinessProbe:
enabled: true
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
httpGet:
path: /api/v4/system/ping
port: 8065
resources: {}
# limits:
# cpu: 100m
# memory: 300Mi
# requests:
# cpu: 100m
# memory: 300Mi
configMaps:
configs:
data:
MM_EMAILSETTINGS_PUSHNOTIFICATIONSERVER: "https://push.loop.ru" #Этот параметр указывает сервер, который отправляет push-уведомления
MM_EMAILSETTINGS_SENDPUSHNOTIFICATIONS: "true" # Включить email-уведомления
MM_EXPERIMENTALSETTINGS_ENABLEAPPBAR: "true" #Этот параметр указывает, включена ли панель приложения
MM_FEATUREFLAGS_AppsEnabled: "true" #Указывает на доступность и возможность использования определенных приложений или функций, например, доступ к галерее для загрузки изображений
MM_FILESETTINGS_AMAZONS3BUCKET: "looponprem-bucket" #Имя корзины S3, где хранятся файлы
MM_FILESETTINGS_AMAZONS3ENDPOINT: "loop-minio:9000" #URL конечной точки хранилища S3
MM_FILESETTINGS_AMAZONS3PATHPREFIX: "loop" #Эта опция позволяет указать путь к файлам на S3
MM_FILESETTINGS_AMAZONS3SSE: "false" #Параметр указывает, включено ли шифрование на стороне сервера (SSL) для S3
MM_FILESETTINGS_AMAZONS3SSL: "false" #Параметр указывает, должно ли подключение к Amazon S3 осуществляться через SSL
MM_FILESETTINGS_DRIVERNAME: "amazons3" #Имя драйвера для работы с файлами
MM_FILESETTINGS_MAXFILESIZE: "1048576000" #Максимальный размер файла для загрузки, указан в байтах
MM_JOBSETTINGS_RUNJOBS: "false" #Указывает, должны ли выполняться задания
MM_JOBSETTINGS_RUNSCHEDULER: "false" #Флаг указывает, должен ли быть запущен планировщик задач
MM_LOGSETTINGS_CONSOLEJSON: "false" #Флаг указывает, должен ли вывод в консоль быть в формате JSON
MM_LOGSETTINGS_CONSOLELEVEL: "DEBUG" #Уровень логирования для вывода в консоль
MM_LOGSETTINGS_FILELEVEL: "DEBUG" #Уровень логирования для записи в файл
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: "true" #Флаг указывает, разрешена ли автоматическая загрузка предустановленных плагинов
MM_PLUGINSETTINGS_ENABLEUPLOADS: "true" #Флаг указывает, разрешена ли загрузка плагинов
MM_SERVICEENVIRONMENT: "production" #Среда, в которой работает приложение (например, "production")
MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION: "true" #Флаг указывает, разрешено ли создание аккаунтов ботов
MM_SERVICESETTINGS_ENABLETEAMCREATION: "true" # Разрешить создание команд
MM_SERVICESETTINGS_ENABLEUSERCREATION: "true" # Разрешить создание пользователей
MM_SERVICESETTINGS_ENABLEOPENSERVER: "true" # Открытый доступ к серверу
MM_SERVICESETTINGS_ENABLECUSTOMEMOJI: "true" # Поддержка пользовательских эмодзи
MM_SERVICESETTINGS_ENABLELINKPREVIEWS: "true" # Предпросмотр ссылок
MM_SERVICESETTINGS_ENABLEOAUTHSERVICEPROVIDER: "true" #Используется для определения, разрешено ли использование сторонних провайдеров OAuth-сервисов
MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS: "true" #Флаг указывает, разрешены ли токены доступа пользователей
MM_SERVICESETTINGS_GIPHYSDKKEY: "<GiphySDKkey>" #Ключ Giphy SDK для интеграции с Giphy
MM_SERVICESETTINGS_LISTENADDRESS: ":8065" #Адрес и порт, на котором служба прослушивает
MM_SERVICESETTINGS_SITEURL: "https://loop.example.com" #Публичный URL службы
MM_SQLSETTINGS_DRIVERNAME: "postgres" #Имя драйвера базы данных
MM_TEAMSETTINGS_MAXUSERSPERTEAM: "500" #Максимальное количество пользователей в команде
MM_EMAILSETTINGS_REQUIREMAILVERIFICATION: "true" # Требовать подтверждение email
MM_EMAILSETTINGS_FEEDBACKEMAIL: "" # Email-адрес для сбора отзывов и обратной связи от пользователей
MM_EMAILSETTINGS_FEEDBACKNAME: "" # Имя отправителя, которое будет отображаться в письмах с отзывами
MM_EMAILSETTINGS_FEEDBACKORGANIZATION: "" # Название организации/компании, от имени которой отправляются письма с отзывами
MM_EMAILSETTINGS_SMTPUSERNAME: "" # SMTP логин
MM_EMAILSETTINGS_CONNECTIONSECURITY: "" # тип подключения: пусто, TLS или STARTTLS
MM_EMAILSETTINGS_SMTPPASSWORD: "" # SMTP пароль для аутентификации
MM_EMAILSETTINGS_ENABLESMTPAUTH: "" # Включить SMTP-аутентификацию (true/false)
MM_EMAILSETTINGS_SMTPPORT: "" # SMTP порт для подключения (например, 25, 465, 587)
MM_EMAILSETTINGS_REPLYTOADDRESS: "" # Адрес для ответа (куда будут приходить ответы на письма)
MM_EMAILSETTINGS_SMTPSERVER: "" # SMTP сервер для отправки писем (например, smtp.gmail.com)
## Для запуска Loop Calls OnPrem
MM_CLOUD_INSTALLATION_ID: "loop.example.com" # В качестве значения укажите домен вашего рабочего пространства, MM_SERVICESETTINGS_SITEURL.
extraInitContainers: []
## The following environment variables are required to use Elasticsearch for searching in loop.
# MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: ""
# MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING: "false"
# MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING: "false"
# Add additional volumes and mounts, for example to add SAML keys in the app or other files the app server may need to access
extraVolumes: []
# - hostPath:
# path: /var/log
# name: varlog
extraVolumeMounts: []
# - name: varlog
# mountPath: /host/var/log
# readOnly: true
## Allows the specification of additional environment variables for LOOP
extraEnv:
- name: MM_ELASTICSEARCHSETTINGS_USERNAME
value: "" # Username Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_PASSWORD
value: "" # Password Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL
value: "" # Elasticsearch URL
- name: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING
value: "false" # Use Elasticsearch indexing
- name: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING
value: "false" # Use Elasticsearch for searching
- name: MM_FILESETTINGS_AMAZONS3SSE
value: "false" #Env indicates whether SSL (Server-Side Encryption) is enabled for S3
- name: MM_FILESETTINGS_AMAZONS3SSL
value: "false" #Env indicates whether the connection to Amazon S3 should take place over SSL
- name: MM_FILESETTINGS_DRIVERNAME
value: "amazons3" #The name of the driver for working with files
- name: MM_FILESETTINGS_AMAZONS3ACCESSKEYID
value: "accesskey-minio" # S3 access key
- name: MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY
value: "secretkey-minio" # S3 secretaccess key
- name: MM_FILESETTINGS_AMAZONS3BUCKET
value: "loop-bucket" # S3 bucket name
- name: MM_FILESETTINGS_AMAZONS3ENDPOINT
value: "loop-minio:9000" # S3 endpoint url
- name: MM_FILESETTINGS_MAXFILESIZE
value: "1048576000" #The maximum file size to download, specified in bytes.
- name: MM_LOGSETTINGS_CONSOLEJSON
value: "false" #The flag indicates whether the output to the console should be in JSON format
- name: MM_LOGSETTINGS_CONSOLELEVEL
value: "DEBUG" #Logging level for console output
- name: MM_LOGSETTINGS_FILELEVEL
value: "DEBUG" #The logging level for writing to a file
- name: MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS
value: "true" #The flag indicates whether automatic loading of pre-packaged plugins is allowed
- name: MM_PLUGINSETTINGS_ENABLEUPLOADS
value: "true" #The flag indicates whether plugin downloads are allowed
- name: MM_SERVICEENVIRONMENT
value: "production" #The environment in which the application is running (for example, "production")
- name: MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION
value: "true" #The flag indicates whether the creation of bot accounts is allowed
- name: MM_SERVICESETTINGS_ENABLEOAUTHSERVICEPROVIDER
value: "true" #It is used to determine whether the use of third-party OAuth service providers is allowed
- name: MM_SERVICESETTINGS_ENABLETESTING
value: "true" #The flag indicates whether testing is allowed
- name: MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS
value: "true" #The flag indicates whether user access tokens are allowed
- name: MM_SERVICESETTINGS_GIPHYSDKKEY
value: "" #Giphy SDK key for integration with Giphy
## The following environment variables are required to run loop in clustered mode.
# - name: MM_CLUSTERSETTINGS_ENABLE
# value: "true"
# - name: MM_CLUSTERSETTINGS_CLUSTERNAME
# value: "loop-example-cluster"
# - name: MM_CLUSTERSETTINGS_ENABLEEXPERIMENTALGOSSIPENCRYPTION
# value: "true"
# - name: MM_CLUSTERSETTINGS_ENABLEGOSSIPCOMPRESSION
# value: "false"
# - name: MM_CLUSTERSETTINGS_READONLYCONFIG
# value: "false"
## Additional pod annotations
extraPodAnnotations: {}
# MM_CLUSTERSETTINGS_ENABLE: "true"
# MM_CLUSTERSETTINGS_CLUSTERNAME: "loop-example-cluster"
# MM_CLUSTERSETTINGS_ENABLEEXPERIMENTALGOSSIPENCRYPTION: "true"
# MM_CLUSTERSETTINGS_ENABLEGOSSIPCOMPRESSION: "false"
# MM_CLUSTERSETTINGS_READONLYCONFIG: "false"
# Postgresql Section. Use this to configure postgresql.
# If you want to use an external database, disable this.
postgresql:
enabled: true
global:
postgresql:
auth:
database: "loop-db"
username: "loopuser"
password: "loop-password"
name: loop-database
fullnameOverride: loop-database
nameOverride: loop-database
primary:
persistence:
size: 500Mi
settings:
superuserPassword:
value: "supersecretpassword"
userDatabase:
name:
value: "loopdb"
user:
value: "loopuser"
password:
value: "looppassword"
storage:
requestedSize: 10Gi
className: ""
resources:
limits:
cpu: 500m
memory: 1Gi
cpu: 2
memory: 5Gi
requests:
cpu: 250m
memory: 256Mi
cpu: 500m
memory: 512Mi
# Minio Section. Use this to configure Minio storage.
# If you want to use external storage, disable this.
loop-minio:
minio:
enabled: true
accessKey: "accesskey-minio"
secretKey: "secretkey-minio"
name: loop-minio
fullnameOverride: loop-minio
nameOverride: loop-minio
persistence:
enabled: true
storageClass: ""
accessMode: ReadWriteOnce
size: 10Gi
resources:
requests:
memory: 256Mi
cpu: 250m
defaultBuckets: "loop-bucket"
provisioning:
enabled: true
users:
- username: accesskey-minio
password: secretkey-minio
disabled: false
policies:
- readwrite
serviceAccount:
create: false
name:
annotations: {}
mode: standalone
rootUser: admin
rootPassword: supersecretpassword
users:
- accessKey: accesskey-minio
secretKey: secretkey-minio
policy: readwrite
persistence:
enabled: true
annotations:
helm.sh/resource-policy: keep
storageClass: ""
size: 10Gi
mcJob:
enabled: true