From 1552133e20904c1502b89c7ceb2903c2200d7a01 Mon Sep 17 00:00:00 2001 From: Alexandr Vnukov Date: Sun, 28 Aug 2022 03:32:29 +0300 Subject: [PATCH] First public version of Helm Apps Library --- .github/workflows/release.yml | 49 + .gitignore | 5 + LICENSE | 201 ++ Makefile | 4 + README.md | 343 +++ charts/helm-apps/Chart.yaml | 9 + .../helm-apps/templates/_apps-adopt-utils.tpl | 9 + .../templates/_apps-certificates.tpl | 15 + .../helm-apps/templates/_apps-components.tpl | 254 +++ .../helm-apps/templates/_apps-configmaps.tpl | 21 + charts/helm-apps/templates/_apps-cronjobs.tpl | 38 + .../helm-apps/templates/_apps-deckhouse.tpl | 16 + .../templates/_apps-default-values.yaml | 166 ++ charts/helm-apps/templates/_apps-dex.tpl | 53 + .../helm-apps/templates/_apps-fl-wrappers.tpl | 27 + charts/helm-apps/templates/_apps-helpers.tpl | 282 +++ charts/helm-apps/templates/_apps-infra.tpl | 83 + .../helm-apps/templates/_apps-ingresses.tpl | 80 + charts/helm-apps/templates/_apps-jobs.tpl | 30 + .../templates/_apps-kafka-strimzi.tpl | 226 ++ .../helm-apps/templates/_apps-limit-range.tpl | 19 + .../helm-apps/templates/_apps-prometheus.tpl | 57 + charts/helm-apps/templates/_apps-pvcs.tpl | 23 + charts/helm-apps/templates/_apps-secrets.tpl | 27 + charts/helm-apps/templates/_apps-specs.tpl | 58 + charts/helm-apps/templates/_apps-stateful.tpl | 59 + .../helm-apps/templates/_apps-stateless.tpl | 61 + charts/helm-apps/templates/_apps-system.tpl | 41 + charts/helm-apps/templates/_apps-utils.tpl | 236 ++ charts/helm-apps/templates/_apps-version.tpl | 3 + .../fl-functions/_expandIncludesInValues.tpl | 154 ++ .../fl-functions/_formatStringAsDNSLabel.tpl | 9 + .../_formatStringAsDNSSubdomain.tpl | 9 + .../templates/fl-functions/_isFalse.tpl | 3 + .../templates/fl-functions/_isTrue.tpl | 3 + .../templates/fl-functions/_percentage.tpl | 12 + .../templates/fl-functions/_value.tpl | 82 + .../templates/fl-functions/_valueQuoted.tpl | 6 + .../fl-functions/_valueSingleQuoted.tpl | 6 + .../fl-snippets/_generateConfigMapEnvVars.tpl | 17 + .../fl-snippets/_generateContainerEnvVars.tpl | 21 + .../_generateContainerFromSecretsEnvVars.tpl | 19 + .../fl-snippets/_generateContainerImage.tpl | 12 + .../_generateContainerResources.tpl | 15 + .../templates/fl-snippets/_generateLabels.tpl | 8 + .../fl-snippets/_generateSecretData.tpl | 17 + .../fl-snippets/_generateSecretEnvVars.tpl | 3 + .../fl-snippets/_generateSelectorLabels.tpl | 6 + charts/helm-apps/values.yaml | 1 + cr.yaml | 1 + docs/example/.gitlab-ci.yml | 14 + docs/example/.helm/Chart.yaml | 7 + .../templates/init-flant-apps-library.yaml | 2 + docs/example/.helm/values.yaml | 123 ++ docs/example/werf.yaml | 2 + docs/usage.md | 71 + tests/.helm/Chart.lock | 6 + tests/.helm/Chart.yaml | 11 + .../templates/init-flant-apps-library.yaml | 2 + tests/.helm/values.yaml | 1914 +++++++++++++++++ tests/werf.yaml | 5 + 61 files changed, 5056 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 charts/helm-apps/Chart.yaml create mode 100644 charts/helm-apps/templates/_apps-adopt-utils.tpl create mode 100644 charts/helm-apps/templates/_apps-certificates.tpl create mode 100644 charts/helm-apps/templates/_apps-components.tpl create mode 100644 charts/helm-apps/templates/_apps-configmaps.tpl create mode 100644 charts/helm-apps/templates/_apps-cronjobs.tpl create mode 100644 charts/helm-apps/templates/_apps-deckhouse.tpl create mode 100644 charts/helm-apps/templates/_apps-default-values.yaml create mode 100644 charts/helm-apps/templates/_apps-dex.tpl create mode 100644 charts/helm-apps/templates/_apps-fl-wrappers.tpl create mode 100644 charts/helm-apps/templates/_apps-helpers.tpl create mode 100644 charts/helm-apps/templates/_apps-infra.tpl create mode 100644 charts/helm-apps/templates/_apps-ingresses.tpl create mode 100644 charts/helm-apps/templates/_apps-jobs.tpl create mode 100644 charts/helm-apps/templates/_apps-kafka-strimzi.tpl create mode 100644 charts/helm-apps/templates/_apps-limit-range.tpl create mode 100644 charts/helm-apps/templates/_apps-prometheus.tpl create mode 100644 charts/helm-apps/templates/_apps-pvcs.tpl create mode 100644 charts/helm-apps/templates/_apps-secrets.tpl create mode 100644 charts/helm-apps/templates/_apps-specs.tpl create mode 100644 charts/helm-apps/templates/_apps-stateful.tpl create mode 100644 charts/helm-apps/templates/_apps-stateless.tpl create mode 100644 charts/helm-apps/templates/_apps-system.tpl create mode 100644 charts/helm-apps/templates/_apps-utils.tpl create mode 100644 charts/helm-apps/templates/_apps-version.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_expandIncludesInValues.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_formatStringAsDNSLabel.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_formatStringAsDNSSubdomain.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_isFalse.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_isTrue.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_percentage.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_value.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_valueQuoted.tpl create mode 100644 charts/helm-apps/templates/fl-functions/_valueSingleQuoted.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateConfigMapEnvVars.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateContainerEnvVars.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateContainerFromSecretsEnvVars.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateContainerImage.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateContainerResources.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateLabels.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateSecretData.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateSecretEnvVars.tpl create mode 100644 charts/helm-apps/templates/fl-snippets/_generateSelectorLabels.tpl create mode 100644 charts/helm-apps/values.yaml create mode 100644 cr.yaml create mode 100644 docs/example/.gitlab-ci.yml create mode 100644 docs/example/.helm/Chart.yaml create mode 100644 docs/example/.helm/templates/init-flant-apps-library.yaml create mode 100644 docs/example/.helm/values.yaml create mode 100644 docs/example/werf.yaml create mode 100644 docs/usage.md create mode 100644 tests/.helm/Chart.lock create mode 100644 tests/.helm/Chart.yaml create mode 100644 tests/.helm/templates/init-flant-apps-library.yaml create mode 100644 tests/.helm/values.yaml create mode 100644 tests/werf.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..450103a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release Charts + +on: + push: + branches: + - main + +jobs: + release: + runs-on: self-hosted + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Set lib version + run: | + LIB_VERSION=$(sed -n '/version/{s/version: //;p;}' charts/helm-apps/Chart.yaml) + sed -i 's/_FLANT_APPS_LIBRARY_VERSION_/'${LIB_VERSION}'/' charts/helm-apps/templates/_apps-version.tpl + + - name: Install werf CLI + with: + channel: ea + uses: werf/actions/install@v1.2 + + - name: Render + run: | + source $(werf ci-env github --as-file) + pushd tests && werf render --validate --dev --set "global._includes.apps-defaults.enabled=true" + popd + + - name: Install Helm + uses: azure/setup-helm@v1 + with: + version: v3.8.1 + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.4.0 + with: + charts_dir: charts + config: cr.yaml + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4681f90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.helm/charts/*/charts +.helm/charts/*.tgz +**/*/charts +**.tgz +/.packages/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d6d0cad --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: deps +deps: + werf helm dependency update charts/helm-apps + werf helm dependency update tests.helm diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc01795 --- /dev/null +++ b/README.md @@ -0,0 +1,343 @@ +## Репозиторий библиотеки для развертывания приложений в Kubernetes. +1. Позволяет: + * упростить структуру описания приложения. + * переиспользовать шаблоны одного приложения для множества других +2. Ускоряет: + * процесс ревью изменений приложения за счет стандартизирования подхода и уменьшению количества кода. + * развертывание новых приложений за счет лаконичного синтаксиса, сокращения повторяемого кода + * редактирование и добавление новых ресурсов к приложению +3. Упрощает: + * работу с сущностями Kubernetes (не нужно описывать все поля приложения, не нужно думать как правильно выглядит конструкции сущностей). + * связывание сущностей Kubernetes за счет использования хелперов + +> :warning: **На данный момент корректная работа чартов гарантируется только с утилитой** [**Werf**](https://werf.io) + +## Для подключения библиотеки необходимо: +### Инструкция по использованию +#### Использовать пример: +* Скопировать содержимое папки [docs/example](/docs/example) в корень нового проекта +* настроить файлы под свой проект +#### Вручную: +* Добавить в .gitlab-ci.yml строку подключения библиотеки общих чартов + ```bash + werf helm repo add --force-update helm-apps https://flant.github.io/helm-apps + ``` + + к примеру так: + ```yaml + before_script: + - type trdl && source $(trdl use werf ${WERF_VERSION:-1.2 ea}) + - type werf && source $(werf ci-env gitlab --as-file) + - werf helm repo add --force-update helm-apps https://flant.github.io/helm-apps + ``` + у себя на компьютере добавляем репозиторий helm-apps: + ```yaml + werf helm repo add --force-update helm-apps https://flant.github.io/helm-apps + ``` + и обновляем зависимости: + ```yaml + werf helm dependency update .helm + ``` +* Добавить в папку .helm/templates файл [init-helm-apps.yaml](tests/.helm/templates/init-helm-apps.yaml) для инициализаци библиотеки, содержимое файла: + ```yaml + {{- /* Подключаем библиотеку */}} + {{- include "apps-utils.init-library" $ }} + ``` +* В Chart.yaml в секцию **dependencies**: + ```yaml + apiVersion: v2 + name: test-app + version: 1.0.0 + dependencies: + - name: helm-apps + version: ~1 + repository: "@helm-apps" + ``` +* В [values.yaml](docs/example/.helm/values.yaml) отредактировать секцию global._includes с параметрами по умолчанию для хелперов. + +На данный момент актуальная документация находится в файле [tests/.helm/values.yaml](tests/.helm/values.yaml). Ведется дополнительная работа над созданием расширенной версии документации. + +[О хелперах]( docs/usage.md) + +## Пример простейшего деплоймента Nginx на библиотеке: +
+values.yaml секция приложений + +```yaml +global: + ci_url: example.com +# ... +apps-stateless: + # Приложение из примера в документации + nginx: + _include: ["apps-stateless-defaultApp"] + replicas: 1 + containers: + nginx: + image: + name: nginx + ports: | + - name: http + containerPort: 80 + configFiles: + default.conf: + mountPath: /etc/nginx/templates/default.conf.template + content: | + server { + listen 80 default_server; + listen [::]:80 default_server; + server_name {{ $.Values.global.ci_url }} {{ $.Values.global.ci_url }}; + root /var/www/{{ $.Values.global.ci_url }}; + index index.html; + try_files $uri /index.html; + location / { + proxy_set_header Authorization "Bearer ${SECRET_TOKEN}"; + proxy_pass_header Authorization; + proxy_pass https://backend:3000; + } + } + secretEnvVars: + SECRET_TOKEN: "!!!secret-token-for-backend!!!" + service: + enabled: true + ports: | + - name: http + port: 80 + +apps-ingresses: + nginx: + _include: ["apps-ingresses-defaultIngress"] + host: '{{ $.Values.global.ci_url }}' + paths: | + - path: / + pathType: Prefix + backend: + service: + name: nginx + port: + number: 80 + tls: + enabled: true +``` +
+
+Сгенерирует следующее... + +```yaml +# Helm Apps Library: apps-stateless.nginx.podDisruptionBudget +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: "nginx" + labels: + app: "nginx" + chart: "tests" + repo: "" + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +spec: + selector: + matchLabels: + app: "nginx" + maxUnavailable: "15%" +--- +# Helm Apps Library: apps-stateless.nginx.containers.nginx.secretEnvVars +apiVersion: v1 +kind: Secret +metadata: + name: "envs-containers-nginx-nginx" + labels: + app: "nginx" + chart: "tests" + repo: "" + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +type: Opaque +data: + "SECRET_TOKEN": "ISEhc2VjcmV0LXRva2VuLWZvci1iYWNrZW5kISEh" +--- +# Helm Apps Library: apps-stateless.nginx.containers.nginx.configFiles.default.conf +apiVersion: v1 +kind: ConfigMap +metadata: + name: "config-containers-nginx-nginx-default-conf" + labels: + app: "nginx" + chart: "tests" + repo: "" + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +data: + "default.conf": | + server { + listen 80 default_server; + listen [::]:80 default_server; + server_name example.com example.com; + root /var/www/example.com; + index index.html; + try_files $uri /index.html; + location / { + proxy_set_header Authorization "Bearer ${SECRET_TOKEN}"; + proxy_pass_header Authorization; + proxy_pass https://backend:3000; + } + } +--- +# Helm Apps Library: apps-stateless.nginx.service +apiVersion: v1 +kind: Service +metadata: + name: "nginx" + labels: + app: "nginx" + chart: "tests" + repo: "" + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +spec: + selector: + app: "nginx" + ports: + - name: http + port: 80 +--- +# Source: tests/templates/init-flant-apps-library.yaml +# Helm Apps Library: apps-stateless.nginx +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "nginx" + annotations: + checksum/config: "19812d5210967fd69097dc991263af171c4071ebb455357bd49be2a0ca05acdd" + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 + labels: + app: "nginx" + chart: "tests" + repo: "" +spec: + strategy: + rollingUpdate: + maxSurge: 20% + maxUnavailable: 50% + type: RollingUpdate + template: + metadata: + name: "nginx" + annotations: + checksum/config: "19812d5210967fd69097dc991263af171c4071ebb455357bd49be2a0ca05acdd" + labels: + app: "nginx" + chart: "tests" + repo: "" + spec: + containers: + - name: "nginx" + image: REPO:TAG + envFrom: + - secretRef: + name: "envs-containers-nginx-nginx" + resources: + volumeMounts: + - name: "config-containers-nginx-nginx-default-conf" + subPath: "default.conf" + mountPath: "/etc/nginx/templates/default.conf.template" + ports: + - name: http + containerPort: 80 + imagePullSecrets: + - name: registrysecret + volumes: + - name: "config-containers-nginx-nginx-default-conf" + configMap: + name: "config-containers-nginx-nginx-default-conf" + selector: + matchLabels: + app: "nginx" + revisionHistoryLimit: 3 + replicas: 1 +--- +# Source: tests/templates/init-flant-apps-library.yaml +# Helm Apps Library: apps-ingresses.nginx +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: "nginx" + annotations: + kubernetes.io/ingress.class: "nginx" + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 + labels: + app: "nginx" + chart: "tests" + repo: "" +spec: + tls: + - secretName: nginx + rules: + - host: "example.com" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx + port: + number: 80 +--- +# Helm Apps Library: apps-ingresses.nginx.tls +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: nginx + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +spec: + secretName: nginx + issuerRef: + kind: ClusterIssuer + name: letsencrypt + dnsNames: + - "example.com" +--- +# Helm Apps Library: apps-stateless.nginx.verticalPodAutoscaler +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: "nginx" + labels: + app: "nginx" + chart: "tests" + repo: "" + annotations: + project.werf.io/env: "" + project.werf.io/name: test + werf.io/version: v1.2.162 +spec: + targetRef: + apiVersion: "apps/v1" + kind: Deployment + name: "nginx" + updatePolicy: + updateMode: "Off" + resourcePolicy: {} +``` +
+ +Самостоятельно можно отрендерить следующей командой: + +```bash +$ cd tests && werf render --dev --set "apps-ingresses.nginx.enabled=true" --set "apps-stateless.nginx.enabled=true" +``` diff --git a/charts/helm-apps/Chart.yaml b/charts/helm-apps/Chart.yaml new file mode 100644 index 0000000..020b4bd --- /dev/null +++ b/charts/helm-apps/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: helm-apps +description: A Helm applications library +type: library +version: 1.0.0 +maintainers: + - name: alvnukov + email: alexandr.vnukov@flant.com + url: https://github.com/alvnukov diff --git a/charts/helm-apps/templates/_apps-adopt-utils.tpl b/charts/helm-apps/templates/_apps-adopt-utils.tpl new file mode 100644 index 0000000..de957b8 --- /dev/null +++ b/charts/helm-apps/templates/_apps-adopt-utils.tpl @@ -0,0 +1,9 @@ +{{- define "apps-adopt-utils.adopt-specs"}} +{{- $RelatedScope := index . 0 -}} +{{- $specsMapping := index . 1 }} +{{- range $specLibName, $specOldName := $specsMapping }} +{{- with index $RelatedScope $specOldName }} +{{- set $RelatedScope $specLibName . }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-certificates.tpl b/charts/helm-apps/templates/_apps-certificates.tpl new file mode 100644 index 0000000..b900bf1 --- /dev/null +++ b/charts/helm-apps/templates/_apps-certificates.tpl @@ -0,0 +1,15 @@ +{{- define "apps-certificates" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-certificates" "name" "apps-certificates") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-certificates.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +{{ include "apps-components.cerificate" (list $ .) }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-components.tpl b/charts/helm-apps/templates/_apps-components.tpl new file mode 100644 index 0000000..194013f --- /dev/null +++ b/charts/helm-apps/templates/_apps-components.tpl @@ -0,0 +1,254 @@ +{{- define "apps-components.verticalPodAutoscaler" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- $verticalPodAutoscaler := index . 2 }} +{{- $kind := index . 3 }} +{{- include "apps-utils.enterScope" (list $ "verticalPodAutoscaler") }} +{{- if $verticalPodAutoscaler }} +{{- if include "fl.isTrue" (list $ . $verticalPodAutoscaler.enabled) }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +{{- include "apps-helpers.metadataGenerator" (list $ $verticalPodAutoscaler ) }} +spec: + targetRef: + apiVersion: "apps/v1" + kind: {{ $kind }} + name: {{ $.CurrentApp.name | quote }} + updatePolicy: + updateMode: {{ include "fl.valueQuoted" (list $ . $verticalPodAutoscaler.updateMode) | default (print "Off" | quote )}} +{{- if include "fl.value" (list $ . $verticalPodAutoscaler.resourcePolicy) }} + resourcePolicy: {{- include "fl.value" (list $ . $verticalPodAutoscaler.resourcePolicy) | trim | nindent 4 }} +{{- else }} + resourcePolicy: {} +{{- end }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-components.cerificate" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- with $RelatedScope }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "fl.value" (list $ . .name) }} +spec: + secretName: {{ include "fl.value" (list $ . .name) }} + issuerRef: + kind: ClusterIssuer + name: {{ include "fl.valueQuoted" (list $ . .clusterIssuer) | default "letsencrypt" }} + dnsNames: + {{- with .host }} + - {{ include "fl.valueQuoted" (list $ $RelatedScope .) }} + {{- end }} + {{- with .hosts }} + {{- include "fl.value" (list $ $RelatedScope .) | nindent 2 }} + {{- end }} +{{- end }} +{{- end }} + +{{- define "apps-components.podDisruptionBudget" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- $podDisruptionBudget := index . 2 }} +{{- include "apps-utils.enterScope" (list $ "podDisruptionBudget") }} + +{{- with $podDisruptionBudget }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +--- +{{- include "apps-utils.printPath" $ }} +{{- if semverCompare ">=1.21.0-0" $.Capabilities.KubeVersion.GitVersion }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +{{- include "apps-helpers.metadataGenerator" (list $ $podDisruptionBudget) }} +spec: + selector: + matchLabels: +{{- include "fl.generateSelectorLabels" (list $ . $.CurrentApp.name) | trim | nindent 6 }} + maxUnavailable: {{ include "fl.valueQuoted" (list $ . .maxUnavailable) }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-components.service" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- $service := index . 2 }} +{{- include "apps-utils.enterScope" (list $ "service") }} +{{- if $service }} +{{- if include "fl.isTrue" (list $ . $service.enabled) }} +{{- if include "fl.value" (list $ . $service.ports) }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: v1 +kind: Service +{{- include "apps-helpers.metadataGenerator" (list $ $service) }} +spec: + selector: {{- include "fl.generateSelectorLabels" (list $ . $.CurrentApp.name) | trim | nindent 4 }} +{{- if include "fl.isTrue" (list $ . $service.headless) }} + clusterIP: None +{{- end }} + ports: {{- include "fl.value" (list $ . $service.ports) | trim | nindent 8 }} +{{- end }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-components.horizontalPodAutoscaler" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- include "apps-utils.enterScope" (list $ "horizontalPodAutoscaler") }} +{{- $kind := index . 2 }} +{{- with $RelatedScope }} +{{- if $.CurrentApp.horizontalPodAutoscaler }} +{{- if include "fl.isTrue" (list $ . $.CurrentApp.horizontalPodAutoscaler.enabled) }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +{{- include "apps-helpers.metadataGenerator" (list $ $.CurrentApp.horizontalPodAutoscaler ) }} +spec: + minReplicas: {{ include "fl.value" (list $ . $.CurrentApp.horizontalPodAutoscaler.minReplicas) }} + maxReplicas: {{ include "fl.value" (list $ . $.CurrentApp.horizontalPodAutoscaler.maxReplicas) }} + behavior: {{- include "fl.value" (list $ . $.CurrentApp.horizontalPodAutoscaler.behavior) | trim | nindent 4 }} + scaleTargetRef: + apiVersion: apps/v1 + kind: {{ $kind }} + name: {{ $.CurrentApp.name | quote }} + metrics: +{{- with required (printf "You need a valid entry in horizontalPodAutoscaler.metric on %s app" $.CurrentApp.name) (include "fl.value" (list $ . $.CurrentApp.horizontalPodAutoscaler.metrics)) }} +{{- if kindIs "string" . }} +{{- print . | nindent 2 }} +{{- else if kindOf "map" . }} +{{- include "apps-helpers.generateHPAMetrics" (list $ $RelatedScope) | trim | nindent 2 }} +{{- end }} +{{- end }} + +{{- range $_customMetricResourceName, $_customMetricResource := $.CurrentApp.horizontalPodAutoscaler.customMetricResources }} +{{- include "apps-utils.enterScope" (list $ $_customMetricResourceName) }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- $_ := set . "name" $_customMetricResourceName }} +{{- $_ = set $ "CurrentTargetCustomMetric" $_customMetricResource }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: deckhouse.io/v1alpha1 +kind: {{ include "fl.valueQuoted" (list $ . .kind) }} +{{- include "apps-helpers.metadataGenerator" (list $ . ) }} +spec: + query: {{ include "fl.valueQuoted" (list $ . .query) }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-components.generateConfigMapsAndSecrets" }} +{{- $ := . }} +{{- /* Loop through containers to generate ConfigMaps and Secrets */ -}} +{{- range $_, $containersType := list "initContainers" "containers" }} +{{- include "apps-utils.enterScope" (list $ $containersType) }} +{{- range $_containerName, $_container := index $.CurrentApp $containersType }} +{{- include "apps-utils.enterScope" (list $ $_containerName) }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- $_ := set . "name" $_containerName }} +{{- $_ = set $ "CurrentContainer" $_container }} +{{- /* ConfigMaps created by "configFiles:" option */ -}} +{{- include "apps-utils.enterScope" (list $ "configFiles") }} +{{- range $configFileName, $configFile := .configFiles }} +{{- if include "fl.value" (list $ . .content) }} +{{- include "apps-utils.enterScope" (list $ $configFileName) }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $configFileName | include "fl.formatStringAsDNSLabel" | quote }} + {{- with include "apps-helpers.generateAnnotations" (list $ .) | trim }} + {{- . | nindent 2 }} + {{- end }} + labels: {{ include "fl.generateLabels" (list $ . $.CurrentApp.name) | trim | nindent 4 }} +data: + {{ $configFileName | quote }}: | {{ include "fl.value" (list $ . .content) | trim | nindent 4 }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- /* Secrets created by "secretConfigFiles:" option */ -}} +{{- range $secretConfigFileName, $secretConfigFile := .secretConfigFiles }} +{{- if include "fl.value" (list $ . .content) }} +{{- include "apps-utils.enterScope" (list $ $secretConfigFileName) }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $secretConfigFileName | include "fl.formatStringAsDNSLabel" | quote }} + {{- with include "apps-helpers.generateAnnotations" (list $ .) | trim }} + {{- . | nindent 2 }} + {{- end }} + labels: {{ include "fl.generateLabels" (list $ . $.CurrentApp.name) | trim | nindent 4 }} +type: Opaque +data: + {{ $secretConfigFileName | quote }}: {{ include "fl.value" (list $ . .content) | b64enc | quote }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- /* Secret created by "secretEnvVars:" option */ -}} +{{- if include "fl.generateSecretEnvVars" (list $ . .secretEnvVars) }} +{{- include "apps-utils.enterScope" (list $ "secretEnvVars") }} +--- +{{- include "apps-utils.printPath" $ }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ print "envs-" $containersType "-" $.CurrentApp.name "-" .name | include "fl.formatStringAsDNSLabel" | quote }} + {{- with include "apps-helpers.generateAnnotations" (list $ .) | trim }} + {{- . | nindent 2 }} + {{- end }} + labels: {{ include "fl.generateLabels" (list $ . $.CurrentApp.name) | trim | nindent 4 }} +type: Opaque +data: {{ include "fl.generateSecretEnvVars" (list $ . .secretEnvVars) | trim | nindent 2 }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} + +{{- define "apps-components.generate-config-checksum" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} + {{- /* Loop through containers to generate Pod volumes */ -}} +{{- $allConfigMaps := "" }} +{{- range $_, $containersType := list "initContainers" "containers" }} +{{- range $_containerName, $_container := index $.CurrentApp $containersType }} +{{- if hasKey . "enabled" }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- range $configFileName, $configFile := .configFiles }} +{{- $allConfigMaps = print $allConfigMaps (include "fl.value" (list $ $RelatedScope $configFile.content)) }} +{{- end }} +{{- end }} +{{- else }} +{{- range $configFileName, $configFile := .configFiles }} +{{- $allConfigMaps = print $allConfigMaps (include "fl.value" (list $ $RelatedScope $configFile.content)) }} +{{- end }} +{{- end }} +{{- end }} + +{{- end }} +{{- printf "checksum/config: '%s'" ($allConfigMaps | sha256sum) }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-configmaps.tpl b/charts/helm-apps/templates/_apps-configmaps.tpl new file mode 100644 index 0000000..6ac6b36 --- /dev/null +++ b/charts/helm-apps/templates/_apps-configmaps.tpl @@ -0,0 +1,21 @@ +{{- define "apps-configmaps" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-configmaps" "name" "apps-configmaps") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} + {{- end -}} +{{- end -}} + +{{- define "apps-configmaps.render" }} +{{- $ := . }} +{{- $_ := set $ "CurrentConfigMap" $.CurrentApp }} +{{- with $.CurrentApp }} +apiVersion: v1 +kind: ConfigMap +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +data: +{{- include "apps.generateConfigMapEnvVars" (list $ . .envVars "envVars") | nindent 2 }} +{{- include "fl.value" (list $ . .data) | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-cronjobs.tpl b/charts/helm-apps/templates/_apps-cronjobs.tpl new file mode 100644 index 0000000..80e83bf --- /dev/null +++ b/charts/helm-apps/templates/_apps-cronjobs.tpl @@ -0,0 +1,38 @@ +{{- define "apps-cronjobs" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-cronjobs" "name" "apps-cronjobs") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-cronjobs.render" }} +{{- $ := . }} +{{- $_ := set $ "CurrentCronJob" $.CurrentApp }} +{{- with $.CurrentApp }} +{{- if not .containers }} +{{- fail (printf "Установлено значение enabled для не настроенной '%s' в %s джобы!" $.CurrentApp.name "apps-cronjobs") }} +{{- end }} +{{- if semverCompare ">=1.21-0" $.Capabilities.KubeVersion.GitVersion }} +apiVersion: batch/v1 +{{- else }} +apiVersion: batch/v1beta1 +{{- end }} +kind: CronJob +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $specs := dict }} + {{- $_ = set $specs "Strings" (list "schedule" "concurrencyPolicy") }} + {{- $_ = set $specs "Numbers" (list "failedJobsHistoryLimit" "startingDeadlineSeconds" "successfulJobsHistoryLimit") }} + {{- $_ = set $specs "Bools" (list "suspend") }} + {{- include "apps-utils.generateSpecs" (list $ . $specs) | indent 2 }} + jobTemplate: {{ include "apps-helpers.jobTemplate" (list $ .) | nindent 4 -}} + +{{- include "apps-components.generateConfigMapsAndSecrets" $ -}} + +{{- include "apps-components.verticalPodAutoscaler" (list $ . .verticalPodAutoscaler "CronJob") -}} + +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-deckhouse.tpl b/charts/helm-apps/templates/_apps-deckhouse.tpl new file mode 100644 index 0000000..bdacf55 --- /dev/null +++ b/charts/helm-apps/templates/_apps-deckhouse.tpl @@ -0,0 +1,16 @@ +{{- define "apps-deckhouse.metrics" }} +{{- $ := . }} +{{- range $metricName, $metric := $.CurrentApp.deckhouseMetrics }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +--- +apiVersion: deckhouse.io/v1beta1 +kind: {{ .kind }} +{{- if not (hasKey . "name")}} +{{- $_ := set . "name" (printf "%s-%s" $.CurrentApp.name (lower $metricName)) }} +{{- end }} +{{- include "apps-helpers.metadataGenerator" (list $ .)}} +spec: + query: {{ include "fl.value" (list $ . .query) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-default-values.yaml b/charts/helm-apps/templates/_apps-default-values.yaml new file mode 100644 index 0000000..d13de04 --- /dev/null +++ b/charts/helm-apps/templates/_apps-default-values.yaml @@ -0,0 +1,166 @@ +{{- define "apps-default-values" }} +global: + ## Альтернатива ограниченным yaml-алиасам Helm'а. Даёт возможность не дублировать одну и ту же конфигурацию много раз. + # + # Здесь, в "global._includes", объявляются блоки конфигурации, которые потом можно использовать в любых values-файлах. + # Пример подтягивания этих блоков конфигурации в репозитории приложения: + # ----------------------------------------------------------------------------------------------- + # .helm/values.yaml: + # ----------------------------------------------------------------------------------------------- + # apps-cronjobs: + # cronjob-1: + # _include: ["apps-cronjobs-defaultCronJob"] + # backoffLimit: 1 + # ----------------------------------------------------------------------------------------------- + # + # В примере выше конфигурация из include-блока "apps-cronjobs-defaultCronJob" развернётся на уровне + # apps-cronjobs.cronjob-1, а потом поверх развернувшейся конфигурации применится параметр "backoffLimit: 1", + # при необходимости перезаписав параметр "backoffLimit" из include-блока. + # + # Подробнее: https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flexpandincludesinvalues-function + _includes: + apps-default-library-app: + enabled: false + imagePullSecrets: | + - name: registrysecret + ## Конфигурация по умолчанию для CronJob в целом. + apps-cronjobs-defaultCronJob: + _include: ["apps-default-library-app"] + # CLIENT: ask if this is ok for a default + concurrencyPolicy: "Forbid" + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + # CLIENT: ask if this is ok for a default + backoffLimit: 0 + priorityClassName: + prod: "production-high" + production: "production-high" + restartPolicy: "Never" + startingDeadlineSeconds: 60 + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + ## Конфигурация по умолчанию для инит-контейнера CronJob. + apps-cronjobs-defaultCronJobInitContainer: + enabled: true + + ## Конфигурация по умолчанию для контейнера CronJob. + apps-cronjobs-defaultCronJobContainer: + enabled: true + + apps-secrets-defaultSecret: + _include: ["apps-default-library-app"] + apps-ingresses-defaultIngress: + _include: ["apps-default-library-app"] + ingressClassName: "nginx" + + ## Конфигурация по умолчанию для Job в целом. + apps-jobs-defaultJob: + _include: ["apps-default-library-app"] + # CLIENT: ask if this is ok for a default + backoffLimit: 0 + priorityClassName: + prod: "production-high" + restartPolicy: "Never" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + ## Конфигурация по умолчанию для инит-контейнера Job. + apps-jobs-defaultJobInitContainer: + enabled: true + + ## Конфигурация по умолчанию для контейнера Job. + apps-jobs-defaultJobContainer: + enabled: true + + + ## Конфигурация по умолчанию для приложения в целом. + apps-stateful-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + terminationGracePeriodSeconds: + _default: 30 + prod: 60 + rspec: 3 + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{ include "fl.generateSelectorLabels" (list $ . .name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + priorityClassName: + prod: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "15%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + headless: true + + ## Конфигурация по умолчанию для инит-контейнера приложения. + apps-stateful-defaultAppInitContainer: + enabled: true + + ## Конфигурация по умолчанию для контейнера приложения. + apps-stateful-defaultAppContainer: + enabled: true + + ## Конфигурация по умолчанию для приложения в целом. + apps-stateless-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + strategy: + _default: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 50% + type: RollingUpdate + prod: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 5% + type: RollingUpdate + production: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 5% + type: RollingUpdate + priorityClassName: + prod: "production-medium" + production: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "10%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + horizontalPodAutoscaler: + enabled: false + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + headless: true + ## Конфигурация по умолчанию для инит-контейнера приложения. + apps-stateless-defaultAppInitContainer: + enabled: true + + ## Конфигурация по умолчанию для контейнера приложения. + apps-stateless-defaultAppContainer: + enabled: true + apps-configmaps-defaultConfigmap: + _include: ["apps-default-library-app"] +{{- end }} diff --git a/charts/helm-apps/templates/_apps-dex.tpl b/charts/helm-apps/templates/_apps-dex.tpl new file mode 100644 index 0000000..a15bdf4 --- /dev/null +++ b/charts/helm-apps/templates/_apps-dex.tpl @@ -0,0 +1,53 @@ +{{- define "apps-dex-authenticators" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-dex-authenticators" "name" "apps-dex-authenticators") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-dex-authenticators.render" }} +{{- $ := . }} +{{- $_ := set $ "CurrentDexAuthenticator" $.CurrentApp }} +{{- with $.CurrentApp }} +{{- if not .applicationDomain }} +{{- fail (printf "Установлено значение enabled для не настроенного '%s' в %s DexAuthenticator!" $.CurrentApp.name "apps-dexauthenticators") }} +{{- end }} +apiVersion: deckhouse.io/v1 +kind: DexAuthenticator +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $specDex := dict }} + {{- $_ = set $specDex "Lists" ( list "whitelistSourceRanges" "tolerations" "allowedGroups") }} + {{- $_ = set $specDex "Maps" (list "nodeSelector") }} + {{- $_ = set $specDex "Strings" (list "applicationDomain" "applicationIngressCertificateSecretName" "applicationIngressClassName" "keepUsersLoggedInFor" "signOutURL" "sendAuthorizationHeader") }} + {{- include "apps-utils.generateSpecs" (list $ . $specDex) | trim | nindent 2 }} + {{- end }} +{{- end }} + +{{- define "apps-dex-clients" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-dex-clients" "name" "apps-dex-clients") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-dex-clients.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +{{- $_ := set $ "CurrentDexClient" . }} +{{- if not .redirectURIs }} +{{- fail (printf "Установлено значение enabled для не настроенного '%s' в %s DexAuthenticator!" $.CurrentApp.name $.Chart.Name) }} +{{- end }} +apiVersion: deckhouse.io/v1alpha1 +kind: DexClient +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + redirectURIs: {{ include "fl.value" (list $ . .redirectURIs) | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-fl-wrappers.tpl b/charts/helm-apps/templates/_apps-fl-wrappers.tpl new file mode 100644 index 0000000..a23b45a --- /dev/null +++ b/charts/helm-apps/templates/_apps-fl-wrappers.tpl @@ -0,0 +1,27 @@ +{{- define "apps.generateContainerEnvVars" }} +{{- $ := index . 0 }} +{{- include "apps-utils.enterScope" (list $ "envVars") }} +{{- include "fl.generateContainerEnvVars" . }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps.generateSecretEnvVars" }} +{{- $ := index . 0 }} +{{- include "apps-utils.enterScope" (list $ "secretEnvVars") }} +{{- include "fl.generateSecretEnvVars" . }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps.generateConfigMapEnvVars" }} +{{- $ := index . 0 }} +{{- include "apps-utils.enterScope" (list $ "secretEnvVars") }} +{{- include "fl.generateConfigMapEnvVars" . }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps.value" }} +{{- $ := index . 0 }} +{{- include "apps-utils.enterScope" (list $ (last .)) }} +{{- include "fl.value" (initial .) }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-helpers.tpl b/charts/helm-apps/templates/_apps-helpers.tpl new file mode 100644 index 0000000..f73776e --- /dev/null +++ b/charts/helm-apps/templates/_apps-helpers.tpl @@ -0,0 +1,282 @@ +{{- define "apps-helpers.generateVolumes" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- /* Loop through containers to generate Pod volumes */ -}} + {{- range $_, $containersType := list "initContainers" "containers" }} + {{- range $_containerName, $_container := index $.CurrentApp $containersType }} + {{- if include "fl.isTrue" (list $ . .enabled) }} + {{- $_ := set . "name" $_containerName }} + {{- $_ = set $ "CurrentContainer" $_container }} + {{- /* Mount ConfigMaps created by "configFiles:" option as volumes */ -}} + {{- range $configFileName, $_ := .configFiles }} + {{- if include "fl.value" (list $ . .content) }} + {{- $_ := set . "name" (print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $configFileName | include "fl.formatStringAsDNSLabel") }} + {{- else }} + {{- if not ( include "fl.value" (list $ . .name)) }} + {{- fail (printf "Для app '%s' %s '%s' в configFiles '%s' нет content и забыли указать .name" $.CurrentApp.name $containersType $.CurrentContainer.name $configFileName) }} + {{- end }} + {{- end }} +- name: {{ print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $configFileName | include "fl.formatStringAsDNSLabel" | quote }} + configMap: + name: {{ .name | quote }} + {{- with include "fl.value" (list $ . .defaultMode) }} + defaultMode: {{ . }} + {{- end }} + {{- end }} + {{- /* Mount Secrets created by "secretConfigFiles:" option as volumes */ -}} + {{- range $secretConfigFileName, $_ := .secretConfigFiles }} + {{- if include "fl.value" (list $ . .content) }} + {{- $_ := set . "name" (print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $secretConfigFileName | include "fl.formatStringAsDNSLabel") }} + {{- end }} + {{- if or (include "fl.value" (list $ . .content)) (include "fl.value" (list $ . .name)) }} +- name: {{ print "config-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $secretConfigFileName | include "fl.formatStringAsDNSLabel" | quote }} + secret: + secretName: {{ .name | quote }} + {{- with include "fl.value" (list $ . .defaultMode) }} + defaultMode: {{ . }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end -}} + +{{- /* Mount config files from ConfigMaps created by "configFiles:" option */ -}} +{{- define "apps-helpers.generateVolumeMounts" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- range $configFileName, $configFile := $.CurrentContainer.configFiles }} + {{- if or (include "fl.value" (list $ . .content)) (include "fl.value" (list $ . .name)) }} +- name: {{ print "config-" $.CurrentApp._currentContainersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $configFileName | include "fl.formatStringAsDNSLabel" | quote }} + subPath: {{ $configFileName | quote }} + mountPath: {{ include "fl.valueQuoted" (list $ . .mountPath) }} + {{- end }} + {{- end -}} + + {{- /* Mount secret files from ConfigMaps created by "secretConfigFiles:" option */ -}} + {{- range $secretConfigFileName, $secretConfigFile := $.CurrentContainer.secretConfigFiles }} +- name: {{ print "config-" $.CurrentApp._currentContainersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $secretConfigFileName | include "fl.formatStringAsDNSLabel" | quote }} + subPath: {{ $secretConfigFileName | quote }} + mountPath: {{ include "fl.valueQuoted" (list $ . .mountPath) }} + {{- end }} + {{- /* Mount persistantVolumes */ -}} + {{- range $persistantVolumeName, $persistantVolume := $.CurrentContainer.persistantVolumes }} + {{- $pvcName := print $persistantVolumeName "-" $.CurrentApp._currentContainersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $persistantVolume.mountPath | include "fl.formatStringAsDNSLabel" }} +- name: {{ $pvcName | quote }} + mountPath: {{ $persistantVolume.mountPath | quote }} + {{- end }} +{{- end -}} + +{{- define "apps-helpers.generateContainers" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- $containers := index . 2 }} + {{- include "apps-utils.enterScope" (list $ "containers") -}} + + {{- range $_containerName, $_container := $containers }} + {{- include "apps-utils.enterScope" (list $ $_containerName) -}} + {{- if not (hasKey . "enabled") }} + {{- $_ := set . "enabled" true }} + {{- end }} + {{- if include "fl.isTrue" (list $ . .enabled) }} + {{- $_ := set . "name" $_containerName }} + {{- $_ = set $ "CurrentContainer" $_container }} +- name: {{ include "fl.valueQuoted" (list $ . .name) }} + image: {{ include "fl.generateContainerImageQuoted" (list $ . .image) }} + {{- with (include "apps-helpers.genereteContainersEnv" (list $ .) | trim) }} + env: + {{- . | nindent 2 }} + {{- end }} + {{- with (include "apps-helpers.genereteContainersEnvFrom" (list $ .) | trim) }} + envFrom: + {{- . | nindent 2 }} + {{- end }} + resources: +{{- include "fl.generateContainerResources" (list $ . .resources) | trim | nindent 4 }} + {{- $volumeMounts := include "fl.value" (list $ . .volumeMounts) | trim }} + {{- $volumeMounts = list $volumeMounts (include "apps-helpers.generateVolumeMounts" (list $ .) | trim) | join "\n" | trim }} + {{- with $volumeMounts }} + volumeMounts: {{ print $volumeMounts | trim | nindent 2}} + {{- end -}} + + {{- $specsContainers := dict }} + {{- $_ = set $specsContainers "Lists" ( list "args" "command" "ports") }} + {{- $_ = set $specsContainers "Maps" (list "lifecycle" "livenessProbe" "readinessProbe" "securityContext" "startupProbe") }} + {{- $_ = set $specsContainers "Strings" (list "imagePullPolicy" "terminationMessagePath" "terminationMessagePolicy" "workingDir") }} + {{- $_ = set $specsContainers "Bools" (list "stdin" "stdinOnce" "tty" "workingDir" ) }} + {{- include "apps-utils.generateSpecs" (list $ . $specsContainers) | trim | nindent 2 }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end -}} + +{{- define "apps-helpers.podTemplate" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} +{{- with $RelatedScope }} +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $_ := set $.CurrentApp "_currentContainersType" "initContainers" }} + {{ with (include "apps-helpers.generateContainers" (list $ . .initContainers) | trim) }} + initContainers: + {{- . | nindent 2 }} + {{- end }} + {{- $_ = set $.CurrentApp "_currentContainersType" "containers" }} + containers: + {{- include "apps-helpers.generateContainers" (list $ . .containers) | trim | nindent 2 }} + {{- $specsTemplate := dict }} + {{- $_ = set $specsTemplate "Lists" ( list "tolerations" "imagePullSecrets" "hostAliases" "topologySpreadConstraints" "apps-specs.containers.volumes") }} + {{- $_ = set $specsTemplate "Maps" (list "affinity" "dnsConfig" "nodeSelector" "overhead" "readinessGates" "securityContext") }} + {{- $_ = set $specsTemplate "Strings" (list "dnsPolicy" "hostname" "nodeName" "preemptionPolicy" "priorityClassName" "restartPolicy" "runtimeClassName" "schedulerName" "serviceAccount" "serviceAccountName" "subdomain") }} + {{- $_ = set $specsTemplate "Numbers" (list "activeDeadlineSeconds" "priority" "terminationGracePeriodSeconds") }} + {{- $_ = set $specsTemplate "Bools" (list "automountServiceAccountToken" "enableServiceLinks" "hostIPC" "hostNetwork" "hostPID" "setHostnameAsFQDN" "shareProcessNamespace") }} + {{- include "apps-utils.generateSpecs" (list $ . $specsTemplate) | trim | nindent 2 }} +{{- $_ := set . "__specName__" "template"}} +{{- end }} +{{- end -}} + +{{- define "apps-helpers.activateContainerForDefault" }} +{{- $ := . }} +{{- range $_, $containersType := list "initContainers" "containers" }} +{{- range $_containerName, $_container := index $.CurrentApp $containersType }} +{{- if not (hasKey . "enabled") }} +{{- $_ := set . "enabled" true }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "apps-helpers.genereteContainersEnv" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- with $RelatedScope }} + {{- if include "fl.isTrue" (list $ . $.CurrentApp.alwaysRestart) }} + {{- $_ := set .envVars "FL_APP_ALWAYS_RESTART" (randAlphaNum 20) }} + {{- end }} + {{- include "apps.generateContainerEnvVars" (list $ . .envVars) | trim | nindent 0 }} + {{- with (include "fl.value" (list $ . .env) | trim) }} + {{- . | nindent 0}} + {{- end }} + {{- with (include "fl.generateContainerFromSecretsEnvVars" (list $ . .fromSecretsEnvVars) | trim) }} + {{- . | nindent 0 }} + {{- end }} + {{- end }} +{{- end }} +{{- define "apps-helpers.genereteContainersEnvFrom" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- with $RelatedScope }} + {{- include "fl.value" (list $ . .envFrom) | trim | nindent 0 }} + {{- /* Mount envs from Secret created by "secretEnvVars:" option */ -}} + {{- if include "fl.generateSecretEnvVars" (list $ . .secretEnvVars) }} +- secretRef: + name: {{ print "envs-" $.CurrentApp._currentContainersType "-" $.CurrentApp.name "-" .name | include "fl.formatStringAsDNSLabel" | quote }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "apps-helpers.jobTemplate" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} +{{- with $RelatedScope }} +spec: + {{- $specs := dict }} + {{- $_ := set $specs "Maps" (list "selector") }} + {{- $_ = set $specs "Strings" (list "completionMode") }} + {{- $_ = set $specs "Numbers" (list "activeDeadlineSeconds" "backoffLimit" "completions" "parallelism" "ttlSecondsAfterFinished") }} + {{- $_ = set $specs "Bools" (list "manualSelector" "suspend") }} +{{ include "apps-utils.generateSpecs" (list $ . $specs) | trim | indent 2 }} + template: {{ include "apps-helpers.podTemplate" (list $ .) | trim | nindent 4 }} +{{- end }} +{{- end -}} + +{{- define "apps-helpers.generateAnnotations" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} +{{- with $RelatedScope }} +{{- $annotationsMap := dict }} +{{- $libAnnotations := dict }} +{{- if hasKey . "__annotations__" }} +{{- $annotationsMap = $.CurrentApp.__annotations__ | mustDeepCopy }} +{{- end }} +{{- if hasKey $.CurrentApp "werfWeight" }} +{{- $_ := set $libAnnotations "werf.io/weight" (include "fl.value" (list $ . $.CurrentApp.werfWeight)) }} +{{- end }} +{{- $libVersion := include "apps-version.getLibraryVersion" $ | trim }} +{{- with $libVersion }} +{{- if not (eq . "_FLANT_APPS_LIBRARY_VERSION_") }} +{{- $_ := set $libAnnotations "helm-apps/version" . }} +{{- end }} +{{- end }} +{{- $userAnnotations := fromYaml (include "fl.value" (list $ . $.CurrentApp.annotations)) }} +{{- $relatedScopeAnnotations := fromYaml (include "fl.value" (list $ . .annotations)) }} +{{- $annotationsMap = mergeOverwrite $annotationsMap $userAnnotations $relatedScopeAnnotations $libAnnotations }} + {{- if gt (len $annotationsMap) 0 }} +annotations: +{{- range $a, $v := $annotationsMap }} + {{ $a }}: {{ $v | quote }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "apps-helpers.metadataGenerator"}} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} +{{- with $RelatedScope }} + +metadata: + {{- if hasKey . "name" }} + name: {{ include "fl.value" (list $ . .name) | quote }} + {{- else }} + name: {{ $.CurrentApp.name | quote }} + {{- end }} + {{- include "apps-helpers.generateAnnotations" (list $ .) | nindent 2 }} + labels: + {{- include "fl.generateLabels" (list $ . $.CurrentApp.name) | nindent 4 }} + {{- with include "fl.value" (list $ . .labels) }} + {{- . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} + +{{- define "apps-helpers.generateHPAMetrics" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- include "apps-utils.enterScope" (list $ "metrics") }} +{{- with $RelatedScope }} +{{- range $metricName, $metric := .horizontalPodAutoscaler.metrics }} +{{- if include "fl.isTrue" (list $ $RelatedScope .enabled) }} +{{- include "apps-utils.enterScope" (list $ $metricName) }} +{{- if has $metricName (list "cpu" "memory") }} +- type: Resource + resource: + name: $metricName + target: + type: Utilization + averageUtilization: {{ include "apps-utils.requiredValue" (list $ . "averageUtilization") }} +{{- else }} +{{ $type := include "apps-utils.requiredValue" (list $ . "type") }} +{{- if eq $type "Object"}} + - type: Object + object: + describedObject: + apiVersion: v1 + kind: {{ include "apps-utils.requiredValue" (list $ . "kind") }} + name: {{ $metricName }} + metric: + name: {{ printf "%s-%s-metric" $.CurrentApp.name $metricName }} + target: + type: Value + value: {{ include "apps-utils.requiredValue" (list $ . "targetValue") }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end -}} + diff --git a/charts/helm-apps/templates/_apps-infra.tpl b/charts/helm-apps/templates/_apps-infra.tpl new file mode 100644 index 0000000..6022416 --- /dev/null +++ b/charts/helm-apps/templates/_apps-infra.tpl @@ -0,0 +1,83 @@ +# TODO: +{{- define "apps-infra" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 -}} + {{- include "apps-utils.enterScope" (list $ "apps-infra") }} + {{- if hasKey $RelatedScope "node-users"}} + {{- include "apps-infra.node-users" (list $ (index $RelatedScope "node-users")) }} + {{- end }} + {{- if hasKey $RelatedScope "node-groups"}} + {{- include "apps-infra.node-groups" (list $ (index $RelatedScope "node-groups")) }} + {{- end }} + {{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-infra.node-users"}} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 -}} + {{- include "apps-utils.enterScope" (list $ "node-users") }} + {{- range $_appName, $_app := omit $RelatedScope "global" "enabled" "_include" "__GroupVars__" -}} + {{- include "apps-utils.enterScope" (list $ $_appName) }} + {{- $_ := set . "name" $_appName }} + {{- $_ = set $ "CurrentApp" $_app }} +{{- include "apps-utils.preRenderHooks" $ }} + {{- if include "fl.isTrue" (list $ . .enabled) }} +apiVersion: deckhouse.io/v1 +kind: NodeUser +metadata: + name: {{ .name | quote }} + annotations: + {{- include "fl.value" (list $ . .annotations) | nindent 4 }} + labels: + {{- include "fl.value" (list $ . .labels) | nindent 4 }} +spec: + {{- $specs := dict }} + {{- $_ := set $specs "Lists" (list "extraGroups" "nodeGroups" "sshPublicKeys") }} + {{- $_ = set $specs "Maps" (list) }} + {{- $_ = set $specs "Strings" (list "sshPublicKey" "passwordHash") }} + {{- $_ = set $specs "Numbers" (list "uid") }} + {{- $_ = set $specs "Bools" (list "isSudoer") }} + {{- $_ = set $specs "Required" (list "uid") }} + {{- include "apps-utils.generateSpecs" (list $ . $specs) | indent 2 }} + {{- end }} + {{- include "apps-utils.leaveScope" $ }} + {{- end }} + {{- include "apps-utils.leaveScope" $ }} +{{- end }} + +{{- define "apps-infra.node-groups"}} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 -}} + {{- include "apps-utils.enterScope" (list $ "node-groups") }} + {{- range $_appName, $_app := omit $RelatedScope "global" "enabled" "_include" "__GroupVars__" -}} + {{- include "apps-utils.enterScope" (list $ $_appName) }} + {{- $_ := set . "name" $_appName }} + {{- $_ = set $ "CurrentApp" $_app }} + {{- if ._preRenderHook }} + {{- $_ := include "fl.value" (list $ . ._preRenderHook) }} + {{- end }} + {{- if include "fl.isTrue" (list $ . .enabled) }} +--- +#{{ print "#" $.CurrentPath }} +apiVersion: deckhouse.io/v1 +kind: NodeGroup +metadata: + name: {{ .name | quote }} + annotations: + {{- include "fl.value" (list $ . .annotations) | nindent 4 }} + labels: + {{- include "fl.value" (list $ . .labels) | nindent 4 }} +spec: + {{- $specs := dict }} + {{- $_ := set $specs "Lists" (list ) }} + {{- $_ = set $specs "Maps" (list) }} + {{- $_ = set $specs "Strings" (list ) }} + {{- $_ = set $specs "Numbers" (list ) }} + {{- $_ = set $specs "Bools" (list ) }} + {{- $_ = set $specs "Required" (list ) }} + {{- include "apps-utils.generateSpecs" (list $ . $specs) | indent 2 }} + {{- end }} + {{- include "apps-utils.leaveScope" $ }} + {{- end }} + {{- include "apps-utils.leaveScope" $ }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-ingresses.tpl b/charts/helm-apps/templates/_apps-ingresses.tpl new file mode 100644 index 0000000..415b522 --- /dev/null +++ b/charts/helm-apps/templates/_apps-ingresses.tpl @@ -0,0 +1,80 @@ +{{- define "apps-ingresses" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-ingresses" "name" "apps-ingresses") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} + {{- end -}} +{{- end -}} + +{{- define "apps-ingresses.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +{{- $_ := set $ "CurrentIngress" . }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .name | quote }} + annotations: + {{- if include "fl.value" (list $ . .class) }} + kubernetes.io/ingress.class: {{ include "fl.valueQuoted" (list $ . .class) }}{{- end }} + {{- include "fl.value" (list $ . .annotations) | nindent 4 }} + {{- with .dexAuth }} + {{- if (include "fl.isTrue" (list $ $.CurrentApp .enabled)) }} + {{- include "apps-utils.enterScope" (list $ "dexAuth") }} + nginx.ingress.kubernetes.io/auth-signin: https://$host/dex-authenticator/sign_in + nginx.ingress.kubernetes.io/auth-url: https://{{ $.CurrentApp.name }}-dex-authenticator.{{ $.Values.werf.namespace }}.svc.{{ include "apps-utils.requiredValue" (list $ . "clusterDomain") }}/dex-authenticator/auth + nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email,Authorization + {{- include "apps-utils.leaveScope" $ }} + {{- end }} + {{- end }} + labels: {{- include "fl.generateLabels" (list $ . .name) | nindent 4 }} +spec: + {{- if include "fl.value" (list $ . .ingressClassName) }} + ingressClassName: {{ include "fl.value" (list $ . .ingressClassName) }}{{- end }} + {{- if .tls }} + {{- if include "fl.isTrue" (list $ . .tls.enabled) }} + tls: + {{- if (include "fl.value" (list $ . .tls.secret_name)) }} + - secretName: {{ include "fl.value" (list $ . .tls.secret_name) }} + {{- else }} + - secretName: {{ include "fl.value" (list $ . .name) }} + {{- end }} + {{- end }} + {{- end }} + rules: + - host: {{ include "fl.valueQuoted" (list $ . .host) }} + http: + paths: {{- include "fl.value" (list $ . .paths) | nindent 6 }} +{{- if .tls }} +{{- if include "fl.isTrue" (list $ . .tls.enabled) }} +{{- if not (include "fl.value" (list $ . .tls.secret_name)) }} +--- +{{- include "apps-utils.enterScope" (list $ "tls") }} +{{- include "apps-utils.printPath" $ }} +{{- include "apps-components.cerificate" (list $ .) }} +{{- include "apps-utils.leaveScope" $ }} +{{- end -}} + +{{- with .dexAuth }} +{{- if (include "fl.isTrue" (list $ $.CurrentApp .enabled)) }} +{{- $_ := set $.CurrentApp "applicationDomain" $.CurrentApp.host }} +{{- if $.CurrentApp.class }} +{{- $_ = set $.CurrentApp "applicationIngressClassName" $.CurrentApp.class }} +{{- else }} +{{- $_ = set $.CurrentApp "applicationIngressClassName" (include "apps-utils.requiredValue" (list $ $.CurrentApp "ingressClassName")) }} +{{- end }} +{{- if (include "fl.value" (list $ . $.CurrentApp.tls.secret_name)) }} +{{- $_ = set $.CurrentApp "applicationIngressCertificateSecretName" (include "fl.value" (list $ $.CurrentApp $.CurrentApp.tls.secret_name)) }} +{{- else }} +{{- $_ = set $.CurrentApp "applicationIngressCertificateSecretName" (include "fl.value" (list $ $.CurrentApp $.CurrentApp.name)) }} +{{- end }} +--- +{{- include "apps-dex-authenticators.render" $ }} +{{- end }} +{{- end }} +{{- end }} + +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-jobs.tpl b/charts/helm-apps/templates/_apps-jobs.tpl new file mode 100644 index 0000000..45843bf --- /dev/null +++ b/charts/helm-apps/templates/_apps-jobs.tpl @@ -0,0 +1,30 @@ +{{- define "apps-jobs" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-jobs" "name" "apps-jobs") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-jobs.render" }} +{{- $ := . }} +{{- $_ := set $ "CurrentJob" $.CurrentApp }} +{{- with $.CurrentApp }} + +{{- if not .containers }} +{{- fail (printf "Установлено значение enabled для не настроенной '%s' в %s джобы!" $.CurrentApp.name "apps-jobs") }} +{{- end }} +apiVersion: batch/v1 +kind: Job +{{- include "apps-helpers.metadataGenerator" (list $ .) -}} + +{{- include "apps-helpers.jobTemplate" (list $ .) -}} + +{{- include "apps-components.generateConfigMapsAndSecrets" $ -}} + +{{- include "apps-components.verticalPodAutoscaler" (list $ . .verticalPodAutoscaler "Job") -}} + +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-kafka-strimzi.tpl b/charts/helm-apps/templates/_apps-kafka-strimzi.tpl new file mode 100644 index 0000000..f7badfe --- /dev/null +++ b/charts/helm-apps/templates/_apps-kafka-strimzi.tpl @@ -0,0 +1,226 @@ +{{- define "apps-kafka-strimzi" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-kafka-strimzi" "name" "apps-kafka-strimzi") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-kafka-strimzi.render"}} + {{- $ := index . }} + {{- $_ := set $ "CurrentKafka" $.CurrentApp }} + {{- with $.CurrentApp }} +apiVersion: kafka.strimzi.io/v1beta2 +kind: Kafka +metadata: + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }} +spec: + kafkaExporter: + groupRegex: ".*" + topicRegex: ".*" + resources: {{- include "fl.generateContainerResources" (list $ . .exporter.resources) | nindent 6 }} + template: + pod: + metadata: + labels: + prometheus.deckhouse.io/custom-target: kafka-exporter + annotations: + prometheus.deckhouse.io/port: "9404" + prometheus.deckhouse.io/sample-limit: "{{ include "fl.value" (list $ . .prometheusSampleLimit) | default 10000 }}" + {{- include "fl.value" (list $ . .annotations) | nindent 12 }} + {{- with .exporter.tolerations }} + tolerations: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 8 }} + {{- end }} + {{- with .exporter.affinity }} + affinity: {{ include "fl.value" (list $ . .) | nindent 10 }} + {{- end }} + kafka: + version: {{ include "fl.value" (list $ . .version) }} + replicas: {{ include "fl.value" (list $ . .replicas) }} + resources: {{ include "fl.generateContainerResources" (list $ . .resources) | nindent 6 }} + jvmOptions: {{ include "fl.value" (list $ . .jvmOptions ) | nindent 6 }} + listeners: + - name: plain + port: 9092 + type: internal + tls: false + - name: tls + port: 9093 + type: internal + tls: true + template: + pod: + metadata: + labels: + prometheus.deckhouse.io/custom-target: kafka + annotations: + prometheus.deckhouse.io/port: "9404" + prometheus.deckhouse.io/sample-limit: "{{ include "fl.value" (list $ . .prometheusSampleLimit) | default 50000 }}" + priorityClassName: {{ include "fl.valueQuoted" (list $ . .priorityClassName) }} + {{- with .tolerations }} + tolerations: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 8 }} + {{- end }} + {{- with .affinity }} + affinity: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 10 }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: "kubernetes.io/hostname" + labelSelector: + matchLabels: + strimzi.io/name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-kafka + {{- end }} + terminationGracePeriodSeconds: 120 + metricsConfig: + type: jmxPrometheusExporter + valueFrom: + configMapKeyRef: + name: kafka-metrics + key: kafka-metrics-config.yml + config: + auto.create.topics.enable: "false" + offsets.topic.replication.factor: 1 + transaction.state.log.replication.factor: 1 + transaction.state.log.min.isr: 1 + log.message.format.version: "2.7" + inter.broker.protocol.version: "2.7" + storage: + type: jbod + volumes: + - id: 0 + type: persistent-claim + size: {{ include "fl.value" (list $ . .storage.size) }} + class: {{ include "fl.value" (list $ . .storage.class) }} + deleteClaim: false + zookeeper: + priorityClassName: {{ include "fl.valueQuoted" (list $ . .priorityClassName) }} + replicas: {{ include "fl.value" (list $ . .zookeeper.replicas) }} + resources: {{ include "fl.generateContainerResources" (list $ . .zookeeper.resources) | nindent 6 }} + template: + pod: + metadata: + labels: + prometheus.deckhouse.io/custom-target: zookeeper + annotations: + prometheus.deckhouse.io/port: "9404" + prometheus.deckhouse.io/sample-limit: "{{ include "fl.value" (list $ . .prometheusSampleLimit) | default 5000 }}" + {{- with .zookeeper.tolerations }} + tolerations: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 8 }} + {{- end }} + priorityClassName: {{ include "fl.valueQuoted" (list $ . .priorityClassName) }} + affinity: + {{- include "fl.value" (list $ $.CurrentApp .zookeeper.affinity) | nindent 10 }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: "kubernetes.io/hostname" + labelSelector: + matchLabels: + strimzi.io/name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-zookeeper + terminationGracePeriodSeconds: 120 + metricsConfig: {{ include "fl.value" (list $ . .zookeeper.metricsConfig) | nindent 6 }} + jvmOptions: {{ include "fl.value" (list $ . .zookeeper.jvmOptions) | nindent 6 }} + storage: + type: persistent-claim + size: {{ include "fl.value" (list $ . .zookeeper.storage.size) }} + class: {{ include "fl.value" (list $ . .zookeeper.storage.class) }} + deleteClaim: false + entityOperator: + priorityClassName: {{ include "fl.valueQuoted" (list $ . .priorityClassName) }} + template: + pod: + metadata: + labels: + apps-kafka-strimzi: entity-operator + priorityClassName: {{ include "fl.valueQuoted" (list $ . .priorityClassName) }} + {{- with .entityOperator.tolerations }} + tolerations: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 8 }} + {{- end }} + {{- with .entityOperator.affinity }} + affinity: {{ include "fl.value" (list $ $.CurrentApp .) | nindent 10 }} + {{- end }} + topicOperator: + resources: {{ include "fl.generateContainerResources" (list $ . .entityOperator.topicOperator.resources) | nindent 8 }} + userOperator: + resources: {{ include "fl.generateContainerResources" (list $ . .entityOperator.userOperator.resources) | nindent 8 }} + +{{- include "kafka-topics" (list $ . .topics) }} + +--- +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-kafka +spec: + targetRef: + apiVersion: "apps/v1" + kind: StatefulSet + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-kafka + updatePolicy: + updateMode: "Off" + +--- +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-zookeeper +spec: + targetRef: + apiVersion: "apps/v1" + kind: StatefulSet + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-zookeeper + updatePolicy: + updateMode: "Off" + +--- +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-entity-operator +spec: + targetRef: + apiVersion: "apps/v1" + kind: Deployment + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-entity-operator + updatePolicy: + updateMode: "Off" + +--- +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-kafka-exporter +spec: + targetRef: + apiVersion: "apps/v1" + kind: Deployment + name: {{ $.CurrentKafka.name }}-{{ $.Values.global.env }}-kafka-exporter + updatePolicy: + updateMode: "Off" + + +{{- end }} +{{- end }} + +{{- define "kafka-topics"}} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $topics := index . 2 }} +{{- range $name, $topic := $topics }} +--- +apiVersion: kafka.strimzi.io/v1beta1 +kind: KafkaTopic +metadata: + name: {{ $name }} + labels: + strimzi.io/cluster: {{ $.CurrentApp.name }}-{{ $.Values.global.env }} +spec: + topicName: {{ $name }} + partitions: {{ include "fl.value" (list $ . .partitions) }} + replicas: {{ include "fl.value" (list $ . .replicas) }} + config: + retention.ms: {{ include "fl.value" (list $ . .retention) }} + segment.bytes: {{ include "fl.value" (list $ . .segment_bytes) }} + min.insync.replicas: {{ include "fl.value" (list $ . .min_insync_replicas) }} +{{- end }} +{{- end }} + diff --git a/charts/helm-apps/templates/_apps-limit-range.tpl b/charts/helm-apps/templates/_apps-limit-range.tpl new file mode 100644 index 0000000..5ed1064 --- /dev/null +++ b/charts/helm-apps/templates/_apps-limit-range.tpl @@ -0,0 +1,19 @@ +{{- define "apps-limit-range" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-limit-range" "name" "apps-limit-range") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-limit-range.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +apiVersion: v1 +kind: LimitRange +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + limits: {{- include "fl.value" (list $ . .limits) | nindent 4 }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-prometheus.tpl b/charts/helm-apps/templates/_apps-prometheus.tpl new file mode 100644 index 0000000..956aa0f --- /dev/null +++ b/charts/helm-apps/templates/_apps-prometheus.tpl @@ -0,0 +1,57 @@ +{{- define "apps-custom-prometheus-rules" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-custom-prometheus-rules" "name" "apps-custom-prometheus-rules") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-custom-prometheus-rules.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +apiVersion: deckhouse.io/v1 +kind: CustomPrometheusRules +metadata: + labels: + component: rules + prometheus: main + name: {{ include "fl.value" (list $ . .name)}} +spec: + groups: + {{- range $_groupName, $_group := .groups }} + - name: {{ include "fl.value" (list $ . $_groupName)}} + rules: + {{- range $_alertName, $alert := .alerts}} + - alert: {{ include "fl.value" (list $ . $_alertName)}} + {{- if include "fl.isTrue" (list $ . .isTemplate) }} + {{- include "fl.value" (list $ . .content) | nindent 10 }} + {{- else }} + {{- .content | nindent 10 }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "apps-grafana-dashboards" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-grafana-dashboards" "name" "apps-grafana-dashboards") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-grafana-dashboards.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +apiVersion: deckhouse.io/v1alpha1 +kind: GrafanaDashboardDefinition +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + folder: {{ include "fl.value" (list $ . .folder) | default "Custom" }} + definition: | + {{ $.Files.Get (printf "dashboards/%s.json" .name) | nindent 4 }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-pvcs.tpl b/charts/helm-apps/templates/_apps-pvcs.tpl new file mode 100644 index 0000000..6933251 --- /dev/null +++ b/charts/helm-apps/templates/_apps-pvcs.tpl @@ -0,0 +1,23 @@ +{{- define "apps-pvcs" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-pvcs" "name" "apps-pvcs") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-pvcs.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +kind: PersistentVolumeClaim +apiVersion: v1 +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $specsPVCs := dict }} + {{- $_ := set $specsPVCs "Lists" ( list "accessModes" ) }} + {{- $_ := set $specsPVCs "Maps" (list "resources" ) }} + {{- $_ := set $specsPVCs "Strings" (list "storageClassName") }} + {{- include "apps-utils.generateSpecs" (list $ . $specsPVCs) | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-secrets.tpl b/charts/helm-apps/templates/_apps-secrets.tpl new file mode 100644 index 0000000..e668c39 --- /dev/null +++ b/charts/helm-apps/templates/_apps-secrets.tpl @@ -0,0 +1,27 @@ +{{- define "apps-secrets" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-secrets" "name" "apps-secrets") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-secrets.render" }} +{{- $ := . }} +{{- $_ := set $ "CurrentSecret" $.CurrentApp }} +{{- with $.CurrentApp }} +apiVersion: v1 +kind: Secret +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +type: {{- include "fl.value" (list $ . .type) | default "Opaque" | nindent 2 }} +data: +{{- if (include "fl.value" (list $ . .data)) }} +{{- include "fl.value" (list $ . .data) | nindent 2}} +{{- else }} +{{- include "fl.generateSecretEnvVars" (list $ . .envVars) | nindent 2 }} +{{- include "fl.generateSecretData" (list $ . .data) | nindent 2 }} +{{- end }} + +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-specs.tpl b/charts/helm-apps/templates/_apps-specs.tpl new file mode 100644 index 0000000..a44ed95 --- /dev/null +++ b/charts/helm-apps/templates/_apps-specs.tpl @@ -0,0 +1,58 @@ +{{- define "apps-specs.containers.volumes" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- with $relativeScope }} +{{ include "fl.value" (list $ . .volumes) | trim | nindent 0 }} +{{ include "apps-helpers.generateVolumes" (list $ .) | trim | nindent 0 }} +{{- $_ := set . "__specName__" "volumes"}} +{{- end }} +{{- end }} + +{{- define "apps-specs.selector" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- with $relativeScope }} +matchLabels: {{- include "fl.generateSelectorLabels" (list $ . .name) | nindent 2 }} +{{- $_ := set . "__specName__" "selector"}} +{{- end }} +{{- end }} + +{{- define "apps-specs.serviceName" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- with $relativeScope }} +{{- include "fl.value" (list $ . .service.name) }} +{{- $_ := set . "__specName__" "serviceName"}} +{{- end }} +{{- end }} + +{{- define "apps-specs.volumeClaimTemplates" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- with $relativeScope }} +{{- include "fl.value" (list $ . .volumeClaimTemplates) | nindent 0 }} +{{- /* Loop through containers to generate Pod volumes */ -}} +{{- range $_, $containersType := list "initContainers" "containers" }} +{{- range $_containerName, $_container := index $.CurrentApp $containersType }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- $_ := set . "name" $_containerName }} +{{- $_ = set $ "CurrentContainer" $_container }} +{{- range $persistantVolumeName, $persistantVolume := .persistantVolumes }} +{{- $pvcName := print $persistantVolumeName "-" $containersType "-" $.CurrentApp.name "-" $.CurrentContainer.name "-" $persistantVolume.mountPath | include "fl.formatStringAsDNSLabel" }} +- metadata: + name: {{ $pvcName }} + spec: + accessModes: {{include "fl.value" (list $ . $persistantVolume.accessModes) | default "\n- ReadWriteOnce" | nindent 4 }} + resources: + requests: + storage: {{ include "fl.value" (list $ . $persistantVolume.size) }} + storageClassName: {{ include "fl.value" (list $ . $persistantVolume.storageClass) }} + volumeMode: Filesystem +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- $_ := set . "__specName__" "volumeClaimTemplates"}} +{{- end }} +{{- end }} + diff --git a/charts/helm-apps/templates/_apps-stateful.tpl b/charts/helm-apps/templates/_apps-stateful.tpl new file mode 100644 index 0000000..290507f --- /dev/null +++ b/charts/helm-apps/templates/_apps-stateful.tpl @@ -0,0 +1,59 @@ +{{- define "apps-stateful" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-stateful" "name" "apps-stateful") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-stateful.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +{{- if not .containers }} +{{- fail (printf "Установлено значение enabled для не настроенного '%s' в %s приложения!" $.CurrentApp.name "apps-stateful") }} +{{- end }} +{{/* Defaults values */}} +{{- if .service }} +{{- if include "fl.isTrue" (list $ . .service.enabled) }} +{{- if not .service.name }} +{{- $_ := set .service "name" .name }} +{{- end }} +{{- end }} +{{- end }} +{{/* Defaults values end */}} +{{- $serviceAccount := include "apps-system.serviceAccount" $ }} +apiVersion: apps/v1 +kind: StatefulSet +{{ $_ := set . "__annotations__" dict }} +{{- if .reloader }} +{{- $_ := set .__annotations__ "pod-reloader.deckhouse.io/auto" "true" }} +{{- else }} +{{- $_ := set . "__annotations__" (include "apps-components.generate-config-checksum" (list $ .) | fromYaml) }} +{{- end }} +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $specs := dict }} + {{- $_ = set $specs "Numbers" (list "minReadySeconds" "progressDeadlineSeconds" "revisionHistoryLimit") }} + {{- $_ = set $specs "Maps" (list "apps-helpers.podTemplate" "apps-specs.selector") }} + {{- $_ = set $specs "Numbers" (list "replicas") }} + {{- $_ = set $specs "Strings" (list "apps-specs.serviceName") }} + {{- $_ = set $specs "Lists" (list "apps-specs.volumeClaimTemplates") }} + {{- include "apps-utils.generateSpecs" (list $ . $specs) | nindent 2 }} + {{- $_ = unset . "__annotations__" -}} + +{{- include "apps-components.generateConfigMapsAndSecrets" $ -}} + +{{- include "apps-components.service" (list $ . .service) -}} + +{{- include "apps-components.podDisruptionBudget" (list $ . .podDisruptionBudget) -}} + +{{- include "apps-components.verticalPodAutoscaler" (list $ . .verticalPodAutoscaler "StatefulSet") -}} + +{{- include "apps-deckhouse.metrics" $ -}} + +{{ $serviceAccount -}} + +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/helm-apps/templates/_apps-stateless.tpl b/charts/helm-apps/templates/_apps-stateless.tpl new file mode 100644 index 0000000..aaba6f2 --- /dev/null +++ b/charts/helm-apps/templates/_apps-stateless.tpl @@ -0,0 +1,61 @@ +{{- define "apps-stateless" }} + {{- $ := index . 0 }} + {{- $RelatedScope := index . 1 }} + {{- if not (kindIs "invalid" $RelatedScope) }} + {{- $_ := set $RelatedScope "__GroupVars__" (dict "type" "apps-stateless" "name" "apps-stateless") }} + {{- include "apps-utils.renderApps" (list $ $RelatedScope) }} +{{- end -}} +{{- end -}} + +{{- define "apps-stateless.render" }} +{{- $ := . }} +{{- with $.CurrentApp }} +{{- if kindIs "invalid" .containers }} +{{- fail (printf "Установлено значение enabled для не настроенного '%s' в %s приложения!" $.CurrentApp.name "apps-stateless") }} +{{- end }} +{{- if .service }} +{{- if include "fl.isTrue" (list $ . .service.enabled) }} +{{- if not .service.name }} +{{- $_ := set .service "name" .name }} +{{- end }} +{{- end }} +{{- end }} +{{- $serviceAccount := include "apps-system.serviceAccount" $ }} +apiVersion: apps/v1 +kind: Deployment +{{ $_ := set . "__annotations__" dict }} +{{- if .reloader }} +{{- $_ := set .__annotations__ "pod-reloader.deckhouse.io/auto" "true" }} +{{- else }} +{{- $_ := set . "__annotations__" (include "apps-components.generate-config-checksum" (list $ .) | fromYaml) }} +{{- end }} +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +spec: + {{- $specs := dict }} + {{- $_ = set $specs "Numbers" (list "minReadySeconds" "progressDeadlineSeconds" "revisionHistoryLimit") }} + {{- $_ = set $specs "Maps" (list "strategy" "apps-helpers.podTemplate" "apps-specs.selector") }} + {{- include "apps-utils.generateSpecs" (list $ . $specs) | indent 2 }} + {{- if .horizontalPodAutoscaler }} + {{- if include "fl.isFalse" (list $ . .horizontalPodAutoscaler.enabled) }} + replicas: {{ include "fl.value" (list $ . .replicas) }} + {{- end }} + {{- else if .replicas }} + replicas: {{ include "fl.value" (list $ . .replicas) }} + {{- end }} +{{- $_ = unset . "__annotations__" }} +{{- include "apps-components.generateConfigMapsAndSecrets" $ -}} + +{{- include "apps-components.service" (list $ . .service) -}} + +{{- include "apps-components.podDisruptionBudget" (list $ . .podDisruptionBudget) -}} + +{{- include "apps-components.verticalPodAutoscaler" (list $ . .verticalPodAutoscaler "Deployment") }} + +{{- include "apps-components.horizontalPodAutoscaler" (list $ . "Deployment") -}} + +{{- include "apps-deckhouse.metrics" $ -}} + +{{ $serviceAccount -}} + +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-system.tpl b/charts/helm-apps/templates/_apps-system.tpl new file mode 100644 index 0000000..181bb55 --- /dev/null +++ b/charts/helm-apps/templates/_apps-system.tpl @@ -0,0 +1,41 @@ +{{- define "apps-system.serviceAccount" }} +{{- $ := . }} +{{- with $.CurrentApp.serviceAccount }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- include "apps-utils.enterScope" (list $ "serviceAccount") }} +{{- if not (hasKey . "name") }} +{{- $_ := set . "name" $.CurrentApp.name }} +{{- end }} +{{- $serviceAccountName := include "fl.value" (list $ . .name) }} +{{- $_ := set $.CurrentApp "serviceAccountName" $serviceAccountName }} +--- +apiVersion: v1 +kind: ServiceAccount +{{- include "apps-helpers.metadataGenerator" (list $ .) }} +{{- if hasKey . "clusterRole" }} +{{- include "apps-utils.enterScope" (list $ "clusterRole") }} +{{- $roleName := include "apps-utils.requiredValue" (list $ .clusterRole "name") }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +{{- include "apps-helpers.metadataGenerator" (list $ .clusterRole) }} +rules: +{{- include "apps-utils.requiredValue" (list $ .clusterRole "rules") | nindent 2 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +{{- include "apps-helpers.metadataGenerator" (list $ .clusterRole) }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $roleName }} +subjects: +- kind: ServiceAccount + name: {{ $serviceAccountName }} + namespace: {{ $.Values.werf.namespace }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-utils.tpl b/charts/helm-apps/templates/_apps-utils.tpl new file mode 100644 index 0000000..fcb6ed9 --- /dev/null +++ b/charts/helm-apps/templates/_apps-utils.tpl @@ -0,0 +1,236 @@ +{{- define "apps-check-password" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- $val := index . 2 }} +{{- $msg := index . 3 }} +{{- $password := include "fl.value" (list $ $RelatedScope $val) }} +{{- if eq $password "!!!CHANGE_ME!!!" }} +{{- fail $msg }} +{{- else }} +{{- $password }} +{{- end }} +{{- end }} + +{{- define "fl.generateContainerImageQuoted" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- $imageConfig := index . 2 }} +{{- $imageName := include "fl.value" (list $ . $imageConfig.name) }} +{{- if include "fl.value" (list $ . $imageConfig.staticTag) }} +{{- $imageName }}:{{ include "fl.value" (list $ . $imageConfig.staticTag) }} +{{- else -}} +{{- index $.Values.werf.image $imageName }} +{{- end }} +{{- end }} + +{{- define "apps-utils.generateSpecs" }} +{{- $ := index . 0 }} +{{- $relativeScope := index . 1 }} +{{- $specs := index . 2 -}} +{{- if hasKey $specs "Required" }} +{{- range $_, $specName := $specs.Required }} +{{- $_ := include "apps-utils.requiredValue" (list $ $relativeScope $specName) }} +{{- end }} +{{- end }} +{{- with $specs.Lists }} +{{- range $_, $specName := . }} +{{- if hasPrefix "apps-" $specName }} +{{- with include $specName (list $ $relativeScope (index $relativeScope .)) | trim }} +{{ $relativeScope.__specName__ }}: {{ print . | nindent 0 }} +{{- end }} +{{- else }} +{{- with include "fl.value" (list $ $relativeScope (index $relativeScope .)) | trim }} +{{ $specName }}: {{ print . | trim | nindent 0 }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- with $specs.Maps }} +{{- range $_, $specName := . }} +{{- if hasPrefix "apps-" $specName }} +{{- with include $specName (list $ $relativeScope (index $relativeScope .)) | trim }} +{{ $relativeScope.__specName__ }}: {{ print . | nindent 2 }} +{{- end }} +{{- else }} +{{- with include "fl.value" (list $ $relativeScope (index $relativeScope .)) | trim }} +{{ $specName }}: {{ print . | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- with $specs.Strings }} +{{- range $_, $specName := . }} +{{- if hasPrefix "apps-" $specName }} +{{- with include $specName (list $ $relativeScope (index $relativeScope .)) }} +{{ $relativeScope.__specName__ }}: {{ print . | quote }} +{{- end }} +{{- else }} +{{- with include "fl.valueQuoted" (list $ $relativeScope (index $relativeScope .)) }} +{{ $specName }}: {{ . }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- with $specs.Numbers }} +{{- range $_, $specName := . }} +{{- if hasPrefix "apps-" $specName }} +{{- with include $specName (list $ $relativeScope (index $relativeScope .)) }} +{{ $relativeScope.__specName__ }}: {{ print . }} +{{- end }} +{{- else }} +{{- with include "fl.value" (list $ $relativeScope (index $relativeScope .)) }} +{{ $specName }}: {{ . }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- with $specs.Bools }} +{{- range $_, $specName := . }} +{{- if hasPrefix "apps-" $specName }} +{{- $specValue := include $specName (list $ $relativeScope (index $relativeScope .)) | trim }} +{{- if $specValue }} +{{ $relativeScope.__specName__ }}: {{ print $specValue }} +{{- end }} +{{- else }} +{{- if ne (include "fl.value" (list $ $relativeScope (index $relativeScope .))) "" }} +{{ $specName }}: {{ include "fl.isTrue" (list $ $relativeScope (index $relativeScope .)) }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "apps-utils.requiredValue" }} +{{- $ := index . 0 }} +{{- $RelatedScope := index . 1 }} +{{- $VarName := index . 2 }} +{{- required (printf "You need a valid entry in %s.%s" ($.CurrentPath | join ".") $VarName ) (include "fl.value" (list $ $RelatedScope (index $RelatedScope $VarName ))) }} +{{- end }} +{{- define "apps-utils.enterScope" }} +{{- $ := index . 0 }} +{{- $ScopeName := index . 1 }} +{{- if not (kindIs "slice" $.CurrentPath) }} +{{- $_ := set $ "CurrentPath" list }} +{{- end }} +{{- $path := append $.CurrentPath $ScopeName }} +{{- $_ := set $ "CurrentPath" $path }} +{{- end }} +{{- define "apps-utils.leaveScope" }} +{{- $_ := set . "CurrentPath" (initial .CurrentPath ) }} +{{- end }} +{{- define "apps-utils.preRenderHooks" }} +{{- $ := . }} +{{- if hasKey $ "CurrentGroupVars" }} +{{- if hasKey $.CurrentGroupVars "_preRenderAppHook" }} +{{- $_ := include "fl.value" (list $ $.CurrentApp $.CurrentGroupVars._preRenderAppHook) }} +{{- end }} +{{- if hasKey $.CurrentGroupVars "_groupPreRenderHook" }} +{{- $_ := include "fl.value" (list $ $.CurrentApp $.CurrentGroupVars._groupPreRenderHook) }} +{{- end }} +{{- end }} +{{- if hasKey $.CurrentApp "_preRenderHook" }} +{{- $_ := include "fl.value" (list $ $.CurrentApp $.CurrentApp._preRenderHook) }} +{{- end }} +{{- end }} +{{- define "apps-utils.renderApps" }} +{{- $ := index . 0 }} +{{- $appScope := index . 1 }} +{{- include "apps-utils.enterScope" (list $ $appScope.__GroupVars__.name) }} +{{- include "_apps-utils.initCurrentGroupVars" (list $ $appScope $appScope.__GroupVars__.name) }} +{{- range $_appName, $_app := omit $appScope "global" "enabled" "_include" "__GroupVars__" "__AppType__" }} +{{- if hasKey . "__GroupVars__" }} +{{- include "_apps-utils.initCurrentGroupVars" (list $ . $_appName) }} +{{- include "apps-utils.renderApps" (list $ . $.CurrentGroupVars.type) }} +{{- include "_apps-utils.initCurrentGroupVars" (list $ $appScope $appScope.__GroupVars__.name) }} +{{- else }} +{{- include "apps-utils.enterScope" (list $ $_appName) }} +{{- $type := $.CurrentGroupVars.type }} +{{- if hasKey . "__AppType__" }} +{{- $type = .__AppType__ }} +{{- end }} +{{- $_ := set . "__AppName__" $_appName }} +{{- if hasKey . "name" }} +{{- $_ := set . "name" (include "fl.value" (list $ . .name)) }} +{{- else }} +{{- $_ := set . "name" $_appName }} +{{- end }} +{{- $_ := set $ "CurrentApp" . }} +{{- include "apps-utils.preRenderHooks" $ }} +{{- if (include "fl.isTrue" (list $ . .randomName)) }} +{{- $_ := set . "name" (printf "%s-%s" .name (randAlphaNum 7 | lower)) }} +{{- end }} +{{- if include "fl.isTrue" (list $ . .enabled) }} +{{- if not .__Rendered__ }} +{{- include "apps-utils.printPath" $ }} +{{- include "apps-helpers.activateContainerForDefault" $ }} +{{- include (printf "%s.render" $type) $ }} +{{- end }} +{{- end }} +{{- $_ = set . "__Rendered__" true }} +{{- include "apps-utils.leaveScope" $ }} +{{- end }} +{{- end }} +{{- include "apps-utils.leaveScope" $ }} +{{- end -}} + +{{- define "_apps-utils.initCurrentGroupVars" }} +{{- $ := index . 0 }} +{{- $groupScope := index . 1 }} +{{- $groupName := index . 2 }} +{{- $_ := set $groupScope.__GroupVars__ "name" $groupName }} +{{- if kindIs "invalid" $groupScope.__GroupVars__.type }} +{{- if not (kindIs "invalid" $.CurrentGroupVars) }} +{{- $_ = set $groupScope.__GroupVars__ "type" (include "apps-utils.requiredValue" (list $ $.CurrentGroupVars "type")) }} +{{- end }} +{{- end }} +{{- $_ = set $ "CurrentGroupVars" $groupScope.__GroupVars__ }} +{{- $_ = set $ "CurrentGroup" $groupScope }} +{{- if hasKey $groupScope.__GroupVars__ "_preRenderGroupHook" }} +{{- $_ = include "fl.value" (list $ $groupScope $groupScope.__GroupVars__._preRenderGroupHook) }} +{{- end }} +{{- end }} + +{{- define "apps-utils.init-library" }} +{{- $ := . }} +{{- $_ := include "fl.expandIncludesInValues" (list $ $.Values) }} +{{- include "apps-utils.findApps" $ }} +--- +# Source: apps.utils: fl.expandIncludesInValues +{{- $Library := list +"stateless" +"stateful" +"ingresses" +"cronjobs" +"jobs" +"configmaps" +"secrets" +"dex-clients" +"dex-authenticators" +"limit-range" +"kafka-strimzi" +"custom-prometheus-rules" +"grafana-dashboards" +"infra" +"pvcs" +"certificates" +}} +{{- range $app := $Library }} +{{- include (printf "apps-%s" $app) (list $ (index $.Values (printf "apps-%s" $app))) }} +{{- end }} +--- +{{- end }} +{{- define "apps-utils.findApps" }} +{{- $ := . }} +{{- range $groupName, $group := omit $.Values "global" "enabled" "_include" }} +{{- if kindIs "map" $group }} +{{- if hasKey $group "__GroupVars__" }} +{{- $_ := set $group.__GroupVars__ "name" $groupName }} +{{- include "apps-utils.renderApps" (list $ .) }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "apps-utils.printPath" }} +{{- printf "\n---\n# Helm Apps Library: %s" (.CurrentPath | join ".") }} +{{- end }} diff --git a/charts/helm-apps/templates/_apps-version.tpl b/charts/helm-apps/templates/_apps-version.tpl new file mode 100644 index 0000000..83ca04d --- /dev/null +++ b/charts/helm-apps/templates/_apps-version.tpl @@ -0,0 +1,3 @@ +{{- define "apps-version.getLibraryVersion" }} +{{- "_FLANT_APPS_LIBRARY_VERSION_" }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_expandIncludesInValues.tpl b/charts/helm-apps/templates/fl-functions/_expandIncludesInValues.tpl new file mode 100644 index 0000000..e2e6cd5 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_expandIncludesInValues.tpl @@ -0,0 +1,154 @@ +{{/* The stuff below is complex and scary, and I can't make it simpler */}} + +{{- define "fl.expandIncludesInValues" }} +{{- $ := index . 0 }} +{{- $location := index . 1 }} {{/* Expand includes recursively starting here */}} +{{- if kindIs "map" $location }} +{{- include "fl._recursiveMergeAndExpandIncludes" (list $ $location) }} +{{- else if kindIs "slice" $location }} +{{- range $_, $locationNested := $location }} +{{- include "fl.expandIncludesInValues" (list $ $locationNested) }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "_fl.make_includes_from" }} +{{- $ := index . 0 }} +{{- $prevContext := index . 1 }} +{{- $curContext := index . 2 }} +{{- $varName := index . 3 }} +{{- if kindIs "map" $curContext }} +{{- if hasKey $curContext "_include_from" }} +{{- $includeVar := "" }} +{{- $excludeParam := list }} +{{- $includeParams := $curContext._include_from }} +{{- if kindIs "map" $includeParams }} +{{- $excludeParam = $curContext._include_from.exclude }} +{{- $includeVar = $curContext._include_from.path }} +{{- else }} +{{- $includeVar = $curContext._include_from }} +{{- end }} +{{- $tmpMap := include "_getMapKeyValue" (list $.Values $includeVar) | fromJson }} +{{- if gt (len $excludeParam) 0 }} +{{- range $e := $excludeParam }} +{{- $_ := unset $tmpMap $e }} +{{- end }} +{{- end }} +{{- $curContext := mergeOverwrite $curContext $tmpMap }} +{{- $_ := set $prevContext $varName $curContext }} +{{- $_ = unset $curContext "_include_from" }} +{{- end }} +{{- range $key,$varsDict := $curContext -}} +{{- if kindIs "map" $varsDict -}} +{{- if gt (len $varsDict) 0 -}} +{{- include "_fl.make_includes_from" (list $ $curContext $varsDict $key) -}} +{{- end -}} +{{- end -}} +{{- end }} +{{- end }} +{{- end }} + +{{- define "_getMapKeyValue" }} +{{- $map := index . 0 }} +{{- $path := index . 1 }} +{{- $tmpMap := $map }} +{{- if contains "." $path }} +{{- $keys := regexSplit "\\." $path -1 }} +{{- range $k := $keys }} +{{ if kindIs "map" $tmpMap }} +{{- $tmpValue := get $tmpMap $k }} +{{- if kindIs "map" $tmpValue }} +{{ $tmpMap = $tmpValue }} +{{- else }} +{{ fail $k }} +{{- end }} +{{- end }} +{{- end }} +{{- else }} +{{- $tmpMap = get $tmpMap $path }} +{{- end }} +{{- toJson $tmpMap }} +{{- end }} + +{{- define "fl._recursiveMergeAndExpandIncludes" }} + {{- $ := index . 0 }} + {{- $mergeInto := index . 1 }} + + {{- if kindIs "map" $mergeInto }} + {{- if hasKey $mergeInto "_include" }} + {{- $joinedIncludes := (include "fl._getJoinedIncludesInJson" (list $ $mergeInto._include) | fromJson).wrapper }} + {{- $_ := unset $mergeInto "_include" }} + {{- include "fl._recursiveMapsMerge" (list $ $joinedIncludes $mergeInto) }} + {{- include "fl.expandIncludesInValues" (list $ $mergeInto) }} + {{- else }} + {{- range $nestedKey, $nestedVal := $mergeInto }} + {{- if ne $nestedKey "_includes" }} + {{- include "fl.expandIncludesInValues" (list $ $nestedVal) }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "fl._getJoinedIncludesInJson" }} + {{- $ := index . 0 }} + {{- $includesNames := index . 1 }} + + {{- $includesBodies := list }} + {{- range $_, $includeName := $includesNames }} + {{- $includesBodies = append $includesBodies (index $.Values.global._includes $includeName) }} + {{- end }} + + {{- $joinedIncludesResult := dict }} + {{- range $i, $includeBody := reverse $includesBodies }} + {{- include "fl._recursiveMapsMerge" (list $ $includeBody $joinedIncludesResult) }} + {{- end }} + {{- dict "wrapper" $joinedIncludesResult | toJson }} +{{- end }} + +{{- define "fl._recursiveMapsMerge" }} + {{- $ := index . 0 }} + {{- $mapToMergeFrom := index . 1 }} + {{- $mapToMergeInto := index . 2 }} + + {{- range $keyToMergeFrom, $valToMergeFrom := $mapToMergeFrom }} + {{- $valToMergeInto := index $mapToMergeInto $keyToMergeFrom }} + + {{- if kindIs "map" $valToMergeFrom }} + {{- if kindIs "map" $valToMergeInto }} + {{- include "fl._recursiveMapsMerge" (list $ $valToMergeFrom $valToMergeInto) }} + {{- else if not (hasKey $mapToMergeInto $keyToMergeFrom) }} + {{- $_ := set $mapToMergeInto $keyToMergeFrom $valToMergeFrom }} + {{- end }} + + {{- else if kindIs "slice" $valToMergeFrom }} + {{- if eq $keyToMergeFrom "_include" }} + {{- if kindIs "slice" $valToMergeInto }} + {{- $joinedIncludes := (include "fl._concatLists" (list $valToMergeFrom $valToMergeInto) | fromJson).wrapper }} + {{- $_ := unset $mapToMergeInto "_include" }} + {{- $_ := set $mapToMergeInto "_include" $joinedIncludes }} + {{- else }} + {{- $_ := unset $mapToMergeInto "_include" }} + {{- $_ := set $mapToMergeInto "_include" $valToMergeFrom }} + {{- end }} + {{- end }} + + {{- else }} + {{- if not (hasKey $mapToMergeInto $keyToMergeFrom) }} + {{- $_ := set $mapToMergeInto $keyToMergeFrom $valToMergeFrom }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "fl._concatLists" }} + {{- $lists := index . }} + + {{- $result := list }} + {{- range $_, $list := $lists }} + {{- range $_, $list_elem := $list }} + {{- $result = append $result $list_elem }} + {{- end }} + {{- end }} + {{- dict "wrapper" $result | toJson }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_formatStringAsDNSLabel.tpl b/charts/helm-apps/templates/fl-functions/_formatStringAsDNSLabel.tpl new file mode 100644 index 0000000..13eab28 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_formatStringAsDNSLabel.tpl @@ -0,0 +1,9 @@ +{{- define "fl.formatStringAsDNSLabel" }} + {{- $string := . }} + + {{- $result := $string | lower | nospace | replace "_" "-" | replace "/" "-" | replace "\\" "-" | replace ":" "-" | replace "," "-" | replace "." "-" }} + {{- if gt (len $result) 63 }} + {{- $result = printf "%s-%s" (trunc 53 $result) (adler32sum $result | trunc 9 ) }} + {{- end }} + {{- $result }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_formatStringAsDNSSubdomain.tpl b/charts/helm-apps/templates/fl-functions/_formatStringAsDNSSubdomain.tpl new file mode 100644 index 0000000..71143c1 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_formatStringAsDNSSubdomain.tpl @@ -0,0 +1,9 @@ +{{- define "fl.formatStringAsDNSSubdomain" }} + {{- $string := . }} + + {{- $result := $string | lower | nospace | replace "_" "-" | replace "/" "-" | replace "\\" "-" | replace ":" "-" | replace "," "-" }} + {{- if gt (len $result) 253 }} + {{- $result = printf "%s-%s" (trunc 243 $result) (adler32sum $result | trunc 9 ) }} + {{- end }} + {{- $result }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_isFalse.tpl b/charts/helm-apps/templates/fl-functions/_isFalse.tpl new file mode 100644 index 0000000..e6d8404 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_isFalse.tpl @@ -0,0 +1,3 @@ +{{- define "fl.isFalse" }} + {{- ternary "" true (include "fl.value" . | eq "true") }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_isTrue.tpl b/charts/helm-apps/templates/fl-functions/_isTrue.tpl new file mode 100644 index 0000000..b21015d --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_isTrue.tpl @@ -0,0 +1,3 @@ +{{- define "fl.isTrue" }} + {{- ternary true "" (include "fl.value" . | eq "true") }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_percentage.tpl b/charts/helm-apps/templates/fl-functions/_percentage.tpl new file mode 100644 index 0000000..c0c0011 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_percentage.tpl @@ -0,0 +1,12 @@ +{{/* TODO: refactor, document */}} +{{/* +{{- define "fl.percentage" }} + {{- $num := index . 0 }} + {{- $percents := index . 1 }} + + {{- $num_mul_by_1000 := mul $num 1000 }} + {{- $1_percent_of_num_mul_by_1000 := div $num_mul_by_1000 100 }} + {{- $result_mul_by_1000 := mul $1_percent_of_num_mul_by_1000 $percents }} + {{- div $result_mul_by_1000 1000 }} +{{- end }} +*/}} diff --git a/charts/helm-apps/templates/fl-functions/_value.tpl b/charts/helm-apps/templates/fl-functions/_value.tpl new file mode 100644 index 0000000..4b6a4e1 --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_value.tpl @@ -0,0 +1,82 @@ +{{- define "fl.value" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $val := index . 2 }} + {{- $prefix := "" }} {{- /* Optional */ -}} + {{- $suffix := "" }} {{- /* Optional */ -}} + {{- if gt (len .) 3 }} + {{- $optionalArgs := index . 3 }} + {{- if hasKey $optionalArgs "prefix" }} + {{- $prefix = $optionalArgs.prefix }} + {{- end }} + {{- if hasKey $optionalArgs "suffix" }} + {{- $suffix = $optionalArgs.suffix }} + {{- end }} + {{- end }} + + {{- if kindIs "map" $val }} + {{- $currentEnvVal := "" }} + {{- if hasKey $val $.Values.global.env }} + {{- $currentEnvVal = index $val $.Values.global.env }} + {{- else if eq (include "_fl.getValueRegex" .) "" }} + {{- $currentEnvVal = $._CurrentFuncResult }} + {{- else if hasKey $val "_default" }} + {{- $currentEnvVal = index $val "_default" }} + {{- end }} + {{- include "fl._renderValue" (list $ $relativeScope $currentEnvVal $prefix $suffix) }} + {{- else }} + {{- include "fl._renderValue" (list $ $relativeScope $val $prefix $suffix) }} + {{- end }} +{{- end }} + +{{- define "fl._renderValue" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $val := index . 2 }} + {{- $prefix := index . 3 }} + {{- $suffix := index . 4 }} + + {{- if and (not (kindIs "map" $val)) (not (kindIs "slice" $val)) }} + {{- $valAsString := toString $val }} + {{- if not (regexMatch "^<(nil|no value)>$" $valAsString) }} + {{- $result := "" }} + {{- if contains "{{" $valAsString }} + {{- if empty $relativeScope }} + {{- $relativeScope = $ }} {{- /* tpl fails if $relativeScope is empty */ -}} + {{- end }} + {{- $result = tpl (printf "%s{{ with $.RelativeScope }}%s{{ end }}%s" $prefix $valAsString $suffix) (merge (dict "RelativeScope" $relativeScope) $) }} + {{- else }} + {{- $result = printf "%s%s%s" $prefix $valAsString $suffix }} + {{- end }} + {{- if ne $result "" }}{{ $result }}{{ end }} + {{- end }} + {{- end }} +{{- end -}} + +{{- define "_fl.getValueRegex" }} +{{- $ := index . 0 }} +{{- $val := index . 2 }} +{{- $_ := set $ "_CurrentFuncError" "" }} +{{- $regexList := list }} +{{- range $env, $value := omit $val "_default" $.Values.global.env }} +{{- $env = trimPrefix "^" $env }} +{{- $env = trimSuffix "$" $env }} +{{- $env = printf "^%s$" $env }} +{{- if regexMatch $env $.Values.global.env }} +{{- $_ := set $ "_CurrentFuncResult" $value }} +{{- $regexList = append $regexList $env }} +{{- end }} +{{- end }} +{{- if gt (len $regexList) 1 }} +{{- fail (printf "В переменной %s используется неоднозначное определение окружения %s" ($.CurrentPath | join ".") $regexList) }} +{{- end }} +{{- if eq (len $regexList) 0 }} +{{- print "not found" }} +{{- end }} +{{- end -}} + +{{- define "fl.Result" }} +{{- $ := index . 0 }} +{{- $_ := set $ "_CurrentFuncResult" (index . 1) }} +{{- $_ = set $ "_CurrentFuncError" (index . 2) }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_valueQuoted.tpl b/charts/helm-apps/templates/fl-functions/_valueQuoted.tpl new file mode 100644 index 0000000..1ba691d --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_valueQuoted.tpl @@ -0,0 +1,6 @@ +{{- define "fl.valueQuoted" }} + {{- $result := include "fl.value" . }} + {{- if ne $result "" }} + {{- $result | quote }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-functions/_valueSingleQuoted.tpl b/charts/helm-apps/templates/fl-functions/_valueSingleQuoted.tpl new file mode 100644 index 0000000..464e89b --- /dev/null +++ b/charts/helm-apps/templates/fl-functions/_valueSingleQuoted.tpl @@ -0,0 +1,6 @@ +{{- define "fl.valueSingleQuoted" }} + {{- $result := include "fl.value" . }} + {{- if ne $result "" }} + {{- $result | squote }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateConfigMapEnvVars.tpl b/charts/helm-apps/templates/fl-snippets/_generateConfigMapEnvVars.tpl new file mode 100644 index 0000000..290f262 --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateConfigMapEnvVars.tpl @@ -0,0 +1,17 @@ +{{- define "fl.generateConfigMapEnvVars" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $envs := index . 2 }} + + {{- range $envVarName, $envVarVal := $envs }} + {{- if $.Values.global.configFlantLibVariableUppercaseEnvs }} + {{- $envVarName = upper $envVarName }} + {{- end }} + {{- $envVarVal = include "apps.value" (list $ $relativeScope $envVarVal $envVarName) }} + {{- if eq $envVarVal "___FL_THIS_ENV_VAR_WILL_BE_DEFINED_BUT_EMPTY___" }} +{{ $envVarName | quote }}: "" + {{- else if ne $envVarVal "" }} +{{ $envVarName | quote }}: {{ $envVarVal | quote }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateContainerEnvVars.tpl b/charts/helm-apps/templates/fl-snippets/_generateContainerEnvVars.tpl new file mode 100644 index 0000000..a5753d2 --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateContainerEnvVars.tpl @@ -0,0 +1,21 @@ +{{- define "fl.generateContainerEnvVars" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $envs := index . 2 }} + + {{- range $envVarName, $envVarVal := $envs }} + {{- if $.Values.global.configFlantLibVariableUppercaseEnvs }} + {{- $envVarName = upper $envVarName }} + {{- end }} + {{- $envVarVal = include "apps.value" (list $ $relativeScope $envVarVal $envVarName) }} + {{- if eq $envVarVal "___FL_THIS_ENV_VAR_WILL_BE_DEFINED_BUT_EMPTY___" }} +- name: {{ $envVarName | quote }} + value: "" + {{- else if ne $envVarVal "" }} +- name: {{ $envVarName | quote }} + value: {{ $envVarVal | quote }} + {{- end }} + {{- end }} +{{- end }} + + diff --git a/charts/helm-apps/templates/fl-snippets/_generateContainerFromSecretsEnvVars.tpl b/charts/helm-apps/templates/fl-snippets/_generateContainerFromSecretsEnvVars.tpl new file mode 100644 index 0000000..b2f89db --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateContainerFromSecretsEnvVars.tpl @@ -0,0 +1,19 @@ +{{- define "fl.generateContainerFromSecretsEnvVars" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $envsFromSecret := index . 2 }} +{{- range $secretName, $secretVars := $envsFromSecret}} + {{- range $envVarName, $envNameInSecret := $secretVars }} + {{- if $.Values.global.configFlantLibVariableUppercaseEnvs }} + {{- $envVarName = upper $envVarName }} + {{- end }} +- name: {{ $envVarName | quote}} + valueFrom: + secretKeyRef: + name: {{ $secretName | quote}} + key: {{ $envNameInSecret | quote }} + {{- end }} + {{- end }} +{{- end }} + + diff --git a/charts/helm-apps/templates/fl-snippets/_generateContainerImage.tpl b/charts/helm-apps/templates/fl-snippets/_generateContainerImage.tpl new file mode 100644 index 0000000..9960f7d --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateContainerImage.tpl @@ -0,0 +1,12 @@ +{{- define "fl.generateContainerImageQuoted" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $imageConfig := index . 2 }} + + {{- $imageName := include "fl.value" (list $ . $imageConfig.name) }} + {{- if include "fl.value" (list $ . $imageConfig.staticTag) }} + {{- $imageName }}:{{ include "fl.value" (list $ . $imageConfig.staticTag) }} + {{- else -}} + {{- index $.Values.werf.image $imageName }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateContainerResources.tpl b/charts/helm-apps/templates/fl-snippets/_generateContainerResources.tpl new file mode 100644 index 0000000..781dff8 --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateContainerResources.tpl @@ -0,0 +1,15 @@ +{{- define "fl.generateContainerResources" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $resources := index . 2 }} + + {{- range $resourceGroup, $resourceGroupVals := $resources }} + {{- $resourceGroup | nindent 0 }}: + {{- $mcpu := include "fl.value" (list $ . $resourceGroupVals.mcpu (dict "suffix" "m")) }} + {{- if $mcpu }}{{ cat "cpu:" $mcpu | nindent 2 }}{{ end }} + {{- $memoryMb := include "fl.value" (list $ . $resourceGroupVals.memoryMb (dict "suffix" "Mi")) }} + {{- if $memoryMb }}{{ cat "memory:" $memoryMb | nindent 2 }}{{ end }} + {{- $ESMb := include "fl.value" (list $ . $resourceGroupVals.ephemeralStorageMb (dict "suffix" "Mi")) }} + {{- if $ESMb }}{{ cat "ephemeral-storage:" $ESMb | nindent 2 }}{{ end }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateLabels.tpl b/charts/helm-apps/templates/fl-snippets/_generateLabels.tpl new file mode 100644 index 0000000..4519395 --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateLabels.tpl @@ -0,0 +1,8 @@ +{{- define "fl.generateLabels" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $appName := index . 2 }} +app: {{ $appName | quote }} +chart: {{ $.Chart.Name | trunc 63 | quote }} +repo: {{ regexSplit "/" $.Values.werf.repo -1 | rest | join "-" | trunc 63 | quote }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateSecretData.tpl b/charts/helm-apps/templates/fl-snippets/_generateSecretData.tpl new file mode 100644 index 0000000..b1c3fbf --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateSecretData.tpl @@ -0,0 +1,17 @@ +{{- define "fl.generateSecretData" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $data := index . 2 }} + + {{- range $key, $value := $data }} + {{- if $.Values.global.configFlantLibVariableUppercaseEnvs }} + {{- $key = upper $key }} + {{- end }} + {{- $value = include "apps.value" (list $ $relativeScope $value $key) }} + {{- if eq $value "___FL_THIS_ENV_VAR_WILL_BE_DEFINED_BUT_EMPTY___" }} +{{ $key | quote }}: "" + {{- else if ne $value "" }} +{{ $key | quote }}: {{ $value | b64enc | quote }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateSecretEnvVars.tpl b/charts/helm-apps/templates/fl-snippets/_generateSecretEnvVars.tpl new file mode 100644 index 0000000..9b4399f --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateSecretEnvVars.tpl @@ -0,0 +1,3 @@ +{{- define "fl.generateSecretEnvVars" }} +{{- include "fl.generateSecretData" . }} +{{- end }} diff --git a/charts/helm-apps/templates/fl-snippets/_generateSelectorLabels.tpl b/charts/helm-apps/templates/fl-snippets/_generateSelectorLabels.tpl new file mode 100644 index 0000000..23971bc --- /dev/null +++ b/charts/helm-apps/templates/fl-snippets/_generateSelectorLabels.tpl @@ -0,0 +1,6 @@ +{{- define "fl.generateSelectorLabels" }} + {{- $ := index . 0 }} + {{- $relativeScope := index . 1 }} + {{- $appName := index . 2 }} +app: {{ $appName | quote }} +{{- end }} diff --git a/charts/helm-apps/values.yaml b/charts/helm-apps/values.yaml new file mode 100644 index 0000000..d4ca941 --- /dev/null +++ b/charts/helm-apps/values.yaml @@ -0,0 +1 @@ +enabled: true diff --git a/cr.yaml b/cr.yaml new file mode 100644 index 0000000..7b97ebe --- /dev/null +++ b/cr.yaml @@ -0,0 +1 @@ +skip-existing: true diff --git a/docs/example/.gitlab-ci.yml b/docs/example/.gitlab-ci.yml new file mode 100644 index 0000000..e7d056d --- /dev/null +++ b/docs/example/.gitlab-ci.yml @@ -0,0 +1,14 @@ +stages: +- validate +- publish-charts + + +default: + tags: + - werf + +before_script: +- set -eo pipefail +- type trdl && source $(trdl use werf ${WERF_VERSION:-1.2 ea}) +- type werf && source $(werf ci-env gitlab --as-file) +- werf helm repo add --force-update helm-apps https://flant.github.io/helm-apps diff --git a/docs/example/.helm/Chart.yaml b/docs/example/.helm/Chart.yaml new file mode 100644 index 0000000..8bccc8b --- /dev/null +++ b/docs/example/.helm/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: app +version: 1.0.0 +dependencies: +- name: helm-apps + version: ~1 + repository: "@helm-apps diff --git a/docs/example/.helm/templates/init-flant-apps-library.yaml b/docs/example/.helm/templates/init-flant-apps-library.yaml new file mode 100644 index 0000000..3f3c083 --- /dev/null +++ b/docs/example/.helm/templates/init-flant-apps-library.yaml @@ -0,0 +1,2 @@ +{{- /* Подключаем библиотеку */}} +{{- include "apps-utils.init-library" $ }} diff --git a/docs/example/.helm/values.yaml b/docs/example/.helm/values.yaml new file mode 100644 index 0000000..2d1c6d2 --- /dev/null +++ b/docs/example/.helm/values.yaml @@ -0,0 +1,123 @@ +global: + ## Альтернатива ограниченным yaml-алиасам Helm'а. Даёт возможность не дублировать одну и ту же конфигурацию много раз. + # + # Здесь, в "global._includes", объявляются блоки конфигурации, которые потом можно использовать в любых values-файлах. + # Пример подтягивания этих блоков конфигурации в репозитории приложения: + # ----------------------------------------------------------------------------------------------- + # .helm/values.yaml: + # ----------------------------------------------------------------------------------------------- + # apps-cronjobs: + # cronjob-1: + # _include: ["apps-cronjobs-defaultCronJob"] + # backoffLimit: 1 + # ----------------------------------------------------------------------------------------------- + # + # В примере выше конфигурация из include-блока "apps-cronjobs-defaultCronJob" развернётся на уровне + # apps-cronjobs.cronjob-1, а потом поверх развернувшейся конфигурации применится параметр "backoffLimit: 1", + # при необходимости перезаписав параметр "backoffLimit" из include-блока. + # + # Подробнее: https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flexpandincludesinvalues-function + _includes: + apps-defaults: + enabled: false + apps-default-library-app: + _include: ["apps-defaults"] + # CLIENT: ask if this is ok for a defaul + imagePullSecrets: | + - name: registrysecret + ## Конфигурация по умолчанию для CronJob в целом. + apps-cronjobs-defaultCronJob: + _include: ["apps-default-library-app"] + concurrencyPolicy: "Forbid" + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + backoffLimit: 0 + priorityClassName: + prod: "production-high" + restartPolicy: "Never" + startingDeadlineSeconds: 60 + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + apps-secrets-defaultSecret: + _include: ["apps-defaults"] + + apps-ingresses-defaultIngress: + _include: ["apps-defaults"] + class: "nginx" + + apps-jobs-defaultJob: + _include: ["apps-default-library-app"] + backoffLimit: 0 + priorityClassName: + prod: "production-high" + restartPolicy: "Never" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + apps-stateful-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + terminationGracePeriodSeconds: + _default: 30 + prod: 60 + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{ include "fl.generateSelectorLabels" (list $ . .name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + priorityClassName: + prod: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "15%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + headless: true + + apps-stateless-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + strategy: + _default: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 50% + type: RollingUpdate + prod: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 25% + type: RollingUpdate + priorityClassName: + prod: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "15%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + horizontalPodAutoscaler: + enabled: false + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + headless: true + + apps-configmaps-defaultConfigmap: + _include: ["apps-defaults"] diff --git a/docs/example/werf.yaml b/docs/example/werf.yaml new file mode 100644 index 0000000..a9f012e --- /dev/null +++ b/docs/example/werf.yaml @@ -0,0 +1,2 @@ +project: app +configVersion: 1 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..85fa261 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,71 @@ +# Инструкции по использованию хелперов + +* Любой параметр хелпера может иметь разные значения для разных окружений: + ```yaml + revisionHistoryLimit: 3 # Для всех окружений значение будет одно. + + revisionHistoryLimit: + _default: 3 # То же самое, что и выше. + + revisionHistoryLimit: + _default: 3 # Значение по умолчанию для всех окружений. + some_other_env: 5 # Но для этого окружения значение другое. + ``` + [Подробнее здесь](https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flvalue-function). + +* Любой параметр хелпера можно шаблонизировать: + ```yaml + apps-stateless: + app-1: + service: + name: "{{ $.CurrentApp.name | trim 15 }}" + ``` + [Подробнее здесь](https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flvalue-function). + +* Все компоненты могут включаться/выключаться ключом `enabled`: + ```yaml + apps-stateless: + app-1: + service: + enabled: true + ... + horizontalPodAutoscaler: + enabled: true + ... + ``` +* Можно создать несколько ресурсов одного типа (если в документации к параметру это допускается): + ```yaml + # Содержит список приложений. Можно указывать несколько. + apps-stateless: + app-1: # Создать первое приложение. + ... + app-2: # Создать второе приложение. + ... + ``` +* Структура `values.yaml` повторяет структуру Kubernetes-ресурсов насколько возможно, в том числе нейминг старался сохранять тем же. Т. е. если вам кажется, что `command` в `values.yaml` — это то, что пробросится в `command` контейнера, то скорее всего так и есть. +* Если в `values.yaml` [multiline-строкой](https://lzone.de/cheat-sheet/YAML#yaml-heredoc-multiline-strings) пробрасывается список или словарь (`command: | ...`), то это значит, что этот блок вставится в манифест Kubernetes-ресурса *без преобразований*. Такие параметры обязательно пробрасывать именно строкой, содержащей в себе список или словарь. Также для подобных параметров поддерживается только YAML-стиль оформления списков и словарей, но не JSON-стиль. Пример: + ```yaml + # Верно: + command: | + - echo + + # Неверно: + command: + - echo + command: ["echo"] + command: | + ["echo"] + + # Верно: + annotations: | + key: value + + # Неверно: + annotations: + key: value + annotations: {"key": "value"} + annotations: | + {"key": "value"} + ``` +* Логика отделена от конфигурации: вся логика находится в шаблонах хелпера ([пример](../charts/helm-apps/templates/)), вся конфигурация по умолчанию находится в `values.yaml` в секции global._includes, а вся актуальная конфигурация приложения находится в `.helm/values.yaml` в репозитории приложения. В репозитории приложения обычно шаблонов быть не должно(кроме шаблона подключения библиотеки), только конфигурация в `.helm/values.yaml` и `.helm/secret-values.yaml`. +* Дублирование конфигурации минимизировано за счет специального хелпера, реализующего более мощную альтернативу YAML-алисам. Подробнее смотрите в файлах `values.yaml` . diff --git a/tests/.helm/Chart.lock b/tests/.helm/Chart.lock new file mode 100644 index 0000000..7e65e78 --- /dev/null +++ b/tests/.helm/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: helm-apps + repository: file://../../charts/helm-apps/ + version: 1.0.0 +digest: sha256:98bbb56d3a6ef88c8ad6e1828d171d099edfee1d6dcb2d75a318d0f06c9dd1a2 +generated: "2022-08-28T03:23:29.999326+03:00" diff --git a/tests/.helm/Chart.yaml b/tests/.helm/Chart.yaml new file mode 100644 index 0000000..904e553 --- /dev/null +++ b/tests/.helm/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: tests +version: 1.0.0 +maintainers: + - name: alvnukov + email: alexandr.vnukov@flant.com + url: https://github.com/alvnukov +dependencies: +- name: helm-apps + repository: "file://../../charts/helm-apps/" + version: ~1 diff --git a/tests/.helm/templates/init-flant-apps-library.yaml b/tests/.helm/templates/init-flant-apps-library.yaml new file mode 100644 index 0000000..8fe4a60 --- /dev/null +++ b/tests/.helm/templates/init-flant-apps-library.yaml @@ -0,0 +1,2 @@ +{{- /* Подключаем библиотеку */}} +{{- include "apps-utils.init-library" $ }} \ No newline at end of file diff --git a/tests/.helm/values.yaml b/tests/.helm/values.yaml new file mode 100644 index 0000000..afe851c --- /dev/null +++ b/tests/.helm/values.yaml @@ -0,0 +1,1914 @@ +global: + ci_url: example.com + ## Альтернатива ограниченным yaml-алиасам Helm'а. Даёт возможность не дублировать одну и ту же конфигурацию много раз. + # + # Здесь, в "global._includes", объявляются блоки конфигурации, которые потом можно использовать в любых values-файлах. + # Пример подтягивания этих блоков конфигурации в репозитории приложения: + # ----------------------------------------------------------------------------------------------- + # .helm/values.yaml: + # ----------------------------------------------------------------------------------------------- + # apps-cronjobs: + # cronjob-1: + # _include: ["apps-cronjobs-defaultCronJob"] + # backoffLimit: 1 + # ----------------------------------------------------------------------------------------------- + # + # В примере выше конфигурация из include-блока "apps-cronjobs-defaultCronJob" развернётся на уровне + # apps-cronjobs.cronjob-1, а потом поверх развернувшейся конфигурации применится параметр "backoffLimit: 1", + # при необходимости перезаписав параметр "backoffLimit" из include-блока. + # + # Подробнее: https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flexpandincludesinvalues-function + _includes: + apps-defaults: + enabled: false + apps-default-library-app: + _include: ["apps-defaults"] + # CLIENT: ask if this is ok for a defaul + imagePullSecrets: | + - name: registrysecret + ## Конфигурация по умолчанию для CronJob в целом. + apps-cronjobs-defaultCronJob: + _include: ["apps-default-library-app"] + concurrencyPolicy: "Forbid" + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + backoffLimit: 0 + priorityClassName: + prod: "production-high" + restartPolicy: "Never" + startingDeadlineSeconds: 60 + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + apps-secrets-defaultSecret: + _include: ["apps-defaults"] + + apps-ingresses-defaultIngress: + _include: ["apps-defaults"] + class: "nginx" + + apps-jobs-defaultJob: + _include: ["apps-default-library-app"] + backoffLimit: 0 + priorityClassName: + prod: "production-high" + restartPolicy: "Never" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + + apps-stateful-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + terminationGracePeriodSeconds: + _default: 30 + prod: 60 + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{ include "fl.generateSelectorLabels" (list $ . .name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + priorityClassName: + prod: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "15%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + headless: true + + apps-stateless-defaultApp: + _include: ["apps-default-library-app"] + revisionHistoryLimit: 3 + strategy: + _default: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 50% + type: RollingUpdate + prod: | + rollingUpdate: + maxSurge: 20% + maxUnavailable: 25% + type: RollingUpdate + priorityClassName: + prod: "production-medium" + podDisruptionBudget: + enabled: true + maxUnavailable: "15%" + verticalPodAutoscaler: + enabled: true + updateMode: "Off" + resourcePolicy: | + {} + horizontalPodAutoscaler: + enabled: false + service: + enabled: false + name: "{{ $.CurrentApp.name }}" + + apps-configmaps-defaultConfigmap: + _include: ["apps-defaults"] + + +## Имя чарта. Ниже перечисляются ConfigMaps для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-configmaps: + ## Имя первого ConfigMap'а. + configmap-1: + _include: ["apps-configmaps-defaultConfigmap"] + ## Включить/выключить создание ConfigMap'а. + # enabled: false + ## Содержание ConfigMap'а. Файлы конфигурации, пробрасываемые в контейнеры. + # Пробрасывается как есть в ConfigMap.data. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#configmap-v1-core + data: | + nginx.conf2: | + configline1 + configline2 + something.conf: | + configline1 + ## Содержание ConfigMap'а. Несекретные переменные окружения, пробрасываемые в контейнеры. + # По итогу пробросится в ConfigMap.data. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + envVars: + TEST1: "val1" + TEST2: "val2" + +## Имя чарта. Ниже перечисляются CronJob'ы для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-cronjobs: + ## Имя первой CronJob. Используется в основном для генерации имён ресурсов. + cronjob-1: + _include: ["apps-cronjobs-defaultCronJob"] + ## Включить/выключить CronJob и создаваемые с ней ресурсы. + # Здесь включено напрямую через Values только для того, чтобы работал линт перед публикацией чартов. + # В репозитории приложения этой строки быть не должно, вместо этого CronJob'а включается через GitLab CI. + ## Дополнительные аннотации для CronJob/Job/Pod. + # Пробрасываются как есть в CronJob.metadata.annotations, CronJob.spec.jobTemplate.metadata.annotations + # и CronJob.spec.jobTemplate.spec.template.metadata.annotations. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: | + testAnnotation: "testVal" + ## Расписание запуска CronJob'ы в cron-формате. + # https://en.wikipedia.org/wiki/Cron#Overview + schedule: "0 * * * *" + ## Разрешение параллельных запусков Job'ы. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#cronjobspec-v1beta1-batch + concurrencyPolicy: "Allow" + ## Сколько успешно завершенных Jobs и их Pod'ов хранить. + # Можно уменьшить, чтобы не захламлять неймспейс старыми Pod'ами. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#cronjobspec-v1beta1-batch + successfulJobsHistoryLimit: 1 + ## Сколько неудачно завершенных Jobs и их Pod'ов хранить. + # Можно уменьшить, чтобы не захламлять неймспейс старыми Pod'ами. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#cronjobspec-v1beta1-batch + failedJobsHistoryLimit: 1 + ## Пометить Job как Failed если она не запустилась в течение указанного количества секунд + # после запланированного времени запуска. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#cronjobspec-v1beta1-batch + startingDeadlineSeconds: 60 + ## Лимит на попытки перезапуска CronJob'ы, если она завершается с ошибкой. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#jobspec-v1-batch + backoffLimit: 0 + ## Ограничение времени выполнения CronJob'ы, после чего она будет принудительно завершена. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#jobspec-v1-batch + activeDeadlineSeconds: 1800 + ## Имена Secret'ов, в которых хранятся credentials для скачивания Docker-образов из Docker registry. + # Пробрасываются как есть в CronJob.spec.jobTemplate.spec.template.spec.imagePullSecrets. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + imagePullSecrets: | + - name: registrysecret + ## Правила для шедулинга Pod'ов. + # Пробрасываются как есть в CronJob.spec.jobTemplate.spec.template.spec.affinity. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#affinity-v1-core + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- include "fl.generateLabels" (list $ . $.CurrentApp.name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + ## Разрешить шедулинг Pod'ов на tainted Nodes. + # Пробрасываются как есть в CronJob.spec.jobTemplate.spec.template.spec.tolerations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#toleration-v1-core + tolerations: | + - key: something + operator: Equal + value: somethingelse + ## Приоритет Pod'ов при шедулинге. + # При недостатке ресурсов на ноде или в кластере могут удалиться/пересоздасться Pod'ы с более низким приоритетом. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + priorityClassName: "production-medium" + ## Пытаться ли перезапускать контейнеры, если они не поднимутся. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + restartPolicy: "Never" + ## Volumes, которые можно примонтировать в контейнеры CronJob'ы. + # Монтирование происходит ниже, в (init)containers.volumeMounts. + # Пробрасываются как есть в CronJob.spec.jobTemplate.spec.template.spec.volumes. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volume-v1-core + volumes: | + - name: cache-volume + emptyDir: {} + + ## Список контейнеров CronJob'ы. Можно указать несколько. + containers: + ## Имя контейнера. + container-1: + ## Включить/выключить контейнер. + enabled: true + ## Настройка имени и тега Docker-образа. + # Использовать образ, собранный с Werf (с динамическим тегом): + # --------------------------------------------------------------------------------------- + # image: + # name: "main" + # generateSignatureBasedTag: true + # --------------------------------------------------------------------------------------- + # Использовать обычный образ со статическим тегом: + # --------------------------------------------------------------------------------------- + # image: + # name: "alpine" + # staticTag: "3" + # --------------------------------------------------------------------------------------- + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerimagequoted-template + image: + name: "alpine" + staticTag: "3" + ## Команда, запускаемая при старте контейнера. + # Пробрасывается как есть в CronJob.spec.jobTemplate.spec.template.spec.containers.command. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + command: | + - tail + ## Аргументы для команды, запускаемой при старте контейнера. + # Пробрасываются как есть в CronJob.spec.jobTemplate.spec.template.spec.containers.args. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + args: | + - -f + - /dev/null + ## Несекретные переменные окружения контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + envVars: + TEST1: "val1" + TEST2: "val2" + ## Секретные переменные окружения контейнера. + # Автоматически создадут Secret с указанными переменными окружения и примонтируют и пробросят их в контейнер. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgenerateconfigmapenvvars-template + secretEnvVars: + TEST1: "val1" + TEST2: "val2" + ## Пробросить переменные окружения из ConfigMap или Secret в контейнер. + # Пробрасывается как есть в CronJob.spec.jobTemplate.spec.template.spec.containers.envFrom. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envfromsource-v1-core + envFrom: | + - configMapRef: + name: env-configmap + ## Ресурсы контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerresources-template + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + ## Хуки, отрабатывающие при запуске или остановке контейнера. + # Если приложение не может корректно завершить работу само при получении STOPSIGNAL (обычно TERM), + # то здесь можно указать команду, которая gracefully завершит ваше приложение. + # Пробрасывается как есть в CronJob.spec.jobTemplate.spec.template.spec.containers.lifecycle. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#lifecycle-v1-core + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + ## Примонтировать volume в контейнер. + # Перед монтированием volume его надо добавить в параметр "volumes:" выше. + # Пробрасывается как есть в CronJob.spec.jobTemplate.spec.template.spec.containers.volumeMounts. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + volumeMounts: | + - mountPath: /cache + name: cache-volume + ## Несекретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут ConfigMaps и примонтируют их в контейнер по указанному пути. + configFiles: + ## Имя первого конфигурационного файла. + nginx.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/nginx/nginx.conf + ## Содержание конфигурационного файла. + # Пробрасывается как есть в ConfigMap.data.<Имя файла>. + content: | + configline1 + configline2 + ## Секретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут Secrets и примонтируют их в контейнер по указанному пути. + secretConfigFiles: + ## Имя первого конфигурационного файла. + secret.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/secret.conf + # Автоматически сконвертируется в base64 и пробросится в Secret.data.<Имя файла>. + content: | + secretline1 + secretline2 + + ## Список инит-контейнеров CronJob'ы. Можно указать несколько. + # Конфигурация доступных параметров идентична конфигурации containers, описанной выше ^^ + initContainers: + init-container-1: + enabled: true + image: + name: "alpine" + staticTag: "3" + command: | + - tail + args: | + - -f + - /dev/null + envVars: + TEST1: "val1" + secretEnvVars: + TEST1: "val1" + envFrom: | + - configMapRef: + name: env-configmap + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + volumeMounts: | + - mountPath: /cache + name: cache-volume + configFiles: + nginx.conf: + mountPath: /etc/nginx/nginx.conf + content: | + configline1 + secretConfigFiles: + secret.conf: + mountPath: /etc/secret.conf + content: | + secretline1 + + ## Сбор статистики по потреблению ресурсов контейнеров и (опционально) автоматическое выставление resources. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler + verticalPodAutoscaler: + ## Включить/выключить verticalPodAutoscaler. + enabled: true + ## Выставлять ли resources автоматически, и в каких случаях. + # Для сбора статистики достаточно "updateMode: Off". + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#podupdatepolicy_v1_autoscalingk8sio + updateMode: "Initial" + ## Настройка автовыставления resources для каждого контейнера по отдельности. + # Пробрасывается как есть в VerticalPodAutoscaler.spec.resourcePolicy. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#verticalpodautoscalerspec_v1_autoscalingk8sio + resourcePolicy: | + containerPolicies: + - containerName: "some-container" + mode: "Off" + +## Имя чарта. Ниже перечисляются Ingress'ы для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-ingresses: + ## Имя первого Ingress. + ingress-1: + _include: ["apps-ingresses-defaultIngress"] + ## Включить/выключить Ingress. + # В репозитории приложения этой строки быть не должно, вместо этого Ingress включается через GitLab CI. + # enabled: false + ## Дополнительные аннотации для Ingress. + # Пробрасываются как есть в Ingress.metadata.annotations. + # https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations + annotations: | + nginx.ingress.kubernetes.io/auth-url: example.org + ## Ingress-класс. + # Пробрасывается в аннотацию "kubernetes.io/ingress.class". + # https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + class: "nginx" + ## Домен Ingress'а. + # Пробрасывается как есть в Ingress.spec.rules.host. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#ingressrule-v1-networking-k8s-io + host: "example.org" + ## Сопоставление HTTP-путей и Service'ов. + # Пробрасывается как есть в Ingress.spec.rules.http.paths. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#httpingresspath-v1-networking-k8s-io + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + ## Добавляет обработку tls а также включает создание Certificate + # Делает магию Certificate без параметров. они берутся из других полей + tls: + enabled: true + ## Переопределяет Ingress.spec.tls.secretName + # Убирает автоматическое создание Certificate + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#ingresstls-v1-networking-k8s-io + secret_name: "test" + + ingress-2: + _include: ["apps-ingresses-defaultIngress"] + class: "nginx" + host: "example.org" + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + tls: + enabled: false + ingress-3: + _include: ["apps-ingresses-defaultIngress"] + class: "nginx" + host: "example.org" + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + tls: + enabled: true + ingress-4: + _include: ["apps-ingresses-defaultIngress"] + class: "nginx" + ingressClassName: "nginx" + host: "example.org" + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + # Приложение из примера в документации + nginx: + _include: ["apps-ingresses-defaultIngress"] + host: '{{ $.Values.global.ci_url }}' + paths: | + - path: / + pathType: Prefix + backend: + service: + name: nginx + port: + number: 80 + tls: + enabled: true + +## Имя чарта. Ниже перечисляются Job'ы для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-jobs: + ## Имя первой Job. Используется в основном для генерации имён ресурсов. + job-1: + _include: ["apps-jobs-defaultJob"] + ## Включить/выключить Job и создаваемые с ней ресурсы. + # В репозитории приложения этой строки быть не должно, вместо этого Job'а включается через GitLab CI. + # enabled: false + randomName: true + # alwaysRestart добавляет перременную в FL_APP_ALWAYS_RESTART контейнеры приложения с + # рандомным значением + alwaysRestart: true + werfWeight: + _default: -10 + production: 0 + ## Дополнительные аннотации для Job/Pod. + # Пробрасываются как есть в Job.metadata.annotations и Job.spec.template.metadata.annotations. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: | + testAnnotation: "testVal" + ## Лимит на попытки перезапуска Job'ы, если она завершается с ошибкой. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#jobspec-v1-batch + backoffLimit: 0 + ## Ограничение времени выполнения Job'ы, после чего она будет принудительно завершена. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#jobspec-v1-batch + activeDeadlineSeconds: 1800 + ## Имена Secret'ов, в которых хранятся credentials для скачивания Docker-образов из Docker registry. + # Пробрасываются как есть в Job.spec.template.spec.imagePullSecrets. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + imagePullSecrets: | + - name: registrysecret + ## Правила для шедулинга Pod'ов. + # Пробрасываются как есть в Job.spec.template.spec.affinity. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#affinity-v1-core + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- include "fl.generateLabels" (list $ . $.CurrentApp.name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + ## Разрешить шедулинг Pod'ов на tainted Nodes. + # Пробрасываются как есть в Job.spec.template.spec.tolerations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#toleration-v1-core + tolerations: | + - key: something + operator: Equal + value: somethingelse + ## Приоритет Pod'ов при шедулинге. + # При недостатке ресурсов на ноде или в кластере могут удалиться/пересоздасться Pod'ы с более низким приоритетом. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + priorityClassName: "production-medium" + ## Пытаться ли перезапускать контейнеры, если они не поднимутся. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + restartPolicy: "Never" + ## Volumes, которые можно примонтировать в контейнеры Job'ы. + # Монтирование происходит ниже, в (init)containers.volumeMounts. + # Пробрасываются как есть в Job.spec.template.spec.volumes. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volume-v1-core + volumes: | + - name: cache-volume + emptyDir: {} + + ## Список контейнеров Job'ы. Можно указать несколько. + containers: + ## Имя контейнера. + container-1: + ## Включить/выключить контейнер. Контейнеры включены по-умолчанию + # enabled: true + ## Настройка имени и тега Docker-образа. + # Использовать образ, собранный с Werf (с динамическим тегом): + # --------------------------------------------------------------------------------------- + # image: + # name: "main" + # generateSignatureBasedTag: true + # --------------------------------------------------------------------------------------- + # Использовать обычный образ со статическим тегом: + # --------------------------------------------------------------------------------------- + # image: + # name: "alpine" + # staticTag: "3" + # --------------------------------------------------------------------------------------- + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerimagequoted-template + image: + name: "alpine" + staticTag: "3" + ## Команда, запускаемая при старте контейнера. + # Пробрасывается как есть в Job.spec.template.spec.containers.command. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + command: | + - tail + ## Аргументы для команды, запускаемой при старте контейнера. + # Пробрасываются как есть в Job.spec.template.spec.containers.args. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + args: | + - -f + - /dev/null + ## Несекретные переменные окружения контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + envVars: + TEST1: "val1" + TEST2: "val2" + ## Секретные переменные окружения контейнера. + # Автоматически создадут Secret с указанными переменными окружения и примонтируют и пробросят их в контейнер. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgenerateconfigmapenvvars-template + secretEnvVars: + TEST1: "val1" + TEST2: "val2" + ## Пробросить переменные окружения из ConfigMap или Secret в контейнер. + # Пробрасывается как есть в Job.spec.template.spec.containers.envFrom. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envfromsource-v1-core + envFrom: | + - configMapRef: + name: env-configmap + ## Ресурсы контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerresources-template + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + ## Хуки, отрабатывающие при запуске или остановке контейнера. + # Если приложение не может корректно завершить работу само при получении STOPSIGNAL (обычно TERM), + # то здесь можно указать команду, которая gracefully завершит ваше приложение. + # Пробрасывается как есть в Job.spec.template.spec.containers.lifecycle. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#lifecycle-v1-core + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + ## Примонтировать volume в контейнер. + # Перед монтированием volume его надо добавить в параметр "volumes:" выше. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + volumeMounts: | + - mountPath: /cache + name: cache-volume + ## Несекретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут ConfigMaps и примонтируют их в контейнер по указанному пути. + configFiles: + ## Имя первого конфигурационного файла. + nginx.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/nginx/nginx.conf + ## Содержание конфигурационного файла. + # Пробрасывается как есть в ConfigMap.data.<Имя файла>. + content: | + configline1 + configline2 + ## Секретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут Secrets и примонтируют их в контейнер по указанному пути. + secretConfigFiles: + ## Имя первого конфигурационного файла. + secret.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/secret.conf + # Автоматически сконвертируется в base64 и пробросится в Secret.data.<Имя файла>. + content: | + secretline1 + secretline2 + + ## Список инит-контейнеров Job'ы. Можно указать несколько. + # Конфигурация доступных параметров идентична конфигурации containers, описанной выше ^^ + initContainers: + init-container-1: + # enabled: true + image: + name: "alpine" + staticTag: "3" + command: | + - tail + args: | + - -f + - /dev/null + envVars: + TEST1: "val1" + secretEnvVars: + TEST1: "val1" + envFrom: | + - configMapRef: + name: env-configmap + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + volumeMounts: | + - mountPath: /cache + name: cache-volume + configFiles: + nginx.conf: + mountPath: /etc/nginx/nginx.conf + content: | + configline1 + secretConfigFiles: + secret.conf: + mountPath: /etc/secret.conf + content: | + secretline1 + + ## Сбор статистики по потреблению ресурсов контейнеров и (опционально) автоматическое выставление resources. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler + verticalPodAutoscaler: + ## Включить/выключить verticalPodAutoscaler. + enabled: true + ## Выставлять ли resources автоматически, и в каких случаях. + # Для сбора статистики достаточно "updateMode: Off". + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#podupdatepolicy_v1_autoscalingk8sio + updateMode: "Initial" + ## Настройка автовыставления resources для каждого контейнера по отдельности. + # Пробрасывается как есть в VerticalPodAutoscaler.spec.resourcePolicy. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#verticalpodautoscalerspec_v1_autoscalingk8sio + resourcePolicy: | + containerPolicies: + - containerName: "some-container" + mode: "Off" + +## Имя чарта. Ниже перечисляются Secrets для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-secrets: + ## Имя первого Secret'а. + secret-1: + _include: ["apps-secrets-defaultSecret"] + ## Включить/выключить создание Secret'а. + ## Тип Secret'а. + # Если не указано, "Opaque" выставится автоматически (обычно это то, что нужно). + # https://kubernetes.io/docs/concepts/configuration/secret/#secret-types + type: "Opaque" + ## Содержание Secret'а. Обычно файлы конфигурации, пробрасываемые в контейнеры. + # Автоматически сконвертируется в base64 и пробросится в Secret.data. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#secret-v1-core + data: + secret.conf: | + plainTextLine1 + plainTextLine2 + secret2.conf: | + plainTextLine1 + ## Содержание Secret'а. Секретные переменные окружения, пробрасываемые в контейнеры. + # По итогу пробросится в Secret.data. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + envVars: + TEST1: "val1" + TEST2: "val2" + +## Имя чарта. Ниже перечисляются приложения для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-stateful: + ## Имя первого приложения. Используется в основном для генерации имён ресурсов. + app-1: + _include: ["apps-stateful-defaultApp"] + # werfWeight задает порядок выкатки приложений добавляя аннотацию werf.io/weight: "NUM" + # аннотация добавляется на все автоматически связанные с приложением ресурсы + # подробнее: https://ru.werf.io/documentation/v1.2/reference/deploy_annotations.html#resource-weight + werfWeight: -30 + ## Включить/выключить приложение. + # В репозитории приложения этой строки быть не должно, вместо этого приложение включается через GitLab CI. + # enabled: false + ## Дополнительные аннотации для Deployment/Pod. + # Пробрасываются как есть в Deployment.metadata.annotations и Deployment.spec.template.metadata.annotations. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: | + testAnnotation: "testVal" + ## Дополнительные лейблы для всех ресурсов, создаваемых чартом. + # По умолчанию уже генерируется стандартный набор лейблов, поэтому указывать дополнительные обычно не требуется. + # Пробрасываются как есть в metadata.labels всех ресурсов. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + labels: | + testLabel: "testVal" + ## Количество реплик Deployment'а. + # Если включен HorizontalPodAutoscaler, то эта настройка не применяется. Вместо неё используйте + # minReplicas и maxReplicas из секции horizontalPodAutoscaler ниже. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentspec-v1-apps + replicas: 2 + ## Лимит количества старых ReplicaSets, остающихся после обновлений Deployment'а. + # Их уменьшение делает "werf cleanup" более эффективным. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentspec-v1-apps + revisionHistoryLimit: 3 + ## Стратегия обновлений Deployment'а. + # Пробрасывается как есть в Deployment.spec.strategy. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentstrategy-v1-apps + strategy: | + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + ## Имена Secret'ов, в которых хранятся credentials для скачивания Docker-образов из Docker registry. + # Пробрасываются как есть в Deployment.spec.template.spec.imagePullSecrets. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + imagePullSecrets: | + - name: registrysecret + ## Лимит времени на завершение работы приложения при остановке Pod'а. + # После истечения лимита приложению посылается KILL-сигнал. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + terminationGracePeriodSeconds: 30 + ## Правила для шедулинга Pod'ов. + # Пробрасываются как есть в Deployment.spec.template.spec.affinity. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#affinity-v1-core + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{ include "fl.generateLabels" (list $ . $.CurrentApp.name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + ## Чтобы Pod зашедулился на Node, у Node должны быть все перечисленные лейблы. + # Пробрасываются как есть в Deployment.spec.template.spec.nodeSelector. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + nodeSelector: | + label1: value1 + label2: value2 + ## Разрешить шедулинг Pod'ов на tainted Nodes. + # Пробрасываются как есть в Deployment.spec.template.spec.tolerations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#toleration-v1-core + tolerations: | + - key: something + operator: Equal + value: somethingelse + ## hostNetwork + # https://v1-20.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podspec-v1-core + hostNetwork: true + ## dnsPolicy + # https://v1-20.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podspec-v1-core + dnsPolicy: ClusterFirstWithHostNet + ## Приоритет Pod'ов при шедулинге. + # При недостатке ресурсов на ноде или в кластере могут удалиться/пересоздасться Pod'ы с более низким приоритетом. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + priorityClassName: "production-medium" + ## Volumes, которые можно примонтировать в контейнеры приложения. + # Монтирование происходит ниже, в (init)containers.volumeMounts. + # Пробрасываются как есть в Deployment.spec.template.spec.volumes. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volume-v1-core + volumes: | + - name: cache-volume + emptyDir: {} + persistantVolumes: + data2: + mountPath: /data2 + class: localStorage + size: 1Gi + + ## Список контейнеров приложения. Можно указать несколько. + containers: + ## Имя контейнера. + container-1: + ## Включить/выключить контейнер. + ## Настройка имени и тега Docker-образа. + # Использовать образ, собранный с Werf (с динамическим тегом): + # --------------------------------------------------------------------------------------- + # image: + # name: "backend" + # --------------------------------------------------------------------------------------- + # Использовать обычный образ со статическим тегом: + # --------------------------------------------------------------------------------------- + # image: + # name: "alpine" + # staticTag: "3" + # --------------------------------------------------------------------------------------- + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerimagequoted-template + image: + name: "alpine" + staticTag: "3" + ## Команда, запускаемая при старте контейнера. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.command. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + command: | + - tail + ## Аргументы для команды, запускаемой при старте контейнера. + # Пробрасываются как есть в Deployment.spec.template.spec.containers.args. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + args: | + - -f + - /dev/null + ## Открытые порты контейнера. + # Пробрасываются как есть в Deployment.spec.template.spec.containers.ports. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#containerport-v1-core + ports: | + - name: http + containerPort: 80 + ## Несекретные переменные окружения контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + envs: + TEST1: "val1" + TEST2: "val2" + ## Секретные переменные окружения контейнера. + # Автоматически создадут Secret с указанными переменными окружения и примонтируют и пробросят их в контейнер. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgenerateconfigmapenvvars-template + secretEnvVars: + TEST1: "val1" + TEST2: "val2" + ## Пробросить переменные окружения из ConfigMap или Secret в контейнер. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.envFrom. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envfromsource-v1-core + envFrom: | + - configMapRef: + name: env-configmap + ## Ресурсы контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerresources-template + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + ## Liveness-проба. + # Контейнер перезагружается, если она перестаёт работать. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.livenessProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + livenessProbe: | + httpGet: + path: /liveness + port: 80 + ## Readiness-проба. + # На контейнер перестаёт поступать трафик через Service/Ingress, если она перестаёт работать. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.readinessProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + readinessProbe: | + httpGet: + path: /readiness + port: 80 + ## Startup-проба. + # Startup-проба начинает работать при старте контейнера. Если она единожды отработает успешно, + # то она перестает работать, а вместе неё включаются в работу Liveness и Readiness-пробы. Но + # если Startup-проба при старте так и не отработает успешно, то контейнер будет перезагружен. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.startupProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + startupProbe: | + httpGet: + path: /startup + port: 80 + ## Хуки, отрабатывающие при запуске или остановке контейнера. + # Если приложение не может корректно завершить работу само при получении STOPSIGNAL (обычно TERM), + # то здесь можно указать команду, которая gracefully завершит ваше приложение. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.lifecycle. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#lifecycle-v1-core + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + ## Примонтировать volume в контейнер. + # Перед монтированием volume его надо добавить в параметр "volumes:" выше. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.volumeMounts. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + volumeMounts: | + - mountPath: /cache + name: cache-volume + ## Несекретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут ConfigMaps и примонтируют их в контейнер по указанному пути. + configFiles: + ## Имя первого конфигурационного файла. + nginx.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/nginx/nginx.conf + ## Содержание конфигурационного файла. + # Пробрасывается как есть в ConfigMap.data.<Имя файла>. + content: | + configline1 + configline2 + nginx.conf2: + mountPath: /etc/nginx/nginx2.conf + ## указывает на имя cm с которой линковать файл в контейнере. + name: configmap-1 + ## Секретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут Secrets и примонтируют их в контейнер по указанному пути. + secretConfigFiles: + ## Имя первого конфигурационного файла. + secret.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/secret.conf + # Автоматически сконвертируется в base64 и пробросится в Secret.data.<Имя файла>. + content: | + secretline1 + secretline2 + persistantVolumes: + data2: + mountPath: /data2 + class: localStorage + size: 1Gi + + ## Список инит-контейнеров приложения. Можно указать несколько. + # Конфигурация доступных параметров идентична конфигурации containers, описанная выше ^^ + initContainers: + init-container-1: + image: + name: "alpine" + staticTag: "3" + command: | + - tail + args: | + - -f + - /dev/null + envVars: + TEST1: "val1" + secretEnvVars: + TEST1: "val1" + envFrom: | + - configMapRef: + name: env-configmap + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + volumeMounts: | + - mountPath: /cache + name: cache-volume + + configFiles: + nginx.conf: + mountPath: /etc/nginx/nginx.conf + content: | + configline1 + secretConfigFiles: + secret.conf: + mountPath: /etc/secret.conf + content: | + secretline1 + + ## Service приложения. + # Для связи других приложений и клиентов с этим приложением. + # https://kubernetes.io/docs/concepts/services-networking/service + service: + ## Включить/выключить сервис. + enabled: true + ## DNS-имя сервиса, а также имя самого сервиса. + name: "{{ $.CurrentApp.name }}" + ## Дополнительные аннотации. + # Пробрасываются как есть в Service.metadata.annotations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta + annotations: null + ## Дополнительные лейблы для сервиса. + # По умолчанию уже генерируется стандартный набор лейблов, поэтому указывать дополнительные обычно не требуется. + # Пробрасываются как есть в Service.metadata.labels. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + labels: | + testLabel: "testVal" + ## Headless-сервис (ClusterIP: none) + # Позволяет уменьшить нагрузку на кластер при большом количестве сервисов. + # https://kubernetes.io/docs/concepts/services-networking/service/#headless-services + headless: true + ## Настройка связи Service-портов с Deployment-портами. + # Пробрасывается как есть в Service.spec.ports. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core + ports: | + - name: http + port: 80 + + ## Лимит на удаление подов шедулером. + # Работает только для evict'ов Pod'а. При обновлении/рестарте Deployment'а evict'ов не происходит. + # Поэтому лимиты при обновлении Deployment'а описываются в отдельном ключе `strategy:` выше. + # https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#pod-disruption-budgets + podDisruptionBudget: + ## Включить/выключить podDisruptionBudget. + enabled: true + ## Максимальное количество недоступных (evicted) Pod'ов. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#poddisruptionbudgetspec-v1beta1-policy + maxUnavailable: 1 + + ## Сбор статистики по потреблению ресурсов контейнеров и (опционально) автоматическое выставление resources. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler + verticalPodAutoscaler: + ## Включить/выключить verticalPodAutoscaler. + enabled: true + # Выставлять ли resources автоматически, и в каких случаях. + # Для сбора статистики достаточно "updateMode: Off". + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#podupdatepolicy_v1_autoscalingk8sio + updateMode: "Off" + # Настройка автовыставления resources для каждого контейнера по отдельности. + # Пробрасывается как есть в VerticalPodAutoscaler.spec.resourcePolicy. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#verticalpodautoscalerspec_v1_autoscalingk8sio + resourcePolicy: | + containerPolicies: + - containerName: "some-container" + mode: "Off" + stateful-simple: + _include: ["apps-stateful-defaultApp"] + containers: + main: + image: + name: nginx + +## Имя чарта. Ниже перечисляются приложения для развертывания. +# Указано в .helm/requirements.yaml в репозитории приложения в ключах dependencies.name или dependencies.alias. +# https://helm.sh/docs/topics/charts/#managing-dependencies-with-the-dependencies-field +apps-stateless: + ## Имя первого приложения. Используется в основном для генерации имён ресурсов. + app-1: + _include: ["apps-stateless-defaultApp"] + # werfWeight задает порядок выкатки приложений добавляя аннотацию werf.io/weight: "NUM" + # аннотация добавляется на все автоматически связанные с приложением ресурсы + # подробнее: https://ru.werf.io/documentation/v1.2/reference/deploy_annotations.html#resource-weight + werfWeight: -10 + ## Включить/выключить приложение. + # В репозитории приложения этой строки быть не должно, вместо этого приложение включается через GitLab CI. + # enabled: false + ## Дополнительные аннотации для Deployment/Pod. + # Пробрасываются как есть в Deployment.metadata.annotations и Deployment.spec.template.metadata.annotations. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: | + testAnnotation: "testVal" + ## Дополнительные лейблы для всех ресурсов, создаваемых чартом. + # По умолчанию уже генерируется стандартный набор лейблов, поэтому указывать дополнительные обычно не требуется. + # Пробрасываются как есть в metadata.labels всех ресурсов. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + labels: | + testLabel: "testVal" + ## Количество реплик Deployment'а. + # Если включен HorizontalPodAutoscaler, то эта настройка не применяется. Вместо неё используйте + # minReplicas и maxReplicas из секции horizontalPodAutoscaler ниже. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentspec-v1-apps + replicas: 2 + ## Лимит количества старых ReplicaSets, остающихся после обновлений Deployment'а. + # Их уменьшение делает "werf cleanup" более эффективным. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentspec-v1-apps + revisionHistoryLimit: 3 + ## Стратегия обновлений Deployment'а. + # Пробрасывается как есть в Deployment.spec.strategy. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentstrategy-v1-apps + strategy: | + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + ## Имена Secret'ов, в которых хранятся credentials для скачивания Docker-образов из Docker registry. + # Пробрасываются как есть в Deployment.spec.template.spec.imagePullSecrets. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + imagePullSecrets: | + - name: registrysecret + ## Лимит времени на завершение работы приложения при остановке Pod'а. + # После истечения лимита приложению посылается KILL-сигнал. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + terminationGracePeriodSeconds: 30 + ## Правила для шедулинга Pod'ов. + # Пробрасываются как есть в Deployment.spec.template.spec.affinity. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#affinity-v1-core + affinity: | + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- include "fl.generateLabels" (list $ . $.CurrentApp.name) | nindent 22 }} + topologyKey: kubernetes.io/hostname + weight: 10 + ## Разрешить шедулинг Pod'ов на tainted Nodes. + # Пробрасываются как есть в Deployment.spec.template.spec.tolerations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#toleration-v1-core + tolerations: | + - key: something + operator: Equal + value: somethingelse + ## hostNetwork + # https://v1-20.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podspec-v1-core + hostNetwork: true + ## dnsPolicy + # https://v1-20.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podspec-v1-core + dnsPolicy: ClusterFirstWithHostNet + ## Приоритет Pod'ов при шедулинге. + # При недостатке ресурсов на ноде или в кластере могут удалиться/пересоздасться Pod'ы с более низким приоритетом. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core + priorityClassName: "production-medium" + ## Volumes, которые можно примонтировать в контейнеры приложения. + # Монтирование происходит ниже, в (init)containers.volumeMounts. + # Пробрасываются как есть в Deployment.spec.template.spec.volumes. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volume-v1-core + volumes: | + - name: cache-volume + emptyDir: {} + + ## Список контейнеров приложения. Можно указать несколько. + containers: + ## Имя контейнера. + container-1: + ## Включить/выключить контейнер. + # enabled: true + ## Настройка имени и тега Docker-образа. + # Использовать образ, собранный с Werf (с динамическим тегом): + # --------------------------------------------------------------------------------------- + # image: + # name: "backend" + # generateSignatureBasedTag: true + # --------------------------------------------------------------------------------------- + # Использовать обычный образ со статическим тегом: + # --------------------------------------------------------------------------------------- + # image: + # name: "alpine" + # staticTag: "3" + # --------------------------------------------------------------------------------------- + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerimagequoted-template + image: + name: "alpine" + staticTag: "3" + ## Команда, запускаемая при старте контейнера. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.command. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + command: | + - tail + ## Аргументы для команды, запускаемой при старте контейнера. + # Пробрасываются как есть в Deployment.spec.template.spec.containers.args. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core + args: | + - -f + - /dev/null + ## Открытые порты контейнера. + # Пробрасываются как есть в Deployment.spec.template.spec.containers.ports. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#containerport-v1-core + ports: | + - name: http + containerPort: 80 + ## Несекретные переменные окружения контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerenvvars-template + env: | + - name: "APP_ENV_NAME_2" + valueFrom: + secretKeyRef: + name: "secret-1" + key: "SECRET_ENV_NAME_2" + envVars: + TEST1: "val1" + TEST2: "val2" + ## Секретные переменные окружения контейнера. + # Автоматически создадут Secret с указанными переменными окружения и примонтируют и пробросят их в контейнер. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgenerateconfigmapenvvars-template + secretEnvVars: + TEST1: "val1" + TEST2: "val2" + ## Пробросить переменные окружения из ConfigMap или Secret в контейнер. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.envFrom. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envfromsource-v1-core + envFrom: | + - configMapRef: + name: env-configmap + ## Ресурсы контейнера. + # https://github.com/flant/helm-charts/tree/master/.helm/charts/flant-lib#flgeneratecontainerresources-template + resources: + requests: + mcpu: 100 + memoryMb: 200 + ephemeralStorageMb: 1024 + limits: + mcpu: null + memoryMb: 200 + ephemeralStorageMb: 1024 + ## Liveness-проба. + # Контейнер перезагружается, если она перестаёт работать. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.livenessProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + livenessProbe: | + httpGet: + path: /liveness + port: 80 + ## Readiness-проба. + # На контейнер перестаёт поступать трафик через Service/Ingress, если она перестаёт работать. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.readinessProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + readinessProbe: | + httpGet: + path: /readiness + port: 80 + ## Startup-проба. + # Startup-проба начинает работать при старте контейнера. Если она единожды отработает успешно, + # то она перестает работать, а вместе неё включаются в работу Liveness и Readiness-пробы. Но + # если Startup-проба при старте так и не отработает успешно, то контейнер будет перезагружен. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.startupProbe. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#probe-v1-core + startupProbe: | + httpGet: + path: /startup + port: 80 + ## Хуки, отрабатывающие при запуске или остановке контейнера. + # Если приложение не может корректно завершить работу само при получении STOPSIGNAL (обычно TERM), + # то здесь можно указать команду, которая gracefully завершит ваше приложение. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.lifecycle. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#lifecycle-v1-core + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + ## Примонтировать volume в контейнер. + # Перед монтированием volume его надо добавить в параметр "volumes:" выше. + # Пробрасывается как есть в Deployment.spec.template.spec.containers.volumeMounts. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + volumeMounts: | + - mountPath: /cache + name: cache-volume + ## Несекретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут ConfigMaps и примонтируют их в контейнер по указанному пути. + configFiles: + ## Имя первого конфигурационного файла. + nginx.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/nginx/nginx.conf + ## Содержание конфигурационного файла. + # Пробрасывается как есть в ConfigMap.data.<Имя файла>. + content: | + configline1 + configline2 + nginx.conf2: + mountPath: /etc/nginx/nginx2.conf + ## указывает на имя cm с которой линковать файл в контейнере. + name: configmap-1 + ## Секретные конфигурационные файлы контейнера. Можно указывать несколько. + # Автоматически создадут Secrets и примонтируют их в контейнер по указанному пути. + secretConfigFiles: + ## Имя первого конфигурационного файла. + secret.conf: + ## Путь для монтирования файла в контейнере. + # Пробрасывается как есть в Job.spec.template.spec.containers.volumeMounts.mountPath. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#volumemount-v1-core + mountPath: /etc/secret.conf + # Автоматически сконвертируется в base64 и пробросится в Secret.data.<Имя файла>. + content: | + secretline1 + secretline2 + + + ## Список инит-контейнеров приложения. Можно указать несколько. + # Конфигурация доступных параметров идентична конфигурации containers, описанная выше ^^ + initContainers: + init-container-1: + image: + name: "alpine" + staticTag: "3" + command: | + - tail + args: | + - -f + - /dev/null + envVars: + TEST1: "val1" + secretEnvVars: + TEST1: "val1" + envFrom: | + - configMapRef: + name: env-configmap + resources: + requests: + mcpu: 100 + memoryMb: 200 + limits: + mcpu: null + memoryMb: 200 + lifecycle: | + preStop: + exec: + command: ["/usr/sbin/nginx", "-s", "quit"] + volumeMounts: | + - mountPath: /cache + name: cache-volume + configFiles: + nginx.conf: + mountPath: /etc/nginx/nginx.conf + content: | + configline1 + secretConfigFiles: + secret.conf: + mountPath: /etc/secret.conf + content: | + secretline1 + + + ## Service приложения. + # Для связи других приложений и клиентов с этим приложением. + # https://kubernetes.io/docs/concepts/services-networking/service + service: + ## Включить/выключить сервис. + enabled: true + ## DNS-имя сервиса, а также имя самого сервиса. + name: "{{ $.CurrentApp.name }}" + ## Дополнительные аннотации. + # Пробрасываются как есть в Service.metadata.annotations. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta + annotations: null + ## Headless-сервис (ClusterIP: none) + # Позволяет уменьшить нагрузку на кластер при большом количестве сервисов. + # https://kubernetes.io/docs/concepts/services-networking/service/#headless-services + headless: true + ## Настройка связи Service-портов с Deployment-портами. + # Пробрасывается как есть в Service.spec.ports. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core + ports: | + - name: http + port: 80 + + ## Лимит на удаление подов шедулером. + # Работает только для evict'ов Pod'а. При обновлении/рестарте Deployment'а evict'ов не происходит. + # Поэтому лимиты при обновлении Deployment'а описываются в отдельном ключе `strategy:` выше. + # https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#pod-disruption-budgets + podDisruptionBudget: + ## Включить/выключить podDisruptionBudget. + enabled: true + ## Максимальное количество недоступных (evicted) Pod'ов. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#poddisruptionbudgetspec-v1beta1-policy + maxUnavailable: 1 + + ## Сбор статистики по потреблению ресурсов контейнеров и (опционально) автоматическое выставление resources. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler + verticalPodAutoscaler: + ## Включить/выключить verticalPodAutoscaler. + enabled: true + ## Выставлять ли resources автоматически, и в каких случаях. + # Для сбора статистики достаточно "updateMode: Off". + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#podupdatepolicy_v1_autoscalingk8sio + updateMode: "Auto" + ## Настройка автовыставления resources для каждого контейнера по отдельности. + # Пробрасывается как есть в VerticalPodAutoscaler.spec.resourcePolicy. + # https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#verticalpodautoscalerspec_v1_autoscalingk8sio + resourcePolicy: | + containerPolicies: + - containerName: "some-container" + mode: "Off" + + ## Горизонтальный скейлинг Pod'ов. + horizontalPodAutoscaler: + ## Включить/выключить horizontalPodAutoscaler. + enabled: true + ## Не скейлить ниже этого количества реплик. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#horizontalpodautoscalerspec-v2beta2-autoscaling + minReplicas: 1 + ## Не скейлить выше этого количества реплик. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#horizontalpodautoscalerspec-v2beta2-autoscaling + maxReplicas: 1 + ## Поведение при скейлинге. + # Пробрасывается как есть в HorizontalPodAutoscaler.spec.behavior. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#horizontalpodautoscalerbehavior-v2beta2-autoscaling + behavior: | + scaleDown: + policies: + - type: Percent + value: 10 + periodSeconds: 60 + ## Конфигурация метрик, по которым скейлить. + # Пробрасывается как есть в HorizontalPodAutoscaler.spec.metrics. + # https://v1-19.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#metricspec-v2beta2-autoscaling + metrics: | + - type: Object + object: + describedObject: + apiVersion: v1 + kind: Service + name: {{ include "fl.valueQuoted" (list $ . $.CurrentApp.service.name) }} + metric: + name: "metric-1" + target: + type: Value + value: 10000m + + ## Кастомные метрики для скейлинга. Можно указывать несколько. + # https://early.deckhouse.io/modules/301-prometheus-metrics-adapter/usage.html#скейлинг-пaо-кастомным-метрикам + customMetricResources: + ## Имя ресурса метрики. + metric-1: + ## Включить/выключить создание метрики. + enabled: true + ## Тип кастомной метрики. + # https://early.deckhouse.io/modules/301-prometheus-metrics-adapter/cr.html#namespaced-custom-resources + kind: "ServiceMetric" + ## PromQL-выражение, результат которого и будет метрикой. + # Пробрасывается как есть в .spec.query. + # https://early.deckhouse.io/modules/301-prometheus-metrics-adapter/cr.html#namespaced-custom-resources + query: 'sum(rate(sidekiq_jobs_enqueued_total{<<.LabelMatchers>>, queue="default"}[1m])) by (<<.GroupBy>>)' + # Приложение из примера в документации + nginx: + _include: ["apps-stateless-defaultApp"] + replicas: 1 + containers: + nginx: + image: + name: nginx + ports: | + - name: http + containerPort: 80 + configFiles: + default.conf: + mountPath: /etc/nginx/templates/default.conf.template + content: | + server { + listen 80 default_server; + listen [::]:80 default_server; + server_name {{ $.Values.global.ci_url }} {{ $.Values.global.ci_url }}; + root /var/www/{{ $.Values.global.ci_url }}; + index index.html; + try_files $uri /index.html; + location / { + proxy_set_header Authorization "Bearer ${SECRET_TOKEN}"; + proxy_pass_header Authorization; + proxy_pass https://backend:3000; + } + } + secretEnvVars: + SECRET_TOKEN: "!!!secret-token-for-backend!!!" + service: + enabled: true + ports: | + - name: http + port: 80 + minimum-app: + _include: ["apps-stateless-defaultApp"] + # enabled: false + containers: + container-1: + enabled: true + image: + name: "alpine" + staticTag: "3" + command: | + - tail + pod-disruption-budget-integer: + _include: ["apps-stateless-defaultApp"] + # enabled: false + replicas: 2 + podDisruptionBudget: + enabled: true + maxUnavailable: 1 + containers: + container-1: + enabled: true + image: + name: "alpine" + staticTag: "3" + command: | + - tail +apps-custom-prometheus-rules: + test-rule: + _include: ["apps-default-library-app"] + groups: + test-group: + alerts: + test-alert: + isTemplate: false + content: | + for: 5m + expr: sum(kafka_consumergroup_lag{}) by (consumergroup, topic) > 10000 + labels: + severity_level: "4" + annotations: + description: "Too many unprocessed messages in sentry in kafka in a topic" + summary: |- + Слишком много не обработанных сообщений в топиках kafka sentry + Проверить работу consumer топика + plk_markup_format: markdown + plk_protocol_version: "1" + plk_labels_as_annotations: pod,instance + sentry-kafka-lag: + isTemplate: true + content: | + for: 5m + expr: sum(kafka_consumergroup_lag{}) by (consumergroup, topic) > 50000 + labels: + severity_level: "3" + annotations: + description: "Too many unprocessed messages in sentry in kafka in a topic" + summary: |- + Слишком много не обработанных сообщений в топиках kafka sentry + Проверить работу consumer топика + plk_markup_format: markdown + plk_protocol_version: "1" + plk_labels_as_annotations: pod,instance + +apps-limit-range: + mem-dafault: + _include: ["apps-default-library-app"] + limits: | + - default: + memory: 128Mi + defaultRequest: + memory: 128Mi + type: Container + +apps-pvcs: + test-pvc: + _include: ["apps-default-library-app"] + storageClassName: test-sc + accessModes: | + - ReadWriteMany + resources: | + requests: + storage: 1Gi + +test-group: + __GroupVars__: + type: apps-stateless + test-app-ingress: + __AppType__: apps-ingresses + _include: ["apps-default-library-app"] + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + test-app-stateless: + _include: ["apps-default-library-app"] + containers: + main: + image: + name: nginx + envVars: + notRegex: + _default: testDefault + "te[st]t": testRegex + test: notRegex + regexEnv: + _default: defaultError + "te[ds].": regex + testing: error + defaultTest: + _default: default + "tes[x]": regexError + testing: error + +# 1.3.0 +# add custom certificates +apps-certificates: + custom-certificate-1: + _include: ["apps-default-library-app"] + host: example.org + hosts: | + - "example-1.org" + - "example-2.org" + custom-issuer-1: + _include: ["apps-default-library-app"] + clusterIssuer: customIssuer + host: example.org + hosts: | + - "example-1.org" + - "example-2.org" +apps-kafka-strimzi: + test-kafka: + kafka: + _include: ["base-defaultApp-stateful"] + brokers: + host: + _default: kafka-sentry-test-kafka-bootstrap + prod: kafka-sentry-prod-kafka-bootstrap + hosts: + _default: + - kafka-sentry-test-kafka-brokers + prod: + - kafka-sentry-prod-kafka-brokers + port: + _default: 9092 + version: + _default: 2.7.0 + replicas: + _default: 1 + prod: 3 + resources: + requests: + mcpu: + _default: 100 + prod: 1000 + memoryMb: + _default: 768 + prod: 4096 + limits: + memoryMb: + _default: 768 + prod: 4096 + jvmOptions: + _default: | + -Xms: 512m + -Xmx: 512m + prod: | + -Xms: 2048m + -Xmx: 2048m + storage: + size: + _default: 5Gi + prod: 100Gi + + ui: + ingress: + url: + prod: sentry-kafka.{{ include "fl.value" (list $ . $.Values.global.baseUrl) }} + dex: + allowedGroups: + _default: + - hypermetrica-external-admins + resources: + requests: + mcpu: 100 + memoryMb: 512 + limits: + memoryMb: 512 + memory: 512Mi + _include: ["base-defaultApp"] + exporter: + _include: ["base-defaultApp-stateful"] + resources: + requests: + mcpu: 200 + memoryMb: 128 + limits: + memoryMb: 128 + entityOperator: + _include: ["base-defaultApp-stateful"] + topicOperator: + resources: + requests: + mcpu: 50 + memoryMb: 256 + limits: + memoryMb: 256 + userOperator: + resources: + requests: + mcpu: 50 + memoryMb: 256 + limits: + memoryMb: 256 + topics: + snuba-commit-log: + _include: ["default-kafkaTopic"] + cdc: + _include: ["default-kafkaTopic"] + event-replacements: + _include: ["default-kafkaTopic"] + event-replacements-legacy: + _include: ["default-kafkaTopic"] + events: + _include: ["default-kafkaTopic"] + partitions: + _default: 1 + replicas: + _default: 3 + events-subscription-results: + _include: ["default-kafkaTopic"] + ingest-attachments: + _include: ["default-kafkaTopic"] + ingest-events: + _include: ["default-kafkaTopic"] + partitions: + _default: 6 + ingest-metrics: + _include: ["default-kafkaTopic"] + ingest-sessions: + _include: ["default-kafkaTopic"] + ingest-transactions: + _include: ["default-kafkaTopic"] + outcomes: + _include: ["default-kafkaTopic"] + snuba-queries: + _include: ["default-kafkaTopic"] + transactions-subscription-results: + _include: ["default-kafkaTopic"] + zookeeper: + _include: ["base-defaultApp-stateful"] + replicas: + _default: 1 + prod: 3 + resources: + requests: + mcpu: 500 + memoryMb: 1024 + limits: + memoryMb: 1024 + jvmOptions: + _default: | + -Xms: 512m + -Xmx: 512m + metricsConfig: | + type: jmxPrometheusExporter + valueFrom: + configMapKeyRef: + name: kafka-metrics + key: zookeeper-metrics-config.yml + storage: + size: + _default: 1Gi + prod: 10Gi + +fromSecretsEnvVars-test: + # __GroupVars__ указываает что блок является группой и будет обрабатываться библиотекой helm-apps, + # генерируя приложения, указанные в поле type + # Также есть возможность переопределить тип генерируемого приложения, в блоке самого приложения, указав его в переменной + # __AppType__, например: + # my-steteless-app: + # __AppType__: "apps-stateless" + __GroupVars__: + # type укзывает какого типа приложения будут генерироваться группой + # приложение можно объявить как темплейт следующим образом + # создать в папке .helm/templates файл _<имя файла>.tpl + # {{ define "<тип приложения>.render" }} + # {{- $ := .}} + # {{- with $.CurrentApp}} + # здесь вставить код темплейта + # в темплейте будут доступны все переменные из блока генерируемого приложения .<имя переменной> + # {{- end}} + # {{- end}} + # или использовать существующий тип приложения + type: apps-stateless + app-for-test-from-secrets-env-vars: + _include: ["apps-stateless-defaultApp"] + # werfWeight задает порядок выкатки приложений добавляя аннотацию werf.io/weight: "NUM" + # аннотация добавляется на все автоматически связанные с приложением ресурсы + # подробнее: https://ru.werf.io/documentation/v1.2/reference/deploy_annotations.html#resource-weight + werfWeight: -10 + verticalPodAutoscaler: + enabled: false + containers: + main: + fromSecretsEnvVars: + secret-1: + APP_ENV_NAME_1: SECRET_ENV_NAME_1 + APP_ENV_NAME_2: SECRET_ENV_NAME_2 + secret-1: + # В существующей группе можно сменить тип любого приложения, + # указав его тип в переменной __AppType__ + __AppType__: apps-secrets + envVars: + SECRET_ENV_NAME_1: test1 + SECRET_ENV_NAME_2: test2 +apps-dex-clients: + app-auth: + _include: ["default-library-app"] + redirectURIs: test.url + +ingress-with-dexAuth: + __GroupVars__: + type: apps-ingresses + ingress-dex-auth: + _include: ["apps-ingresses-defaultIngress"] + dexAuth: + enabled: true + clusterDomain: cluster.local + ingressClassName: nginx + host: example.org + paths: | + - path: / + pathType: Prefix + backend: + service: + name: testService + port: + number: 80 + tls: + enabled: true + +apps-stateless-deckhouse-metrics: + __GroupVars__: + type: apps-stateless + app-with-deckhouse-metrics: + _include: ["apps-stateless-defaultApp"] + containers: + main: + envVars: + test: test + deckhouseMetrics: + mymetric-1: + enabled: + _default: true + kind: PodMetric + query: 'round(sum by(<<.GroupBy>>) (phpfpm_processes_total{state="active",<<.LabelMatchers>>}) / sum by(<<.GroupBy>>) (phpfpm_processes_total{<<.LabelMatchers>>}) * 100)' + metric-with-custom-name: + # по-умолчанию если метрика не выключена, то она считается включенной + enabled: + production: true + kind: ServiceMetric + name: custom-name + query: 'round(sum by(<<.GroupBy>>) (phpfpm_processes_total{state="active",<<.LabelMatchers>>}) / sum by(<<.GroupBy>>) (phpfpm_processes_total{<<.LabelMatchers>>}) * 100)' + +test-common-group-hook: + __GroupVars__: + _include: ["apps-default-library-app"] + type: apps-stateless + _preRenderGroupHook: | + {{ if include "fl.isTrue" (list $ . $.CurrentGroupVars.enabled) }} + {{ set (index $.CurrentGroup "test-app") "enabled" true }} + {{ set $.CurrentGroup "test-app-2" (index $.CurrentGroup "test-app" | deepCopy) }} + {{ end }} + _preRenderAppHook: | + test + test-app: + _include: ["apps-stateless-defaultApp"] + containers: + main: + envVars: + test: test + # serviceAccount генерирует связанный с приложением serviceAccount и связывает его с ClusterRole и ClusterRoleBinding + serviceAccount: + enabled: true + name: "{{ $.CurrentApp.name }}" + clusterRole: + name: "{{ $.CurrentApp.name }}:prometheus-access" + rules: | + - apiGroups: ["monitoring.coreos.com"] + resources: ["prometheuses/http"] + resourceNames: ["main", "longterm"] + verbs: ["get"] diff --git a/tests/werf.yaml b/tests/werf.yaml new file mode 100644 index 0000000..257b19f --- /dev/null +++ b/tests/werf.yaml @@ -0,0 +1,5 @@ +project: test +configVersion: 1 +--- +image: nginx +from: nginx