Compare commits

..

No commits in common. "master" and "3.9.62" have entirely different histories.

134 changed files with 89 additions and 12389 deletions

View File

@ -4,8 +4,6 @@ include:
stages:
- build
- slices
- slices-publish
variables:
RELEASE_NAME: iac-offline
@ -35,31 +33,3 @@ build_offline_mirror:
- docker push ${IMAGE_NAME}
- docker push ${CR}/${RELEASE_NAME}:latest
timeout: 30m
generate_slice_jobs:
stage: slices
image: ${CR}/infra/slice-builder:1.0.0
tags:
- build
rules:
- if: $CI_COMMIT_TAG =~ /^v?(?:\d+\.){2}\d+$/
variables:
GIT_DEPTH: "1"
script:
- python3 ci/publish-slice.py generate --release "$CI_COMMIT_TAG" --commit "$CI_COMMIT_SHA" --out slices-pipeline.yml
- cat slices-pipeline.yml
artifacts:
paths:
- slices-pipeline.yml
publish_contour_slices:
stage: slices-publish
rules:
- if: $CI_COMMIT_TAG =~ /^v?(?:\d+\.){2}\d+$/
needs:
- generate_slice_jobs
trigger:
include:
- artifact: slices-pipeline.yml
job: generate_slice_jobs
strategy: depend

View File

@ -25,13 +25,11 @@
| Путь | Что |
|---|---|
| `clusters/` | 17 точек входа Flux + шаблон `contour` — **источник истины «что где раскатано»** |
| `infrastructure/` | 44 платформенных компонента |
| `apps/` | 39 прикладных сервисов |
| `docs/apps/` | 34 mermaid-диаграммы по сервисам + `README.md` с доменной группировкой |
| `docs/architecture/` | модель контура в ArchiMate + скрипты-сканеры (`scan_calls.py`, `scan_contour.py`) и сборка (`build_archimate.py`); реальные значения инсталляций сюда не кладутся, только `*.example.json` |
| `clusters/` | 10 точек входа Flux — **источник истины «что где раскатано»** |
| `infrastructure/` | 36 платформенных компонентов |
| `apps/` | 37 прикладных сервисов |
| `docs/apps/` | 35 mermaid-диаграмм по сервисам + `README.md` с доменной группировкой |
| `docs/closed-contour-deployment.md` | ранбук поднятия закрытого контура |
| `ci/`, `.gitlab-ci.yml`, `Dockerfile` | на тег `vX.Y.Z`: образ офлайн-зеркала `iac-offline` и публикация «слайсов» контуров (см. ниже) |
| `README.md` | mermaid-мегакарта платформы, структура репозитория, инструкции по добавлению app / infra / кластера |
| `IAC-CATALOG.md` | каталог всего `infra/*`; §5 — про этот репозиторий |
| `inventory.yaml` | сгенерированный снимок прода: 59 namespace → kind → имена |
@ -40,18 +38,15 @@
**Кластер.** `clusters/<cluster>/kustomization.yaml` — плоский список `../../apps/<app>/<cluster>`. Инфраструктура подключается двумя разными способами:
- `d8-ugmk-prod`, `sarex-contour`, `ugok`, `uralkal`, `vad` — прямо в корневом kustomization как `../../infrastructure/<comp>/<c>`;
- `brusnika-prod`, `brusnika-stage`, `vk-infra`, `vk-prod` — через `clusters/<c>/infrastructure/kustomization.yaml`, который ссылается на оверлеи `../../../infrastructure/<comp>/<c>` (`base` + патч HelmRelease + сопутствующие манифесты компонента);
- `wb`, `yc-cps-prod`, `yc-infra-prod`, `yc-k8s-test-02` — через `clusters/<c>/infrastructure/kustomization.yaml`, который ссылается на `../../../infrastructure/<comp>` (резолвится в `base`), а различия лежат в `clusters/<c>/infrastructure/patches/*.yaml`;
- `stand` — послойно: `layers.yaml` объявляет Flux-`Kustomization` `00-vault → 10-mesh → 20-data → 30-platform → 40-routing` (каталоги `clusters/stand/<NN>-<layer>/`). Слои ссылаются на оверлеи `infrastructure/<comp>/contour-template`, а значения контура (`${STORAGE_CLASS}`, `${HOST_*}`, `${GATEWAY_REPLICAS}`) Flux подставляет через `postBuild.substituteFrom` из ConfigMap `contour-vars`.
- `d8-ugmk-prod` — прямо в корневом kustomization как `../../infrastructure/<comp>/d8-ugmk-prod`;
- `brusnika-prod`, `brusnika-stage` — через `clusters/<c>/infrastructure/kustomization.yaml`, который ссылается на оверлеи `../../../infrastructure/<comp>/<c>` (`base` + патч HelmRelease + сопутствующие манифесты компонента);
- `wb` — через `clusters/wb/infrastructure/kustomization.yaml`, который ссылается на `../../../infrastructure/<comp>` (резолвится в `base`), а различия лежат в `clusters/wb/infrastructure/patches/*.yaml`.
Кластеры с приложениями: `asterus`, `brusnika-prod`, `brusnika-stage`, `d8-ugmk-prod`, `sarex-contour`, `ugok`, `uralkal`, `vad`, `wb`, `yc-ecp`, `yc-k8s-test`. Только инфраструктура: `stand`, `vk-infra`, `vk-prod`, `yc-cps-prod`, `yc-infra-prod`, `yc-k8s-test-02`. Шаблон: `contour`.
Кластеры: `brusnika-prod`, `brusnika-stage`, `d8-ugmk-prod`, `wb`, `yc-cps-prod`, `yc-ecp`, `yc-infra-prod`, `yc-k8s-test`, `yc-k8s-test-02`, `contour`.
**Приложение.** `apps/<app>/base/` — по одному `.yaml` на процесс (`backend.yaml`, `celery.yaml`, `frontend.yaml`) плюс `namespace.yaml`; оверлеи в `apps/<app>/<cluster>/`. Namespace = имя приложения. Рядом с манифестами лежит документация: `CONFIGURATION.md` (все env-переменные и откуда берутся), `ENDPOINTS.md` (исходящие HTTP-вызовы), `openapi.yaml` (входящий контракт), `.env.example`. Если процессов несколько — файлы с префиксом процесса: `api.ENDPOINTS.md`, `pdm.CONFIGURATION.md`, `frontend.CONFIGURATION.md`.
**Инфра-компонент.** `infrastructure/<comp>/base/` + оверлеи `<comp>/<cluster>/`. Корневой `infrastructure/<comp>/kustomization.yaml` всегда указывает просто на `base`. Оверлей `contour-template` (есть у vault, istio-*, cert-manager, postgresql, kafka, rabbitmq, zitadel, camunda, istio-config) — параметризованный `${VAR}`, а не привязанный к конкретному кластеру. Чарты идут с суффиксом контура: `vault-contour`, `keycloak-contour`, `postgresql-contour` и — внимание — `idp-contour` это Zitadel.
**Слайсы.** На тег `vX.Y.Z` `ci/publish-slice.py generate` создаёт child-pipeline с отдельной джобой на каждый `clusters/*/kustomization.yaml`. Джоба вызывает `ci/build-slice.py`: тот обходит дерево Kustomize от `clusters/<c>` и упаковывает только нужные файлы в tar. Результат публикуется как OCI-артефакт `cr.yandex/crpct2b2lsmao4nm0cjk/<c>-iac:<tag>` (media type `application/vnd.sarex.slice.config.v1+json`). Так закрытый контур получает только свой срез. Тесты — `ci/tests/test_build_slice.py`.
**Инфра-компонент.** `infrastructure/<comp>/base/` + оверлеи `<comp>/<cluster>/`. Корневой `infrastructure/<comp>/kustomization.yaml` всегда указывает просто на `base`. Чарты идут с суффиксом контура: `vault-contour`, `keycloak-contour`, `postgresql-contour` и — внимание — `idp-contour` это Zitadel.
## Где искать конкретное
@ -67,23 +62,18 @@
| Тег образа в контуре | `apps/<app>/<cluster>/*.yaml`, поле `image.tag` |
| Что реально в проде по namespace | `inventory.yaml` |
| Где лежит исходный код приложения | `IAC-CATALOG.md` §5.3.8 |
| Что попадёт в слайс контура | `python3 ci/build-slice.py` по `clusters/<c>` (логика сбора — там же) |
| Переменные шаблонного контура | `${...}` в `infrastructure/*/contour-template`; значения — ConfigMap `contour-vars` в кластере, не в репозитории |
## Ловушки
- **`dsinv/`** — оверлей есть у 25 приложений, но `clusters/dsinv/` не существует. Это материализованный снимок живого контура WB, а не источник деплоя. Развёрнутым не считать.
- **Две несовместимые конвенции оверлеев** (`IAC-CATALOG.md` §5.3.4). `d8-ugmk-prod`, `yc-k8s-test`, `vad`, `uralkal`, `sarex-contour`, `dsinv` наследуют `base` и патчат его. `brusnika-prod`, `brusnika-stage`, `yc-ecp`, `asterus` — полные копии HelmRelease, которые молча дрейфуют от base. Правка `base` на них не влияет. **`wb` (4 из 27) и `ugok` (9 из 33) смешанные** — проверяйте `resources:` конкретного оверлея; в `ugok` причина обычно указана комментарием в его `kustomization.yaml`.
- **`asterus` подключён почти пустым**: оверлеи есть у 26 приложений, но в `clusters/asterus/kustomization.yaml` активен только `auth-flow`, остальные закомментированы. Инфраструктуры в кластере нет.
- **Слайс публикуется для каждого `clusters/*/kustomization.yaml`** — в том числе для тестовых и инфраструктурных кластеров. Новая папка в `clusters/` с этим файлом автоматически станет слайсом на следующем теге. У `contour` такого файла нет, поэтому для него слайс не собирается.
- **Две несовместимые конвенции оверлеев** (`IAC-CATALOG.md` §5.3.4). `d8-ugmk-prod`, `yc-k8s-test`, `dsinv` наследуют `base` и патчат его. `brusnika-prod`, `brusnika-stage`, `yc-ecp` — полные копии HelmRelease, которые молча дрейфуют от base. Правка `base` на них не влияет.
- **`IAC-CATALOG.md` — снимок на 05.08.2026** и по составу приложений на контур уже расходится с реальностью (там у `wb` ноль приложений, фактически 26 оверлеев). Всегда сверяться с `clusters/*/kustomization.yaml`.
- **Мегадиаграмма в `README.md` идеализирована** — рисована руками, показывает все приложения на одной Postgres и все фронтенды за `control-interface`. Проверяемые источники — env-переменные и istio-config.
- **`clusters/contour/` — не кластер, а шаблон**: в нём нет `flux-system`, только `apps.yaml`, `infrastructure.yaml`, `helm-repositories.yaml`.
- **Path-роутинг лежит в `infrastructure/istio-config/<c>/` у большинства контуров, но не у WB.** В `clusters/wb/infrastructure/patches/istio-config.yaml` через Istio публикуются только платформенные UI (Gitea, Grafana, OpenObserve, Superset, Vault, GoAlert), а прикладной трафик разводится вне репозитория. В Brusnika схема смешанная: часть путей идёт напрямую в сервисы, остальное — одним правилом в `yet-another-nginx-service.global-ingress`.
- **`vk-prod` маршрутизирует в namespace с суффиксом `-prod`** (`eav-prod`, `pm-prod`, `inspections-prod`), а приложений из `apps/` в этом кластере нет: сервисы раскатываются не этим репозиторием.
- **Имена namespace расходятся между источниками**: `inventory.yaml` и `vk-prod` используют суффикс (`flows-prod`), `apps/` — голое имя (`flows`).
- **66 оверлеев приложений не подключены ни к одному кластеру**: 25 — `dsinv`, 25 — закомментированные в `asterus`, остальные 16 — мёртвый код (в основном `brusnika-prod`, плюс `bi`/`iam` в `wb` и `yc-k8s-test`).
- **Нет диаграмм** в `docs/apps/` для `ams-sync`, `auth-flow`, `bi`, `iam`, `premises`.
- **Path-роутинг виден в репозитории только у `d8-ugmk-prod` и `yc-ecp`.** В Brusnika и WB весь прикладной трафик уходит одним правилом в `nginx-service.global-ingress` / `yet-another-nginx-service.global-ingress`, а разводка по путям происходит вне репозитория. Через Istio там публикуются только платформенные UI (Gitea, Grafana, OpenObserve, Superset, Vault, Zitadel, Camunda, MinIO).
- **Имена namespace расходятся между источниками**: `inventory.yaml` использует суффикс (`flows-prod`), `apps/` — голое имя (`flows`).
- **17 оверлеев приложений не подключены ни к одному кластеру** — мёртвый код.
- **Нет диаграмм** в `docs/apps/` для `iam`, `auth-flow`, `ams-sync`.
- **Чистые фронтенды без бэкенда в этом репозитории**: `auth-flow`, `control-interface`, `cross-section`, `document-link`, `prescriptions`, `projects`, `remarks`, `reviews`, `stamp-verification`. Бэкенд `projects` живёт в `planning/projects-backend`.
- Самые центральные сервисы по числу входящих ссылок: `documentations` (~85), `processing` / `workflow` (~76), `django` (~53), `eav` (~47), `resources` (~44), `bim` (~39), `flows` (~38). Менять их конфигурацию — дороже всего.

View File

@ -18,7 +18,7 @@ spec:
spec:
containers:
- name: debug
image: cr.yandex/crp3ccidau046kdj8g9q/mc-files:dev5
image: cr.yandex/crp3ccidau046kdj8g9q/mc-files:dev9
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]
imagePullSecrets:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/cross-section-app:contour_c5e5d476@sha256:04e0eb23df7138eb6c0dad6d790c7229607cb70eb7a4d7448aa70a236caa7d52
deployment:
resources:
requests:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
backend:
deployment:
replicaCount:
_default: 2
envs:
- name: ALLOWED_HOSTS
value:
@ -132,6 +129,10 @@ spec:
- name: ZITADEL_HOST
value:
_default: https://sarex-login.uralmine.com
# Включено вместе с message-hub: он консьюмер топика message-hub-stage
# (SETTINGS_TOPICS в apps/message-hub/d8-ugmk-prod/message-hub.yaml),
# а писать в этот топик должен django. Vault-шаблон django-kafka
# и KAFKA_SSL_CAFILE уже есть в base — здесь только флаг.
- name: SERVER_KAFKA_ENABLED
value:
_default: "True"

File diff suppressed because one or more lines are too long

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kc-sync-configmap
namespace: django
data:
kc_sync.py: |
from .production import *
class KeyCloakSyncJobSettings(KeyCloakSettings):
base_url: str = ""
realm: str = "master"
KEYCLOAKSETTINGS = KeyCloakSyncJobSettings()

View File

@ -1,102 +0,0 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: kc-sync-users
namespace: django
spec:
schedule: "*/10 * * * *"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 0
activeDeadlineSeconds: 540
ttlSecondsAfterFinished: 3600
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: django
vault.hashicorp.com/agent-inject-secret-django-postgresql: secrets/data/apps/django/postgres
vault.hashicorp.com/agent-inject-template-django-postgresql: |-
{{- with secret "secrets/data/apps/django/postgres" -}}
DJANGO_POSTGRES_HOST={{ index .Data.data "host" }}
DJANGO_POSTGRES_PORTS={{ index .Data.data "port" }}
DJANGO_POSTGRES_DATABASE={{ index .Data.data "database" }}
DJANGO_POSTGRES_USER={{ index .Data.data "username" }}
DJANGO_POSTGRES_PASSWORD={{ index .Data.data "password" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-django-jwt-private: secrets/data/vault/common/rsa_keys
vault.hashicorp.com/agent-inject-template-django-jwt-private: |-
{{- with secret "secrets/data/vault/common/rsa_keys" -}}
{{ index .Data.data "private_key" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-django-jwt-public: secrets/data/vault/common/rsa_keys
vault.hashicorp.com/agent-inject-template-django-jwt-public: |-
{{- with secret "secrets/data/vault/common/rsa_keys" -}}
{{ index .Data.data "public_key" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-kc-admin: secrets/data/camunda/keycloak-admin
vault.hashicorp.com/agent-inject-template-kc-admin: |-
{{- with secret "secrets/data/camunda/keycloak-admin" -}}
{{ index .Data.data "admin-password" }}
{{- end -}}
spec:
serviceAccountName: django-vault
automountServiceAccountToken: true
restartPolicy: Never
imagePullSecrets:
- name: regcred
containers:
- name: kc-sync
image: cr.yandex/crp3ccidau046kdj8g9q/backend:production_7d0ec48b
imagePullPolicy: IfNotPresent
workingDir: /opt/sarex
command: ["/bin/sh", "-ec"]
args:
- |
exec python manage.py synchronization_keycloak --username "$KC_ADMIN_USERNAME" --password "$(cat /vault/secrets/kc-admin)" --sync-users-def-company-id 1
env:
- name: DJANGO_SETTINGS_MODULE
value: config.settings.kc_sync
- name: DJANGO_HOST
value: https://sarex.local.uralkali.com
- name: ISOLATED
value: "1"
- name: KC_ADMIN_USERNAME
value: admin
- name: KC_BASE_URL
value: http://camunda-keycloak.camunda.svc.cluster.local/auth
- name: KC_REALM
value: sarex
- name: KC_STAFF
value: GL_sarex-staff
- name: KC_SUPERUSER
value: GL_sarex-admins
- name: KC_SYNC_ADMIN
value: "True"
resources:
requests:
cpu: 50m
memory: 256Mi
volumeMounts:
- name: django-configmap
mountPath: /opt/sarex/config/settings/production.py
subPath: production.py
readOnly: true
- name: kc-sync-configmap
mountPath: /opt/sarex/config/settings/kc_sync.py
subPath: kc_sync.py
readOnly: true
volumes:
- name: django-configmap
configMap:
name: django-configmap
- name: kc-sync-configmap
configMap:
name: kc-sync-configmap

View File

@ -2,11 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- kc-sync-configmap.yaml
- kc-sync-cronjob.yaml
- zitadel-action-configmap.yaml
- zitadel-action-job.yaml
- upload-oblako-job.yaml
patches:
- path: nginx-configmap.yaml
target:

View File

@ -1,113 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
name: upload-oblako-las
namespace: django
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
spec:
backoffLimit: 1
activeDeadlineSeconds: 10800
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: django
vault.hashicorp.com/agent-inject-secret-minio: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-minio: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
{{- $s3 := index .Data.data "documentations_s3_service_account_json" | parseJSON -}}
export MINIO_ENDPOINT='http://{{ $s3.endpoint }}'
export MINIO_ACCESS_KEY='{{ $s3.access_key_id }}'
export MINIO_SECRET_KEY='{{ $s3.secret_access_key }}'
{{- end -}}
spec:
serviceAccountName: django-vault
automountServiceAccountToken: true
restartPolicy: Never
imagePullSecrets:
- name: regcred
containers:
- name: upload
image: cr.yandex/crp3ccidau046kdj8g9q/mc-files:dev5
imagePullPolicy: IfNotPresent
command: ["/bin/bash", "-euo", "pipefail", "-c"]
args:
- |
log() { echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') $*"; }
. /vault/secrets/minio
F="/work/$OBJECT_NAME"
log "step 1/5: free space in /work"
df -h /work
avail=$(df -Pk /work | awk 'NR==2 {print $4 * 1024}')
log "available ${avail} bytes, need ${EXPECTED_SIZE}"
if [ "$avail" -lt "$EXPECTED_SIZE" ]; then
log "ERROR: not enough space"; exit 1
fi
log "step 2/5: download from Yandex Object Storage"
curl -kfsS --retry 5 --retry-delay 10 -C - -o "$F" "$SOURCE_URL" &
pid=$!
while kill -0 "$pid" 2>/dev/null; do
sleep 15
size=$(stat -c %s "$F" 2>/dev/null || echo 0)
log "downloaded ${size} / ${EXPECTED_SIZE} bytes ($((size * 100 / EXPECTED_SIZE))%)"
done
wait "$pid" || { log "ERROR: curl failed with code $?"; exit 1; }
size=$(stat -c %s "$F")
log "download finished, ${size} bytes"
if [ "$size" -ne "$EXPECTED_SIZE" ]; then
log "ERROR: size mismatch"; exit 1
fi
log "step 3/5: connect to MinIO ${MINIO_ENDPOINT}"
mc alias set target "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" >/dev/null
if ! mc ls "target/$BUCKET" >/dev/null; then
log "ERROR: bucket $BUCKET is not reachable with documentations_s3_service_account_json credentials"; exit 1
fi
log "bucket $BUCKET is reachable"
log "step 4/5: upload to $BUCKET/$OBJECT_NAME"
mc cp --quiet "$F" "target/$BUCKET/$OBJECT_NAME" &
pid=$!
while kill -0 "$pid" 2>/dev/null; do
sleep 30
log "upload in progress"
done
wait "$pid" || { log "ERROR: mc cp failed with code $?"; exit 1; }
log "step 5/5: verify"
mc stat "target/$BUCKET/$OBJECT_NAME"
remote=$(mc stat --json "target/$BUCKET/$OBJECT_NAME" | sed -n 's/.*"size":\([0-9]*\).*/\1/p')
if [ "$remote" != "$EXPECTED_SIZE" ]; then
log "ERROR: remote size ${remote} != ${EXPECTED_SIZE}"; exit 1
fi
rm -f "$F"
log "done: $BUCKET/$OBJECT_NAME, ${remote} bytes"
env:
- name: SOURCE_URL
value: "https://storage.yandexcloud.net/documentations-prod-270-dlia-stateisarex/7937275e-967e-452e-b79d-b1991bca1dd4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=YCAJEOB86NUJTIBSnlwY_OvTI%2F20261105%2Fru-central1%2Fs3%2Faws4_request&X-Amz-Date=20261105T120651Z&X-Amz-Expires=259200&X-Amz-Signature=423bf587fbf89ba6a3b10bda57f37100bfcd72c3a6ba84a3c91eec922364c088&X-Amz-SignedHeaders=host&response-content-disposition=attachment"
- name: EXPECTED_SIZE
value: "5488991489"
- name: BUCKET
value: django
- name: OBJECT_NAME
value: Облако.las
resources:
requests:
cpu: 100m
memory: 256Mi
ephemeral-storage: 6Gi
limits:
ephemeral-storage: 7Gi
volumeMounts:
- name: work
mountPath: /work
volumes:
- name: work
emptyDir:
sizeLimit: 7Gi

View File

@ -1,131 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: zitadel-action-create-user
namespace: django
data:
createUser.js: |
function createUser(ctx, api) {
let http = require('zitadel/http');
let logger = require('zitadel/log');
const BACKEND_HOST = 'http://backend-svc.django.svc.cluster.local:80/api'
const USER_COMPANIES = [1]
const BASIC_AUTH = '__BASIC_AUTH__'
logger.log('Creating external user in backend');
logger.log('Context: ' + JSON.stringify(ctx));
const userData = {
first_name: ctx.v1.user.human.firstName,
last_name: ctx.v1.user.human.lastName,
email: ctx.v1.user.human.email,
username: ctx.v1.user.username,
companies: USER_COMPANIES,
departments: [],
user_permissions: []
};
logger.log('Sending user data: ' + JSON.stringify(userData));
const response = http.fetch(BACKEND_HOST + '/core/admin/users/external/', {
method: 'POST',
body: userData,
headers: {
'Content-Type': 'application/json',
'Authorization': BASIC_AUTH
}
});
if (response.status != 200 && response.status != 201) {
logger.log('Error response status: ' + response.status);
logger.log('Error response body: ' + response.text());
throw "Backend API error: " + response.status;
}
const metadata = response.json();
logger.log('Metadata received: ' + JSON.stringify(metadata));
Object.keys(metadata).forEach(function(key) {
var value = metadata[key];
logger.log('Setting metadata - Key: ' + key + ', Value: ' + value);
api.v1.user.appendMetadata(key, value);
});
logger.log('External user successfully created and metadata set');
}
apply.py: |
import os
import sys
import httpx
ZITADEL = os.environ["ZITADEL_URL"]
HEADERS = {
"Host": os.environ["ZITADEL_HOST"],
"X-Forwarded-Proto": "https",
"Authorization": "Bearer " + open("/vault/secrets/zitadel-token").read().strip(),
"Content-Type": "application/json",
}
ORG_NAME = os.environ["ZITADEL_ORG_NAME"]
ACTION_NAME = os.environ["ACTION_NAME"]
FLOW_TYPE = os.environ["FLOW_TYPE"]
TRIGGER_TYPE = os.environ["TRIGGER_TYPE"]
client = httpx.Client(base_url=ZITADEL, headers=HEADERS, timeout=30)
def call(method, path, body=None, org_id=None, allow=()):
headers = {"x-zitadel-orgid": org_id} if org_id else {}
r = client.request(method, path, json=body, headers=headers)
if r.status_code in allow:
return None
if r.status_code >= 300:
print(f"{method} {path} -> {r.status_code}: {r.text}")
sys.exit(1)
return r.json() if r.content else {}
me = call("GET", "/auth/v1/users/me")["user"]
print(f"token user {me.get('userName')} id={me.get('id')}")
org = None
found_orgs = call("POST", "/admin/v1/orgs/_search", {"queries": []}, allow=(403,))
if found_orgs is not None:
orgs = found_orgs.get("result", [])
org = next((o for o in orgs if o.get("name", "").lower() == ORG_NAME.lower()), None)
if org is None:
print(f"org '{ORG_NAME}' not found, available: {[o.get('name') for o in orgs]}")
sys.exit(1)
else:
org = call("GET", "/management/v1/orgs/me")["org"]
print(f"no instance permissions, using token org '{org.get('name')}'")
if org.get("name", "").lower() != ORG_NAME.lower():
print(f"token org is '{org.get('name')}', not '{ORG_NAME}'")
sys.exit(1)
org_id = org["id"]
print(f"org {org['name']} id={org_id}")
script = open("/opt/action/createUser.js").read().replace(
"__BASIC_AUTH__", "Basic " + open("/vault/secrets/backend-basic").read().strip()
)
body = {"name": ACTION_NAME, "script": script, "timeout": "10s", "allowedToFail": False}
found = call(
"POST",
"/management/v1/actions/_search",
{"queries": [{"actionNameQuery": {"name": ACTION_NAME, "method": "TEXT_QUERY_METHOD_EQUALS"}}]},
org_id,
).get("result", [])
if found:
action_id = found[0]["id"]
call("PUT", f"/management/v1/actions/{action_id}", body, org_id)
print(f"action {ACTION_NAME} updated id={action_id}")
else:
action_id = call("POST", "/management/v1/actions", body, org_id)["id"]
print(f"action {ACTION_NAME} created id={action_id}")
call("POST", f"/management/v1/flows/{FLOW_TYPE}/trigger/{TRIGGER_TYPE}", {"actionIds": [action_id]}, org_id)
print(f"flow {FLOW_TYPE} trigger {TRIGGER_TYPE} -> {ACTION_NAME}")

View File

@ -1,63 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
name: zitadel-action-create-user
namespace: django
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
spec:
backoffLimit: 2
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: django
vault.hashicorp.com/agent-inject-secret-zitadel-token: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-zitadel-token: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
{{ index .Data.data "django_zitadel_access_token" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-backend-basic: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-backend-basic: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
{{ index .Data.data "key" }}
{{- end -}}
spec:
serviceAccountName: django-vault
automountServiceAccountToken: true
restartPolicy: Never
imagePullSecrets:
- name: regcred
containers:
- name: apply
image: cr.yandex/crp3ccidau046kdj8g9q/backend:production_7d0ec48b
imagePullPolicy: IfNotPresent
command: ["python", "/opt/action/apply.py"]
env:
- name: ZITADEL_URL
value: http://zitadel-idp-contour.zitadel.svc.cluster.local:8080
- name: ZITADEL_HOST
value: login.sarex.local.uralkali.com
- name: ZITADEL_ORG_NAME
value: zitadel
- name: ACTION_NAME
value: createUser
- name: FLOW_TYPE
value: "1"
- name: TRIGGER_TYPE
value: "2"
resources:
requests:
cpu: 20m
memory: 128Mi
volumeMounts:
- name: action
mountPath: /opt/action
readOnly: true
volumes:
- name: action
configMap:
name: zitadel-action-create-user

View File

@ -36,7 +36,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_9dea5b64
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_3ab263be
pullPolicy:
_default: IfNotPresent

View File

@ -65,7 +65,7 @@ spec:
enabled: false
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_358ca69c
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_179e518c
pullPolicy:
_default: IfNotPresent
service:
@ -89,9 +89,6 @@ spec:
- name: POSTGRES_POOL_SIZE
value:
_default: "20"
- name: CDE_API_ENABLED
value:
_default: "false"
- name: USE_LEGACY_BIM_FLOW
value:
_default: "true"
@ -239,9 +236,6 @@ spec:
- name: VALKEY_PORT
value:
_default: "6379"
- name: CDE_API_ENABLED
value:
_default: "false"
podAnnotations:
_default:
traffic.sidecar.istio.io/excludeOutboundPorts: "8200"

View File

@ -97,9 +97,6 @@ spec:
- name: PUBLIC_LINK_FOLDER_CONNECTOR_ENABLED
value:
_default: "true"
- name: CDE_API_ENABLED
value:
_default: "false"
- name: USE_ZITADEL
value:
_default: "1"
@ -226,9 +223,6 @@ spec:
- name: VALKEY_PORT
value:
_default: "6379"
- name: CDE_API_ENABLED
value:
_default: "false"
podAnnotations:
_default:
traffic.sidecar.istio.io/excludeOutboundPorts: "8200"

View File

@ -37,7 +37,7 @@ spec:
name:
_default: pdf-markings-amqp
replicaCount:
_default: 2
_default: 1
port:
_default: 8000
command:
@ -64,7 +64,7 @@ spec:
enabled: false
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_9dea5b64
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_3ab263be
pullPolicy:
_default: IfNotPresent
service:

View File

@ -37,7 +37,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_358ca69c
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_98a23ef5
pullPolicy:
_default: IfNotPresent

View File

@ -37,7 +37,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_358ca69c
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_98a23ef5
pullPolicy:
_default: IfNotPresent

View File

@ -156,6 +156,3 @@ spec:
- name: VALKEY_PORT
value:
_default: "6379"
- name: CDE_API_ENABLED
value:
_default: "false"

View File

@ -144,6 +144,3 @@ spec:
- name: VALKEY_PORT
value:
_default: "6379"
- name: CDE_API_ENABLED
value:
_default: "false"

View File

@ -8,10 +8,6 @@ spec:
values:
services:
backend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings:contour_ce9ed6d7
envs:
- name: MARKS_APP__LOG_LEVEL
value:
@ -37,6 +33,8 @@ spec:
value:
_default: "true"
# Временно: в образе нет CA УГМК для s3.uralmine.com (INFRA-3034).
# Вернуть "true" после монтирования CA и AWS_CA_BUNDLE.
- name: MARKS_S3__SSL_VERIFY
value:
_default: "false"

View File

@ -36,7 +36,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_358ca69c
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations:prod_98a23ef5
pullPolicy:
_default: IfNotPresent
@ -135,10 +135,6 @@ spec:
value:
_default: "20"
- name: CDE_API_ENABLED
value:
_default: "false"
- name: ZITADEL_ACCOUNT
value:
_default: "/etc/sarex/zitadel/zitadel-account.json"

View File

@ -36,7 +36,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations-api-files:prod_fef3add2
_default: cr.yandex/crp3ccidau046kdj8g9q/documentations-api-files:prod_3f19a21d
pullPolicy:
_default: IfNotPresent
@ -138,10 +138,6 @@ spec:
value:
_default: "0.0.0.0:8080"
- name: CDE_API_ENABLED
value:
_default: "false"
- name: API_ADDRESS_FILE
value:
_default: "0.0.0.0:8080"

View File

@ -36,7 +36,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_9dea5b64
_default: cr.yandex/crp3ccidau046kdj8g9q/pdf-markings-amqp:prod_3ab263be
pullPolicy:
_default: IfNotPresent

View File

@ -8,9 +8,6 @@ spec:
values:
services:
backend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/eav:prod_0dcebba6@sha256:2630ad45e4704881ac834f48a70ea837fa46620a5339c3e462282fd16399cc9c
deployment:
resources:
requests:

View File

@ -18,7 +18,7 @@ data:
# DEBUG SETTINGS START
# ---------------------------------------------------------------------------------------------------------------------
DEBUG = False
DEBUG = True
ALLOWED_HOSTS = ['*']
# ---------------------------------------------------------------------------------------------------------------------
# DEBUG SETTINGS END

View File

@ -9,8 +9,6 @@ spec:
services:
backend:
deployment:
replicaCount:
_default: 2
args:
_default:
- |

View File

@ -8,9 +8,6 @@ spec:
values:
services:
backend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/iams:contour_29754513@sha256:a12cf64b59da50cf99011ee4fc8559e61af637887265f1377a448ee321de2250
envs:
- name: ENVIRONMENT
value:

View File

@ -1,5 +1,33 @@
---
# ВНИМАНИЕ: data["production.py"] — одна строка, kustomize заменяет её ЦЕЛИКОМ,
# поэтому здесь продублировано всё содержимое из
# apps/issues/base/production-configmap.yaml (та же ловушка, что с envs).
#
# Зачем нужен этот патч:
# production.py делает `from .base import *` (там всё через os.getenv), а затем
# переприсваивает часть значений ХАРДКОДОМ — поэтому для этих переменных
# ConfigMap ПОБЕЖДАЕТ env из HelmRelease, и правка env в оверлее
# backend-s3.yaml/celery-s3.yaml сама по себе ни на что не влияет.
#
# INFRA-3005 (основная причина зависания экспорта в Excel):
# в base хардкодом WORKFLOWS_HOST/WORKFLOWS_URL = workflows-api-service.workflow
# — такого сервиса в УГМК нет. celery собирает xlsx, затем
# ExportFinishedNotification → issues/mailing.py:122 делает POST на
# {WORKFLOWS_HOST}/internal/v1/companies/{id}/workflows и падает с
# ConnectionError (Name or service not known) — задача фейлится, фронт не
# получает завершения и крутит загрузку бесконечно. Тем же путём с 26.08
# падали ВСЕ уведомления (send_notification_on_create/on_update/
# to_new_responsible/on_bulk_update, ~1770 ошибок).
#
# Отличия от base:
# - WORKFLOWS_HOST / WORKFLOWS_URL → backend-svc.processing (через os.getenv,
# чтобы env из оверлея снова начал работать)
# - SAREX_API → https://sarex.uralmine.com (в base зашит https://srx.wb.ru)
# - CORS/CSRF origins → https://sarex.uralmine.com (в base только lk.srx.wb.ru)
# - убраны хардкоды KAFKA_USERNAME/KAFKA_PASSWORD/DJANGO_TOKEN и чужой
# дефолт KAFKA_HOST: в УГМК всё это приходит из Vault
# (см. podAnnotations в backend-s3.yaml / celery-s3.yaml / kafka-app.yaml)
# - BASE_AERO_URL оставлен внутренним — он server-to-server
apiVersion: v1
kind: ConfigMap
metadata:
@ -118,9 +146,9 @@ data:
CORS_ALLOW_ALL_ORIGINS = True
ENABLE_MAILGUN=False
SMTP_PORT=587
SMTP_HOST="post.uralmine.com"
EMAIL_FROM="sarex@uralmine.com"
SMTP_PORT=465
SMTP_HOST="mail.rwb.ru"
EMAIL_FROM="sarex@rwb.ru"
CORS_ALLOW_METHODS = [
"DELETE",

View File

@ -12,6 +12,7 @@ data:
# DEBUG SETTINGS START
# -----------------------------------------------------------------------------
DEBUG = True
# -----------------------------------------------------------------------------
TEST_MODE = False
@ -39,7 +40,7 @@ data:
# DEBUG SETTINGS START
# -----------------------------------------------------------------------------
DEBUG = False
DEBUG = True
# -----------------------------------------------------------------------------
REVIEW_HOST='http://backend-service.flows.svc.cluster.local:8000'

View File

@ -1,5 +1,11 @@
---
# Патч backend для vad.
#
# ВНИМАНИЕ: args — список, kustomize заменяет его ЦЕЛИКОМ (JSON merge patch),
# поэтому здесь продублирован весь скрипт из apps/pm/base/backend.yaml.
# При правке args в base их нужно продублировать и сюда.
# podAnnotations — map, мержится по ключам, поэтому переопределён только
# один ключ agent-inject-template-pm-s3; остальные vault-аннотации берутся из base.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -9,15 +15,16 @@ spec:
values:
services:
backend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pm-backend:production_fa0f1551
deployment:
resources:
requests:
cpu: null
memory: null
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/pm-backend:production_fa0f1551
deployment:
args:
_default:
- |
@ -27,6 +34,7 @@ spec:
[ -f /vault/secrets/pm-s3 ] && . /vault/secrets/pm-s3
[ -f /vault/secrets/pm-kafka ] && . /vault/secrets/pm-kafka
set +a
python manage.py makemigrations pm --noinput
python manage.py migrate
exec /opt/sarex/entrypoint.sh
envs:
@ -54,10 +62,6 @@ spec:
value:
_default: "/api/v1"
- name: SETTINGS_VERIFY_SSL
value:
_default: "false"
- name: TRACING_INSECURE
value:
_default: "False"

View File

@ -15,6 +15,11 @@ spec:
values:
services:
celery:
deployment:
resources:
requests:
cpu: null
memory: null
# Образ общий с backend — держать в одной версии обязательно,
# иначе воркер разъедется с API по схеме задач и моделям.
image:
@ -24,10 +29,6 @@ spec:
# base не переопределяет args, а нам нужно добавить sourcing pm-kafka —
# дублируем скрипт целиком (то же самое, что и в backend.yaml).
deployment:
resources:
requests:
cpu: null
memory: null
args:
_default:
- |

View File

@ -147,28 +147,6 @@ spec:
value:
_default: "/api/v1"
- name: KAFKA_ENABLE
value:
_default: "True"
- name: KAFKA_BOOTSTRAP_SERVERS
value:
_default: '["wb-prod-kafka-bootstrap.kafka.svc.cluster.local:9093"]'
- name: KAFKA_SECURITY_PROTOCOL
value:
_default: "SSL"
- name: KAFKA_SSL_CAFILE
value:
_default: "/usr/local/share/ca-certificates/kafka.crt"
# Обязан совпадать с SETTINGS_TOPICS["planning"] в
# apps/message-hub/wb/message-hub.yaml
- name: KAFKA_TOPICS
value:
_default: '{"planning": "message-hub-prod"}'
commitSha: ""
gitlabUri: ""
gitlabJobUrl: ""

View File

@ -141,26 +141,6 @@ spec:
value:
_default: "/api/v1"
- name: KAFKA_ENABLE
value:
_default: "True"
- name: KAFKA_BOOTSTRAP_SERVERS
value:
_default: '["wb-prod-kafka-bootstrap.kafka.svc.cluster.local:9093"]'
- name: KAFKA_SECURITY_PROTOCOL
value:
_default: "SSL"
- name: KAFKA_SSL_CAFILE
value:
_default: "/usr/local/share/ca-certificates/kafka.crt"
- name: KAFKA_TOPICS
value:
_default: '{"planning": "message-hub-prod"}'
commitSha: ""
gitlabUri: ""
gitlabJobUrl: ""

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- postgresql.yaml
- notes-tls.yaml

View File

@ -1,5 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: postgres-tls-test

View File

@ -1,119 +0,0 @@
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: notes-tls
namespace: postgres-tls-test
spec:
interval: 10m
dependsOn:
- name: pg-tls
chart:
spec:
chart: universal-chart
version: "0.1.9"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
global:
env: _default
services:
backend:
enabled: true
deployment:
enabled: true
name:
_default: notes-tls
replicaCount:
_default: 1
port:
_default: 8000
resources:
requests:
cpu:
_default: 25m
memory:
_default: 128Mi
probes:
liveness:
enabled: false
readiness:
enabled: false
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/notes-backend:preprod_c00faf6e
pullPolicy:
_default: IfNotPresent
service:
enabled: true
name:
_default: notes-tls
type:
_default: ClusterIP
port:
_default: 8000
targetPort:
_default: 8000
portName:
_default: http
imagePullSecrets:
enabled:
_default: true
name:
_default: regcred
envs:
- name: PG_HOST
value:
_default: pg-tls.postgres-tls-test.svc.cluster.local
- name: PG_PORT
value:
_default: "5432"
- name: PG_DB
value:
_default: notes
- name: PG_LOGIN
value:
_default: notes
- name: PG_SSL
value:
_default: "true"
- name: PG_SSL_MODE
value:
_default: verify-full
- name: PGSSLMODE
value:
_default: verify-full
- name: PGSSLROOTCERT
value:
_default: /etc/pg-ca/ca.crt
- name: DJANGO_USE
value:
_default: "false"
- name: TIMEOUT
value:
_default: "120"
secretEnvs:
- name: PG_PASSWORD
secretName:
_default: pg-tls
secretKey:
_default: password
volumes:
_default:
- name: pg-ca
mountPath: /etc/pg-ca
readOnly: true
secret:
secretName: pg-tls-crt
items:
- key: ca.crt
path: ca.crt

View File

@ -1,88 +0,0 @@
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: pg-tls
namespace: postgres-tls-test
spec:
interval: 10m
timeout: 15m
chart:
spec:
chart: postgresql-contour
version: "17.0.7"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
fullnameOverride: pg-tls
global:
security:
allowInsecureImages: true
defaultStorageClass: local-path
imagePullSecrets:
- regcred
image:
registry: cr.yandex/crp3ccidau046kdj8g9q
repository: contour/postgresql
tag: 17.0.7
auth:
username: notes
database: notes
metrics:
enabled: false
tls:
enabled: true
autoGenerated: true
primary:
persistence:
storageClass: local-path
size: 1Gi
nodeSelector:
dedicated: generic
containerSecurityContext:
readOnlyRootFilesystem: false
args:
- postgres
- -c
- ssl=on
- -c
- ssl_cert_file=/opt/bitnami/postgresql/certs/tls.crt
- -c
- ssl_key_file=/opt/bitnami/postgresql/certs/tls.key
- -c
- ssl_min_protocol_version=TLSv1.2
- -c
- hba_file=/bitnami/postgresql/conf/pg_hba.conf
customLivenessProbe:
exec:
command:
- /bin/sh
- -c
- exec pg_isready -U notes -d notes -h 127.0.0.1 -p 5432
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
customReadinessProbe:
exec:
command:
- /bin/sh
- -c
- exec pg_isready -U notes -d notes -h 127.0.0.1 -p 5432
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
pgHbaConfiguration: |
local all all scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256
hostssl all all ::/0 scram-sha-256

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/prescriptions-frontend:production_d48699e6@sha256:1e6925d1a41d91878013eb02e4463c30efa0479c25260575ad4fd3ee5471575d
deployment:
resources:
requests:

View File

@ -42,7 +42,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/workflows-engine:contour_835b57c1
_default: cr.yandex/crp3ccidau046kdj8g9q/workflows-engine:contour_89322f69
pullPolicy:
_default: IfNotPresent
@ -132,20 +132,6 @@ spec:
value:
_default: "0.0.0.0:8000"
- name: ENABLE_CROSS_SECTION_API_DB
value:
_default: "1"
- name: CROSS_SECTION_API_DB
value:
_default: "/etc/cross_section/cross-section-api-db-prod.json"
- name: CROSS_SECTION_API_DEBUG
value:
_default: "0"
- name: IGNORE_RESOURCES
value:
_default: "True"
- name: DJANGO_HOST
value:
_default: "http://backend.django.svc.cluster.local:8000"

View File

@ -8,9 +8,6 @@ spec:
values:
services:
backend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/workflows-engine:contour_7d2cd329
deployment:
resources:
requests:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/project-frontend-app:wb_fd49eb19@sha256:09968df4919be603200ccf39662bab15efdc1db902459abf912c6c533c896a11
deployment:
resources:
requests:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/remarks-frontend:contour_013c5091@sha256:05b8667b934ac785a48a41b20fcbd7e4e5b6f377f29a6e47add9c23c474a42c7
deployment:
resources:
requests:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/reviews-frontend:contour_c5a6d3b4@sha256:4333dbd7a7a68dcf4465563d8e079ee29fd52750c2bc841f92e745fb19e84591
deployment:
resources:
requests:

View File

@ -29,7 +29,7 @@ spec:
enabled: true
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/workspaces-v2-frontend:contour_2da2a00e
_default: cr.yandex/crp3ccidau046kdj8g9q/workspaces-v2-frontend:contour_a72e3265
pullPolicy:
_default: IfNotPresent
deployment:

View File

@ -8,9 +8,6 @@ spec:
values:
services:
frontend:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/workspaces-v2-frontend:contour_1b5d64e4
deployment:
replicaCount:
_default: 1

View File

@ -36,7 +36,7 @@ spec:
image:
name:
_default: 10.4.10.187:80/library/workspaces-v2-frontend:contour_1b5d64e4
_default: 10.4.10.187:80/library/workspaces-v2-frontend:contour_07373a45
pullPolicy:
_default: IfNotPresent

View File

@ -1,163 +0,0 @@
#!/usr/bin/env python3
import argparse
import io
import subprocess
import sys
import tarfile
from pathlib import Path, PurePosixPath
import yaml
FLUX_KUSTOMIZATION = "kustomize.toolkit.fluxcd.io"
KUSTOMIZATION_FILES = ("kustomization.yaml", "kustomization.yml", "Kustomization")
class SliceError(Exception):
pass
def is_remote(ref: str) -> bool:
return "://" in ref or ref.startswith(("git@", "github.com/", "gitlab.com/"))
def kustomization_file(directory: Path) -> Path | None:
for name in KUSTOMIZATION_FILES:
candidate = directory / name
if candidate.is_file():
return candidate
return None
def local_refs(doc: dict) -> list[str]:
refs = []
refs += doc.get("resources") or []
refs += doc.get("components") or []
refs += doc.get("patchesStrategicMerge") or []
refs += [p["path"] for p in doc.get("patches") or [] if isinstance(p, dict) and "path" in p]
for key in ("configMapGenerator", "secretGenerator"):
for gen in doc.get(key) or []:
for entry in (gen.get("files") or []) + (gen.get("envs") or []):
refs.append(entry.split("=", 1)[-1])
if gen.get("env"):
refs.append(gen["env"])
return [r for r in refs if isinstance(r, str)]
def flux_paths(path: Path) -> list[str]:
try:
docs = list(yaml.safe_load_all(path.read_text()))
except yaml.YAMLError:
return []
return [
d["spec"]["path"]
for d in docs
if isinstance(d, dict)
and str(d.get("apiVersion", "")).startswith(FLUX_KUSTOMIZATION)
and d.get("kind") == "Kustomization"
and d.get("spec", {}).get("path")
]
def collect(repo: Path, start: str) -> tuple[set[PurePosixPath], list[str]]:
repo = repo.resolve()
files: set[PurePosixPath] = set()
entries: list[str] = []
seen_dirs: set[Path] = set()
def rel(p: Path) -> PurePosixPath:
try:
return PurePosixPath(p.resolve().relative_to(repo).as_posix())
except ValueError:
raise SliceError(f"reference outside the repository: {p}")
def add_file(p: Path):
r = rel(p)
if r in files:
return
files.add(r)
if p.suffix in (".yaml", ".yml"):
for fp in flux_paths(p):
visit_dir(repo / fp, entry=True)
def visit_dir(d: Path, entry: bool = False):
d = d.resolve()
if d in seen_dirs:
return
seen_dirs.add(d)
kfile = kustomization_file(d)
if kfile is None:
raise SliceError(f"no kustomization.yaml in {rel(d)}")
if entry:
entries.append(str(rel(d)))
add_file(kfile)
doc = yaml.safe_load(kfile.read_text()) or {}
for ref in local_refs(doc):
if is_remote(ref):
raise SliceError(f"remote reference {ref} in {rel(kfile)}")
target = (d / ref)
if target.is_dir():
visit_dir(target)
elif target.is_file():
add_file(target)
else:
raise SliceError(f"reference {ref} in {rel(kfile)} does not exist")
visit_dir(repo / start, entry=True)
return files, sorted(set(entries))
def write_tar(repo: Path, files: set[PurePosixPath], out: Path):
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w", format=tarfile.PAX_FORMAT) as tar:
for f in sorted(files):
data = (repo / f).read_bytes()
info = tarfile.TarInfo(str(f))
info.size = len(data)
info.mode = 0o644
info.mtime = 0
info.uid = info.gid = 0
info.uname = info.gname = ""
tar.addfile(info, io.BytesIO(data))
out.write_bytes(buf.getvalue())
def kustomize_build(path: Path) -> bytes:
p = subprocess.run(["kubectl", "kustomize", str(path)], capture_output=True)
if p.returncode != 0:
raise SliceError(f"kustomize build {path} failed: {p.stderr.decode().strip()}")
return p.stdout
def verify(repo: Path, out: Path, entries: list[str]):
import tempfile
with tempfile.TemporaryDirectory() as tmp:
with tarfile.open(out) as tar:
tar.extractall(tmp, filter="data")
for e in entries:
if kustomize_build(repo / e) != kustomize_build(Path(tmp) / e):
raise SliceError(f"kustomize build of {e} differs between slice and full repository")
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("contour")
ap.add_argument("--repo", default=".")
ap.add_argument("--out", required=True)
ap.add_argument("--no-verify", action="store_true")
args = ap.parse_args()
repo = Path(args.repo).resolve()
out = Path(args.out)
try:
files, entries = collect(repo, f"clusters/{args.contour}")
write_tar(repo, files, out)
if not args.no_verify:
verify(repo, out, entries)
except SliceError as e:
print(f"slice {args.contour}: {e}", file=sys.stderr)
return 1
print(f"slice {args.contour}: {len(files)} files, entry points: {', '.join(entries)}")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,217 +0,0 @@
#!/usr/bin/env python3
import argparse
import base64
import hashlib
import importlib.util
import json
import re
import sys
import tempfile
import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
import yaml
spec = importlib.util.spec_from_file_location("build_slice", Path(__file__).with_name("build-slice.py"))
bs = importlib.util.module_from_spec(spec)
spec.loader.exec_module(bs)
REGISTRY_HOST = "cr.yandex"
SLICE_REGISTRY = "crpct2b2lsmao4nm0cjk"
CONFIG_TYPE = "application/vnd.sarex.slice.config.v1+json"
LAYER_TYPE = "application/vnd.oci.image.layer.v1.tar"
MANIFEST_TYPE = "application/vnd.oci.image.manifest.v1+json"
JOB_IMAGE = "${CR}/infra/slice-builder:1.0.0"
class PublishError(Exception):
pass
def digest(data: bytes) -> str:
return "sha256:" + hashlib.sha256(data).hexdigest()
def list_contours(repo: Path) -> list[str]:
return sorted(p.parent.name for p in repo.glob("clusters/*/kustomization.yaml"))
def child_pipeline(contours: list[str], release: str, commit: str) -> str:
jobs: dict = {"stages": ["slices"]}
for c in contours:
jobs[f"slice {c}"] = {
"stage": "slices",
"image": JOB_IMAGE,
"tags": ["build"],
"allow_failure": True,
"variables": {"GIT_DEPTH": "1"},
"script": [
"printf '%s' \"$SLICES_PUSHER_KEY\" | base64 -d > /tmp/slices-key.json",
f"python3 ci/publish-slice.py publish --contour {c} --release {release} --commit {commit} --key-file /tmp/slices-key.json",
],
"after_script": ["rm -f /tmp/slices-key.json"],
}
if not contours:
jobs["no contours"] = {"stage": "slices", "image": JOB_IMAGE, "tags": ["build"], "script": ["echo 'no clusters/*/kustomization.yaml found'"]}
return yaml.safe_dump(jobs, sort_keys=False, allow_unicode=False)
def build_manifest(layer: bytes, annotations: dict[str, str]) -> tuple[bytes, bytes]:
config = b"{}"
manifest = {
"schemaVersion": 2,
"mediaType": MANIFEST_TYPE,
"config": {"mediaType": CONFIG_TYPE, "digest": digest(config), "size": len(config)},
"layers": [{
"mediaType": LAYER_TYPE,
"digest": digest(layer),
"size": len(layer),
"annotations": {"org.opencontainers.image.title": "slice.tar"},
}],
"annotations": dict(sorted(annotations.items())),
}
return config, json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()
class Registry:
def __init__(self, username: str, password: str):
self.base = f"https://{REGISTRY_HOST}"
self.basic = "Basic " + base64.b64encode(f"{username}:{password}".encode()).decode()
self.tokens: dict[str, str] = {}
def _send(self, method, url, data=None, headers=None):
req = urllib.request.Request(url, data=data, method=method, headers=headers or {})
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, dict(r.headers), r.read()
except urllib.error.HTTPError as e:
return e.code, dict(e.headers), e.read()
def _token(self, challenge: str, scope: str) -> str:
params = dict(re.findall(r'(\w+)="([^"]*)"', challenge))
realm = params.pop("realm")
params["scope"] = scope
status, _, body = self._send("GET", f"{realm}?{urllib.parse.urlencode(params)}", headers={"Authorization": self.basic})
if status != 200:
raise PublishError(f"registry auth failed: HTTP {status}")
payload = json.loads(body)
return payload.get("token") or payload.get("access_token")
def request(self, method, repo, path, data=None, headers=None):
scope = f"repository:{repo}:pull,push"
url = path if path.startswith("http") else f"{self.base}/v2/{repo}/{path}"
headers = dict(headers or {})
if scope in self.tokens:
headers["Authorization"] = f"Bearer {self.tokens[scope]}"
status, h, body = self._send(method, url, data, headers)
if status == 401:
challenge = {k.lower(): v for k, v in h.items()}.get("www-authenticate", "")
self.tokens[scope] = self._token(challenge, scope)
headers["Authorization"] = f"Bearer {self.tokens[scope]}"
status, h, body = self._send(method, url, data, headers)
return status, {k.lower(): v for k, v in h.items()}, body
def current_layer(self, repo: str) -> str | None:
status, _, body = self.request("GET", repo, "manifests/current", headers={"Accept": MANIFEST_TYPE})
if status == 404:
return None
if status != 200:
raise PublishError(f"{repo}:current: HTTP {status}")
return json.loads(body)["layers"][0]["digest"]
def put_blob(self, repo: str, data: bytes) -> bool:
d = digest(data)
if self.request("HEAD", repo, f"blobs/{d}")[0] == 200:
return False
status, h, _ = self.request("POST", repo, "blobs/uploads/")
if status != 202:
raise PublishError(f"blob upload to {repo}: HTTP {status}")
location = h["location"]
if location.startswith("/"):
location = self.base + location
sep = "&" if "?" in location else "?"
status, _, _ = self.request("PUT", repo, f"{location}{sep}digest={d}", data, {"Content-Type": "application/octet-stream"})
if status != 201:
raise PublishError(f"blob upload to {repo}: HTTP {status}")
return True
def push(self, repo: str, tags: list[str], layer: bytes, annotations: dict[str, str]) -> bool:
config, manifest = build_manifest(layer, annotations)
self.put_blob(repo, config)
uploaded = self.put_blob(repo, layer)
for tag in tags:
status, _, body = self.request("PUT", repo, f"manifests/{tag}", manifest, {"Content-Type": MANIFEST_TYPE})
if status != 201:
raise PublishError(f"{repo}:{tag}: HTTP {status} {body[:200]!r}")
return uploaded
def log(msg: str):
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
def publish(repo: Path, contour: str, release: str, commit: str, key_file: Path) -> int:
target = f"{SLICE_REGISTRY}/{contour}-iac"
log(f"contour {contour}, source iac@{commit[:8]}, release {release}")
try:
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "slice.tar"
files, entries = bs.collect(repo, f"clusters/{contour}")
log(f"collected {len(files)} files, entry points: {', '.join(entries)}")
bs.write_tar(repo, files, out)
bs.verify(repo, out, entries)
log(f"verified: kustomize build of {len(entries)} entry point(s) identical to full repository")
layer = out.read_bytes()
new = digest(layer)
log(f"slice size {len(layer)} bytes, digest {new}")
registry = Registry("json_key", key_file.read_text())
current = registry.current_layer(f"{target}")
changed = current != new
log(f"registry {REGISTRY_HOST}/{target}, current " + (current or "absent") + (", content changed" if changed else ", content unchanged"))
tags = [commit[:8], release] + (["current"] if changed else [])
uploaded = registry.push(target, tags, layer, {
"org.opencontainers.image.source": "iac",
"org.opencontainers.image.revision": commit,
"org.opencontainers.image.version": release,
"org.opencontainers.image.created": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"io.sarex.slice.contour": contour,
"io.sarex.slice.kind": "iac",
})
log(f"layer {'uploaded' if uploaded else 'already in registry'}, tags pushed: {', '.join(tags)}")
except (bs.SliceError, PublishError) as e:
log(f"FAILED: {e}")
return 1
log("done")
return 0
def main() -> int:
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
gen = sub.add_parser("generate")
gen.add_argument("--repo", default=".")
gen.add_argument("--release", required=True)
gen.add_argument("--commit", required=True)
gen.add_argument("--out", required=True)
pub = sub.add_parser("publish")
pub.add_argument("--repo", default=".")
pub.add_argument("--contour", required=True)
pub.add_argument("--release", required=True)
pub.add_argument("--commit", required=True)
pub.add_argument("--key-file", required=True)
args = ap.parse_args()
repo = Path(args.repo).resolve()
if args.cmd == "generate":
contours = list_contours(repo)
Path(args.out).write_text(child_pipeline(contours, args.release, args.commit))
log(f"{len(contours)} contours: {', '.join(contours)}")
return 0
return publish(repo, args.contour, args.release, args.commit, Path(args.key_file))
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,93 +0,0 @@
import importlib.util
import tarfile
from pathlib import Path
import pytest
import yaml
spec = importlib.util.spec_from_file_location("build_slice", Path(__file__).parents[1] / "build-slice.py")
bs = importlib.util.module_from_spec(spec)
spec.loader.exec_module(bs)
def put(root: Path, rel: str, text: str):
p = root / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(text)
@pytest.fixture
def repo(tmp_path):
put(tmp_path, "clusters/a/kustomization.yaml", "resources:\n - ./flux-system\n - ./layers.yaml\n")
put(tmp_path, "clusters/a/flux-system/kustomization.yaml", "resources:\n - gotk-sync.yaml\n")
put(tmp_path, "clusters/a/flux-system/gotk-sync.yaml",
"apiVersion: kustomize.toolkit.fluxcd.io/v1\nkind: Kustomization\nspec:\n path: ./clusters/a\n")
put(tmp_path, "clusters/a/layers.yaml",
"apiVersion: kustomize.toolkit.fluxcd.io/v1\nkind: Kustomization\nspec:\n path: ./layers/a/00-vault\n")
put(tmp_path, "layers/a/00-vault/kustomization.yaml", "resources:\n - ../../../infrastructure/vault/contour-template\n")
put(tmp_path, "infrastructure/vault/contour-template/kustomization.yaml",
"resources:\n - ../base\npatches:\n - path: vault.yaml\n")
put(tmp_path, "infrastructure/vault/contour-template/vault.yaml", "kind: HelmRelease\n")
put(tmp_path, "infrastructure/vault/base/kustomization.yaml", "resources:\n - helmrelease.yaml\n")
put(tmp_path, "infrastructure/vault/base/helmrelease.yaml", "kind: HelmRelease\n")
put(tmp_path, "infrastructure/vault/base/unused.yaml", "kind: ConfigMap\n")
put(tmp_path, "infrastructure/vault/uralkal/kustomization.yaml", "resources:\n - ../base\n")
put(tmp_path, "clusters/b/kustomization.yaml", "resources:\n - ../../infrastructure/vault/uralkal\n")
return tmp_path
def test_slice_follows_flux_paths_and_skips_foreign(repo):
files, entries = bs.collect(repo, "clusters/a")
names = {str(f) for f in files}
assert "infrastructure/vault/base/helmrelease.yaml" in names
assert "infrastructure/vault/contour-template/vault.yaml" in names
assert "layers/a/00-vault/kustomization.yaml" in names
assert not any("uralkal" in n or n.startswith("clusters/b") for n in names)
assert "infrastructure/vault/base/unused.yaml" not in names
assert entries == ["clusters/a", "layers/a/00-vault"]
def test_remote_reference_stops_build(repo):
put(repo, "clusters/a/kustomization.yaml", "resources:\n - https://github.com/x/y//z\n")
with pytest.raises(bs.SliceError, match="remote reference"):
bs.collect(repo, "clusters/a")
def test_dangling_reference_stops_build(repo):
put(repo, "clusters/a/kustomization.yaml", "resources:\n - ./missing.yaml\n")
with pytest.raises(bs.SliceError, match="does not exist"):
bs.collect(repo, "clusters/a")
def test_tar_is_byte_identical_across_runs(repo, tmp_path_factory):
files, _ = bs.collect(repo, "clusters/a")
out1 = tmp_path_factory.mktemp("o") / "s.tar"
out2 = tmp_path_factory.mktemp("o") / "s.tar"
bs.write_tar(repo, files, out1)
(repo / "clusters/a/layers.yaml").touch()
bs.write_tar(repo, files, out2)
assert out1.read_bytes() == out2.read_bytes()
with tarfile.open(out1) as t:
assert all(m.mtime == 0 for m in t.getmembers())
pspec = importlib.util.spec_from_file_location("publish_slice", Path(__file__).parents[1] / "publish-slice.py")
ps = importlib.util.module_from_spec(pspec)
pspec.loader.exec_module(ps)
def test_every_cluster_with_kustomization_is_a_contour(tmp_path):
put(tmp_path, "clusters/stand/kustomization.yaml", "resources: []\n")
put(tmp_path, "clusters/vad/kustomization.yaml", "resources: []\n")
put(tmp_path, "clusters/contour/apps.yaml", "kind: Kustomization\n")
assert ps.list_contours(tmp_path) == ["stand", "vad"]
def test_child_pipeline_has_one_job_per_contour():
doc = yaml.safe_load(ps.child_pipeline(["stand", "vad"], "3.9.68", "abcdef1234"))
jobs = {k: v for k, v in doc.items() if k != "stages"}
assert sorted(jobs) == ["slice stand", "slice vad"]
assert all(j["allow_failure"] for j in jobs.values())
assert "--contour vad --release 3.9.68 --commit abcdef1234" in jobs["slice vad"]["script"][1]
assert all(ord(ch) < 128 for ch in ps.child_pipeline(["stand"], "1.0.0", "a" * 40))

View File

@ -1,14 +0,0 @@
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: yc-oci-charts
namespace: flux-system
spec:
type: oci
interval: 10m0s
url: ${CHART_REPO:=oci://cr.yandex/crp3ccidau046kdj8g9q/charts}
secretRef:
name: ${CHART_REPO_SECRET:=yc-cr-auth}
certSecretRef:
name: chart-repo-ca

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helm-repositories.yaml
- ../../../infrastructure/vault/contour-template

View File

@ -1,7 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../infrastructure/istio-base/contour-template
- ../../../infrastructure/istio-pilot/contour-template
- ../../../infrastructure/istio-gateway/contour-template
- ../../../infrastructure/cert-manager/contour-template

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../infrastructure/postgresql/contour-template
- ../../../infrastructure/kafka/contour-template
- ../../../infrastructure/rabbitmq/contour-template

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../infrastructure/zitadel/contour-template
- ../../../infrastructure/camunda/contour-template

View File

@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../infrastructure/istio-config/contour-template

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m0s
ref:
branch: main
url: http://gitea.gitea.svc:3000/infra/iac.git
secretRef:
name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 10m0s
path: ./clusters/stand
prune: true
sourceRef:
kind: GitRepository
name: flux-system

View File

@ -1,13 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- target:
kind: Deployment
name: kustomize-controller
patch: |-
- op: add
path: /spec/template/spec/containers/0/args/-
value: --feature-gates=CancelHealthCheckOnNewRevision=true

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./flux-system
- ./layers.yaml

View File

@ -1,93 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 00-vault
namespace: flux-system
spec:
interval: 10m0s
timeout: 10m0s
path: ./clusters/stand/00-vault
prune: true
sourceRef:
kind: GitRepository
name: flux-system
postBuild:
substituteFrom:
- kind: ConfigMap
name: contour-vars
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 10-mesh
namespace: flux-system
spec:
interval: 10m0s
timeout: 15m0s
path: ./clusters/stand/10-mesh
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
postBuild:
substituteFrom:
- kind: ConfigMap
name: contour-vars
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 20-data
namespace: flux-system
spec:
interval: 10m0s
timeout: 20m0s
path: ./clusters/stand/20-data
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
postBuild:
substituteFrom:
- kind: ConfigMap
name: contour-vars
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 30-platform
namespace: flux-system
spec:
interval: 10m0s
timeout: 35m0s
path: ./clusters/stand/30-platform
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
postBuild:
substituteFrom:
- kind: ConfigMap
name: contour-vars
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 40-routing
namespace: flux-system
spec:
interval: 10m0s
timeout: 10m0s
path: ./clusters/stand/40-routing
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
postBuild:
substituteFrom:
- kind: ConfigMap
name: contour-vars

View File

@ -54,32 +54,4 @@ resources:
- ../../apps/pm/uralkal
- ../../apps/message-hub/uralkal
- ../../apps/cde/uralkal
- ../../apps/postgres-tls-test/uralkal
- ../../infrastructure/istio-mtls-check/base
patches:
- target:
kind: HelmRelease
patch: |
- op: add
path: /spec/driftDetection
value:
mode: enabled
ignore:
- paths: ["/webhooks"]
target:
kind: MutatingWebhookConfiguration
- paths: ["/webhooks"]
target:
kind: ValidatingWebhookConfiguration
- target:
kind: HelmRelease
name: istiod
patch: |
- op: add
path: /spec/driftDetection/ignore/-
value:
paths: ["/spec/replicas"]
target:
kind: Deployment

View File

@ -12,4 +12,3 @@ resources:
- ../../../infrastructure/kb/vk-infra
- ../../../infrastructure/gitlab-runner/vk-infra
- ../../../infrastructure/nexus/vk-infra
- ../../../infrastructure/outline/vk-infra

View File

@ -1,354 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: camunda
namespace: camunda
spec:
dependsOn: []
interval: 5m
timeout: 30m
waitStrategy:
name: legacy
postRenderers:
- kustomize:
patches:
- target:
group: apps
version: v1
kind: Deployment
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: StatefulSet
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: batch
version: v1
kind: Job
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-connectors
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-identity
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-operate
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-optimize
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-tasklist
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: Deployment
name: camunda-zeebe-gateway
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
- target:
group: apps
version: v1
kind: StatefulSet
name: camunda-zeebe
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: processing
- op: add
path: /spec/template/spec/tolerations
value:
- key: dedicated
operator: Equal
value: processing
effect: NoSchedule
values:
global:
vault:
enabled: true
role: camunda-contour
authPath: auth/kubernetes
secrets:
postgresql:
path: secrets/data/camunda/identity-db
keys:
password: password
postgresPassword: password
image:
pullSecrets:
- name: regcred
identity:
auth:
publicIssuerUrl: "https://${HOST_CAMUNDA_KEYCLOAK}/auth/realms/camunda-platform"
identity:
redirectUrl: "https://${HOST_CAMUNDA_IDENTITY}"
operate:
redirectUrl: "https://${HOST_CAMUNDA}"
tasklist:
redirectUrl: "https://${HOST_CAMUNDA_TASKLIST}"
optimize:
redirectUrl: "https://${HOST_CAMUNDA_OPTIMIZE}"
identityPostgresql:
enabled: false
auth:
usePasswordFiles: true
primary:
automountServiceAccountToken: true
persistence:
size: 10Gi
storageClass: ${STORAGE_CLASS}
identityKeycloak:
externalDatabase:
host: pg-host.storage.svc.cluster.local
port: 5432
user: bn_keycloak
database: bitnami_keycloak
password: ""
existingSecret: ""
existingSecretPasswordKey: password
postgresql:
enabled: false
auth:
usePasswordFiles: true
primary:
automountServiceAccountToken: true
persistence:
size: 10Gi
storageClass: ${STORAGE_CLASS}
vaultEnv:
enabled: true
role: camunda-contour
authPath: auth/kubernetes
envFiles:
KEYCLOAK_ADMIN_PASSWORD:
path: secrets/data/camunda/keycloak-admin
key: admin-password
KEYCLOAK_PASSWORD:
path: secrets/data/camunda/keycloak-admin
key: admin-password
KEYCLOAK_DATABASE_PASSWORD:
path: secrets/data/camunda/keycloak-db
key: password
global:
storageClass: ${STORAGE_CLASS}
tolerations: []
elasticsearch:
sysctlImage:
registry: cr.yandex
repository: crp3ccidau046kdj8g9q/contour/camunda/os-shell
tag: 12-debian-12-r32
pullSecrets:
- regcred
master:
replicaCount: 1
podAntiAffinityPreset: soft
persistence:
size: 10Gi
storageClass: ${STORAGE_CLASS}
tolerations: []
metrics:
enabled: false
serviceMonitor:
enabled: false
prometheusRule:
enabled: false
tolerations: []
camundaCanary:
enabled: false
prometheusServiceMonitor:
enabled: false
console:
image:
pullSecrets:
- name: regcred
tolerations: []
zeebe:
clusterSize: "1"
partitionCount: "1"
replicationFactor: "1"
pvcStorageClassName: ${STORAGE_CLASS}
image:
pullSecrets:
- name: regcred
tolerations: []
zeebeGateway:
replicas: 1
image:
pullSecrets:
- name: regcred
tolerations: []
identity:
fullURL: "https://${HOST_CAMUNDA_IDENTITY}"
externalDatabase:
enabled: true
host: pg-host.storage.svc.cluster.local
port: 5432
username: identity
database: identity
password: ""
existingSecret: ""
existingSecretPasswordKey: password
image:
pullSecrets:
- name: regcred
tolerations: []
operate:
image:
pullSecrets:
- name: regcred
serviceAccount:
automountServiceAccountToken: true
tolerations: []
tasklist:
image:
pullSecrets:
- name: regcred
serviceAccount:
automountServiceAccountToken: true
tolerations: []
optimize:
image:
pullSecrets:
- name: regcred
serviceAccount:
automountServiceAccountToken: true
tolerations: []
executionIdentity:
image:
pullSecrets:
- name: regcred
tolerations: []
webModeler:
image:
pullSecrets:
- name: regcred
restapi:
tolerations: []
webapp:
tolerations: []
websockets:
tolerations: []
connectors:
image:
pullSecrets:
- name: regcred
serviceAccount:
automountServiceAccountToken: true
tolerations: []

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: camunda.yaml

View File

@ -213,35 +213,6 @@ spec:
size: 10Gi
storageClass: local-path
identityKeycloak:
extraVolumes:
- name: keytab
secret:
secretName: keycloak-keytab
optional: true
- name: krb5
configMap:
name: keycloak-krb5
- name: uralkali-ca
configMap:
name: uralkali-ca
extraEnvVars:
- name: KC_TRUSTSTORE_PATHS
value: /opt/uralkali-ca/ber-ca1.crt
- name: KC_LOG_LEVEL
value: "INFO,org.keycloak.storage.ldap:debug"
logging:
level: "INFO,org.keycloak.storage.ldap:debug"
extraVolumeMounts:
- name: uralkali-ca
mountPath: /opt/uralkali-ca
readOnly: true
- name: keytab
mountPath: /opt/keytab
readOnly: true
- name: krb5
mountPath: /etc/krb5.conf
subPath: krb5.conf
readOnly: true
externalDatabase:
host: 10.133.0.249
port: 5432

View File

@ -1,25 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: keycloak-krb5
namespace: camunda
data:
krb5.conf: |
[libdefaults]
default_realm = LOCAL.URALKALI.COM
dns_lookup_kdc = false
dns_lookup_realm = false
rdns = false
default_tkt_enctypes = aes256-cts-hmac-sha1-96
default_tgs_enctypes = aes256-cts-hmac-sha1-96
permitted_enctypes = aes256-cts-hmac-sha1-96
[realms]
LOCAL.URALKALI.COM = {
kdc = PERM-GL-DC3.local.uralkali.com
admin_server = PERM-GL-DC3.local.uralkali.com
}
[domain_realm]
.local.uralkali.com = LOCAL.URALKALI.COM
local.uralkali.com = LOCAL.URALKALI.COM

View File

@ -2,14 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- keycloak-krb5.yaml
- uralkali-ca.yaml
patches:
- path: camunda.yaml
secretGenerator:
- name: keycloak-keytab
namespace: camunda
files:
- sarex.keytab
options:
disableNameSuffixHash: true

View File

@ -1,29 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: uralkali-ca
namespace: camunda
data:
ber-ca1.crt: |
-----BEGIN CERTIFICATE-----
MIIDsjCCApqgAwIBAgIQAXfxtRjCJrpJcAhO6t1LBzANBgkqhkiG9w0BAQsFADBY
MRMwEQYKCZImiZPyLGQBGRYDY29tMRgwFgYKCZImiZPyLGQBGRYIdXJhbGthbGkx
FTATBgoJkiaJk/IsZAEZFgVsb2NhbDEQMA4GA1UEAxMHQkVSLUNBMTAeFw0yMDAz
MjMwNTI3MzBaFw0yODAzMjMwNTM3MjlaMFgxEzARBgoJkiaJk/IsZAEZFgNjb20x
GDAWBgoJkiaJk/IsZAEZFgh1cmFsa2FsaTEVMBMGCgmSJomT8ixkARkWBWxvY2Fs
MRAwDgYDVQQDEwdCRVItQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAnVGFDQfYgVNIsQv6uBVKKAudUwnmVq2781uCpDXPuVet84DfjAmYNcNgz45I
qikJPnkqcAtZimkQ8znmb7If2aQs1qPZ/7pSVhJU4Revode8jy2vpYPBXpbIw4pc
2Z8xWSP88hDWkkVcwze8GoFuVxKtikVaj4HCkWYikGpdKDko6f1sCj+K89kPEK11
l/0ij754c6cC5cyGy0XV5CqVpqZcef+aGu3P+835Skii2s+2j/EmmW9uiHQ7y6fl
OBqbn6tA8ofplhCDcfNSW76w1R2b+eH8X2RzJpIYKaVn/dQ6hdFjGzJAeSA4kzYl
XVv7rq2rEc7Bj1L1GgGCWh4puwIDAQABo3gwdjALBgNVHQ8EBAMCAYYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUQ+2UXR7y3Q+vUIQBeS6uWsg0ogMwEgYJKwYB
BAGCNxUBBAUCAwMAAzAjBgkrBgEEAYI3FQIEFgQUd+FmCvlBWdlBdBK1bcGgDzrK
EUcwDQYJKoZIhvcNAQELBQADggEBAFxO66L5YaZqDEhfRk4f6RONDWoEdEOj5SQe
dmn0N92knxBOxMUNv4bpFgCyErZaeZSykoTboNZOOS2MqA3zY8oOJX8wV9kLiqT2
JzYVQZasE7lEUnTqdWbWVtc2LLJp8GWy/L5JeldiEwBhKXgy70v7ZYKH9wPYvvrm
qi23/uKYiDaEu/Z5kJbIaIwZqrxrLzmgUktrzlQf+slBduHewBY9U3xTM3dLWduR
TDMNLkGhIGadYKE4n376FTNDgR23hABZD+EkkSrQberqpahORANE5T4dveW0CEta
9J6ujWVaf5Rn+kFM4YgbvQVpJzM5VP6ZmntO4T+pCRMAuvzFbZs=
-----END CERTIFICATE-----

View File

@ -1,25 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
interval: 5m
timeout: 10m
values:
controller:
image:
registry: cr.yandex/crp3ccidau046kdj8g9q/cert-manager
repository: cert-manager
acmesolver:
image:
registry: cr.yandex/crp3ccidau046kdj8g9q/cert-manager
repository: acmesolver
webhook:
image:
registry: cr.yandex/crp3ccidau046kdj8g9q/cert-manager
repository: cert-manager-webhook
cainjector:
image:
registry: cr.yandex/crp3ccidau046kdj8g9q/cert-manager
repository: cainjector

View File

@ -1,28 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: cert-manager.yaml
- target:
group: cert-manager.io
version: v1
kind: ClusterIssuer
name: letsencrypt-issuer
patch: |-
$patch: delete
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-issuer
- target:
group: cert-manager.io
version: v1
kind: ClusterIssuer
name: letsencrypt-issuer-istio
patch: |-
$patch: delete
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-issuer-istio

View File

@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base

View File

@ -1,117 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: istio-config
namespace: default
spec:
dependsOn: []
interval: 5m
timeout: 10m
values:
global:
env: contour
environments:
contour:
istio:
gateways:
platform:
name: platform-gateway
namespace: default
selector:
istio: ingressgateway
servers:
- hosts:
- ${HOST_LOGIN}
- ${HOST_CAMUNDA}
- ${HOST_CAMUNDA_IDENTITY}
- ${HOST_CAMUNDA_KEYCLOAK}
- ${HOST_CAMUNDA_OPTIMIZE}
- ${HOST_CAMUNDA_TASKLIST}
- ${HOST_RABBITMQ}
tls:
credentialName: istio-ingress-tls
virtualServices:
zitadel:
name: zitadel-virt-service
namespace: default
hosts:
- ${HOST_LOGIN}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: zitadel-idp-contour.zitadel.svc.cluster.local
port: 8080
camunda-operate:
name: camunda-operate-virt-service
namespace: default
hosts:
- ${HOST_CAMUNDA}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: camunda-operate.camunda.svc.cluster.local
port: 80
camunda-identity:
name: camunda-identity-virt-service
namespace: default
hosts:
- ${HOST_CAMUNDA_IDENTITY}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: camunda-identity.camunda.svc.cluster.local
port: 80
camunda-keycloak:
name: camunda-keycloak-virt-service
namespace: default
hosts:
- ${HOST_CAMUNDA_KEYCLOAK}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: camunda-keycloak.camunda.svc.cluster.local
port: 80
camunda-optimize:
name: camunda-optimize-virt-service
namespace: default
hosts:
- ${HOST_CAMUNDA_OPTIMIZE}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: camunda-optimize.camunda.svc.cluster.local
port: 80
camunda-tasklist:
name: camunda-tasklist-virt-service
namespace: default
hosts:
- ${HOST_CAMUNDA_TASKLIST}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: camunda-tasklist.camunda.svc.cluster.local
port: 80
rabbitmq:
name: rabbitmq-virt-service
namespace: default
hosts:
- ${HOST_RABBITMQ}
gateways:
- default/platform-gateway
routes:
- path:
prefix: /
service: rabbitmq.rabbitmq.svc.cluster.local
port: 15672

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: istio-config.yaml

View File

@ -7,15 +7,6 @@ spec:
dependsOn: []
interval: 5m
timeout: 10m
chart:
spec:
chart: istio-config-contour
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
values:
global:
env: uralkal
@ -28,16 +19,6 @@ spec:
namespace: default
selector:
istio: ingressgateway
httpsRedirect: true
tlsOptions:
minProtocolVersion: TLSV1_2
cipherSuites:
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-CHACHA20-POLY1305
- ECDHE-RSA-CHACHA20-POLY1305
servers:
- hosts:
- sarex.local.uralkali.com
@ -56,24 +37,6 @@ spec:
- camunda-tasklist.sarex.local.uralkali.com
tls:
credentialName: uralkal-camunda-tls
envoyFilters:
gateway-hsts:
namespace: istio-system # там же, где поды ingressgateway
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: HTTP_ROUTE
match:
context: GATEWAY
patch:
operation: MERGE
value:
response_headers_to_add:
- header:
key: Strict-Transport-Security
value: "max-age=31536000"
append_action: OVERWRITE_IF_EXISTS_OR_ADD
virtualServices:
platform:
name: sarex-frontend-virt-service
@ -660,8 +623,8 @@ spec:
routes:
- path:
prefix: /
service: minio.uralkal.external
port: 9000
service: s3-proxy.s3-proxy.svc.cluster.local
port: 80
camunda-keycloak:
namespace: default
hosts:

View File

@ -2,6 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- minio-serviceentry.yaml
patches:
- path: istio-config.yaml

View File

@ -1,16 +0,0 @@
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: minio-external
namespace: default
spec:
hosts:
- minio.uralkal.external
location: MESH_EXTERNAL
resolution: STATIC
ports:
- number: 9000
name: http
protocol: HTTP
endpoints:
- address: 10.133.0.245

View File

@ -58,18 +58,6 @@ spec:
renewBefore: 360h0m0s
privateKey:
rotationPolicy: Always
srx-ui-kit-tls:
dnsNames:
- srx-ui-kit.infra.sarex.tech
- srx-ui-kit-stage.infra.sarex.tech
issuerRef:
name: letsencrypt-issuer-istio
kind: ClusterIssuer
spec:
duration: 2160h0m0s
renewBefore: 360h0m0s
privateKey:
rotationPolicy: Always
nexus-tls:
dnsNames:
- nexus.infra.sarex.tech
@ -92,28 +80,6 @@ spec:
renewBefore: 360h0m0s
privateKey:
rotationPolicy: Always
wiki-tls:
dnsNames:
- wiki.sarex.space
issuerRef:
name: letsencrypt-issuer-istio
kind: ClusterIssuer
spec:
duration: 2160h0m0s
renewBefore: 360h0m0s
privateKey:
rotationPolicy: Always
goalert-tls:
dnsNames:
- goalert.infra.sarex.io
issuerRef:
name: letsencrypt-issuer-istio
kind: ClusterIssuer
spec:
duration: 2160h0m0s
renewBefore: 360h0m0s
privateKey:
rotationPolicy: Always
istio:
gateways:
vault:
@ -138,18 +104,6 @@ spec:
tls:
credentialName: kb-tls
mode: SIMPLE
srx-ui-kit:
name: srx-ui-kit-gateway
namespace: istio-system
selector:
istio: ingressgateway
servers:
- hosts:
- srx-ui-kit.infra.sarex.tech
- srx-ui-kit-stage.infra.sarex.tech
tls:
credentialName: srx-ui-kit-tls
mode: SIMPLE
nexus:
name: nexus-gateway
namespace: istio-system
@ -172,28 +126,6 @@ spec:
tls:
credentialName: idp-tls
mode: SIMPLE
wiki:
name: wiki-gateway
namespace: istio-system
selector:
istio: ingressgateway
servers:
- hosts:
- wiki.sarex.space
tls:
credentialName: wiki-tls
mode: SIMPLE
goalert:
name: goalert-gateway
namespace: istio-system
selector:
istio: ingressgateway
servers:
- hosts:
- goalert.infra.sarex.io
tls:
credentialName: goalert-tls
mode: SIMPLE
virtualServices:
vault-virt-service:
namespace: istio-system
@ -217,28 +149,6 @@ spec:
prefix: /
service: kb-service.kb.svc.cluster.local
port: 80
srx-ui-kit-virt-service:
namespace: istio-system
hosts:
- srx-ui-kit.infra.sarex.tech
gateways:
- istio-system/srx-ui-kit-gateway
routes:
- path:
prefix: /
service: universal-static-site-srx-ui-kit-pages.gitlab-pages-srx-ui-kit.svc.cluster.local
port: 80
srx-ui-kit-stage-virt-service:
namespace: istio-system
hosts:
- srx-ui-kit-stage.infra.sarex.tech
gateways:
- istio-system/srx-ui-kit-gateway
routes:
- path:
prefix: /
service: universal-static-site-srx-ui-kit-stage-pages.gitlab-pages-srx-ui-kit-stage.svc.cluster.local
port: 80
nexus-virt-service:
namespace: istio-system
hosts:
@ -261,25 +171,3 @@ spec:
prefix: /
service: idp.zitadel.svc.cluster.local
port: 8080
wiki-virt-service:
namespace: istio-system
hosts:
- wiki.sarex.space
gateways:
- istio-system/wiki-gateway
routes:
- path:
prefix: /
service: outline.outline.svc.cluster.local
port: 80
goalert-virt-service:
namespace: istio-system
hosts:
- goalert.infra.sarex.io
gateways:
- istio-system/goalert-gateway
routes:
- path:
prefix: /
service: goalert.goalert.svc.cluster.local
port: 8081

View File

@ -1,31 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: ingressgateway
namespace: istio-system
spec:
interval: 5m
timeout: 10m
dependsOn:
- name: istio-base
namespace: istio-system
- name: istiod
namespace: istio-system
values:
_internal_defaults_do_not_set:
replicaCount: ${GATEWAY_REPLICAS}
service:
type: ClusterIP
nodeSelector:
sarex.io/gateway: "true"
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
istio: ingressgateway
topologyKey: kubernetes.io/hostname
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: istio-gateway.yaml

View File

@ -1,13 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: mtls-a
labels:
istio-injection: enabled
---
apiVersion: v1
kind: Namespace
metadata:
name: mtls-b
labels:
istio-injection: enabled

View File

@ -1,88 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx
namespace: mtls-b
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: mtls-b
data:
default.conf: "log_format mtls '$remote_addr \"$request\" status=$status '\n \
\ 'xfcc=\"$http_x_forwarded_client_cert\"';\naccess_log /dev/stdout\
\ mtls;\nserver {\n listen 8080;\n server_name _;\n default_type text/plain;\n\
\ location = /health { return 200 \"ok\\n\"; }\n location / {\n return 200\
\ \"backend=nginx-b\\nX-Forwarded-Client-Cert: $http_x_forwarded_client_cert\\\
n\";\n }\n}\n"
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: mtls-b
spec:
selector:
app: nginx
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: mtls-b
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
annotations:
proxy.istio.io/config: "holdApplicationUntilProxyStarts: true\nproxyMetadata:\n\
\ SECRET_TTL: \"10m\"\n"
spec:
serviceAccountName: nginx
containers:
- name: nginx
image: cr.yandex/crp3ccidau046kdj8g9q/nginx:1.27-alpine
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 2
periodSeconds: 3
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: curl
image: cr.yandex/crp3ccidau046kdj8g9q/curl
command:
- /bin/sh
- -c
args:
- while true; do sleep 3600; done
volumes:
- name: config
configMap:
name: nginx-config
---
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: mtls-b
spec:
mtls:
mode: STRICT

View File

@ -1,86 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx
namespace: mtls-a
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: mtls-a
data:
default.conf: "server {\n listen 8080;\n server_name _;\n default_type text/plain;\n\
\ location = /health { return 200 \"ok\\n\"; }\n location / {\n proxy_pass\
\ http://nginx.mtls-b.svc.cluster.local:8080;\n proxy_http_version 1.1;\n \
\ proxy_set_header Connection \"\";\n }\n}\n"
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: mtls-a
spec:
selector:
app: nginx
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: mtls-a
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
annotations:
proxy.istio.io/config: "holdApplicationUntilProxyStarts: true\nproxyMetadata:\n\
\ SECRET_TTL: \"10m\"\n"
spec:
serviceAccountName: nginx
containers:
- name: nginx
image: cr.yandex/crp3ccidau046kdj8g9q/nginx:1.27-alpine
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 2
periodSeconds: 3
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: curl
image: cr.yandex/crp3ccidau046kdj8g9q/curl
command:
- /bin/sh
- -c
args:
- while true; do sleep 3600; done
volumes:
- name: config
configMap:
name: nginx-config
---
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: mtls-a
spec:
mtls:
mode: STRICT

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: plain-client
namespace: mtls-a
labels:
sidecar.istio.io/inject: 'false'
spec:
containers:
- name: curl
image: cr.yandex/crp3ccidau046kdj8g9q/curl
command:
- /bin/sh
- -c
args:
- while true; do sleep 3600; done

View File

@ -1,7 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- 00-namespaces.yaml
- 10-mtls-b.yaml
- 20-mtls-a.yaml
- 30-plain-client.yaml

View File

@ -1,12 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: istiod
namespace: istio-system
spec:
interval: 5m
timeout: 10m
values:
global:
imagePullSecrets:
- regcred

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: istio-pilot.yaml

View File

@ -13,34 +13,3 @@ spec:
global:
imagePullSecrets:
- regcred
# Провайдер журнала доступа для security-audit (access-collector). Сам по себе ничего не включает:
# работает только для workload'ов, на которые ссылается Telemetry (infrastructure/security-audit/mesh).
meshConfig:
extensionProviders:
- name: audit-access
envoyOtelAls:
service: access-collector.security-audit.svc.cluster.local
port: 4317
logFormat:
labels:
sarex_kind: "mesh_access"
start_time: "%START_TIME%"
request_id: "%REQ(X-REQUEST-ID)%"
trace_id: "%TRACE_ID%"
authority: "%REQ(:AUTHORITY)%"
method: "%REQ(:METHOD)%"
path: "%REQ_WITHOUT_QUERY(:PATH)%"
protocol: "%PROTOCOL%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
duration_ms: "%DURATION%"
bytes_in: "%BYTES_RECEIVED%"
bytes_out: "%BYTES_SENT%"
x_forwarded_for: "%REQ(X-FORWARDED-FOR)%"
downstream_ip: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
user_sub: "%REQ(X-SAREX-AUDIT-SUB)%"
user_org: "%REQ(X-SAREX-AUDIT-ORG)%"
user_agent: "%REQ(USER-AGENT)%"
route: "%ROUTE_NAME%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
# Authorization, Cookie, Set-Cookie и query-строка не логируются (п.23)

View File

@ -1,73 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kafka
namespace: kafka
spec:
interval: 5m
timeout: 10m
postRenderers:
- kustomize:
patches:
- target:
group: apps
version: v1
kind: StatefulSet
namespace: kafka
patch: |-
- op: add
path: /spec/template/spec/nodeSelector
value:
dedicated: generic
- op: add
path: /spec/template/spec/tolerations
value: []
values:
global:
imagePullSecrets:
- regcred
defaultStorageClass: ${STORAGE_CLASS}
image:
pullSecrets:
- regcred
controller:
replicaCount: 1
automountServiceAccountToken: true
persistence:
size: 8Gi
storageClass: ${STORAGE_CLASS}
overrideConfiguration:
offsets.topic.replication.factor: 1
transaction.state.log.replication.factor: 1
transaction.state.log.min.isr: 1
default.replication.factor: 1
min.insync.replicas: 1
broker:
replicaCount: 0
automountServiceAccountToken: true
listeners:
client:
protocol: SASL_PLAINTEXT
sslClientAuth: "none"
provisioning:
enabled: false
sasl:
managedExistingSecret:
enabled: false
existingSecret: ""
enabledMechanisms: PLAIN,SCRAM-SHA-512
interBrokerMechanism: PLAIN
controllerMechanism: PLAIN
client:
users: []
passwords: ""
tls:
type: PEM
vault:
enabled: true
role: kafka
authPath: auth/kubernetes
secretPath: secrets/data/kafka/bootstrap
clusterIdKey: clusterId
interBrokerPasswordKey: interBrokerPassword
controllerPasswordKey: controllerPassword

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: kafka.yaml

View File

@ -1,113 +0,0 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: kafka-scram-users
namespace: kafka
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
spec:
backoffLimit: 2
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: kafka
vault.hashicorp.com/agent-inject-secret-admin: secrets/data/kafka/bootstrap
vault.hashicorp.com/agent-inject-template-admin: |
{{- with secret "secrets/data/kafka/bootstrap" -}}{{ index .Data.data "interBrokerPassword" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-pm-user: secrets/data/kafka/apps/pm
vault.hashicorp.com/agent-inject-template-pm-user: |
{{- with secret "secrets/data/kafka/apps/pm" -}}{{ index .Data.data "username" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-pm-pass: secrets/data/kafka/apps/pm
vault.hashicorp.com/agent-inject-template-pm-pass: |
{{- with secret "secrets/data/kafka/apps/pm" -}}{{ index .Data.data "password" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-user: secrets/data/kafka/apps/issues
vault.hashicorp.com/agent-inject-template-issues-user: |
{{- with secret "secrets/data/kafka/apps/issues" -}}{{ index .Data.data "username" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-pass: secrets/data/kafka/apps/issues
vault.hashicorp.com/agent-inject-template-issues-pass: |
{{- with secret "secrets/data/kafka/apps/issues" -}}{{ index .Data.data "password" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-flows-user: secrets/data/kafka/apps/flows
vault.hashicorp.com/agent-inject-template-flows-user: |
{{- with secret "secrets/data/kafka/apps/flows" -}}{{ index .Data.data "username" }}{{- end -}}
vault.hashicorp.com/agent-inject-secret-flows-pass: secrets/data/kafka/apps/flows
vault.hashicorp.com/agent-inject-template-flows-pass: |
{{- with secret "secrets/data/kafka/apps/flows" -}}{{ index .Data.data "password" }}{{- end -}}
spec:
serviceAccountName: kafka-kafka-contour
automountServiceAccountToken: true
restartPolicy: Never
imagePullSecrets:
- name: regcred
nodeSelector:
dedicated: generic
securityContext:
runAsUser: 1001
runAsNonRoot: true
fsGroup: 1001
containers:
- name: kafka-scram-users
image: cr.yandex/crp3ccidau046kdj8g9q/contour/kafka-nn/kafka:4.0.0-debian-12-r10
imagePullPolicy: IfNotPresent
env:
- name: KAFKA_HEAP_OPTS
value: -Xmx256m
command:
- /bin/bash
- -ec
- |
K=/opt/bitnami/kafka/bin
ADMIN=kafka-kafka-contour-controller-0.kafka-kafka-contour-controller-headless.kafka.svc.cluster.local:9094
CLIENT=kafka-kafka-contour.kafka.svc.cluster.local:9092
S=/vault/secrets
rc=0
printf 'security.protocol=SASL_PLAINTEXT\nsasl.mechanism=PLAIN\nsasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="inter_broker_user" password="%s";\n' "$(cat $S/admin)" > /tmp/admin.conf
for app in pm issues flows; do
u=$(cat $S/$app-user); p=$(cat $S/$app-pass)
if [ -z "$u" ] || [ -z "$p" ]; then
echo "$app: empty username or password in kafka/apps/$app"; rc=1; continue
fi
if [ -f "/tmp/pw-$u" ]; then
if [ "$(cat /tmp/pw-$u)" != "$p" ]; then
echo "$app: user $u already set from another app with a different password, skipped"; rc=1
else
echo "$app: user $u already set, same password"
fi
continue
fi
printf '%s' "$p" > "/tmp/pw-$u"
$K/kafka-configs.sh --bootstrap-server $ADMIN --command-config /tmp/admin.conf --alter --add-config "SCRAM-SHA-512=[iterations=4096,password=$p]" --entity-type users --entity-name "$u" > /dev/null
echo "$app: SCRAM-SHA-512 set for user $u"
done
sleep 5
for app in pm issues flows; do
u=$(cat $S/$app-user); p=$(cat $S/$app-pass)
printf 'security.protocol=SASL_SSL\nsasl.mechanism=SCRAM-SHA-512\nssl.truststore.type=PEM\nssl.truststore.location=/kafka-ca/ca.crt\nsasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="%s" password="%s";\n' "$u" "$p" > /tmp/$app.conf
if $K/kafka-topics.sh --bootstrap-server $CLIENT --command-config /tmp/$app.conf --list > /dev/null; then
echo "$app: login as $u over SASL_SSL ok"
else
echo "$app: login as $u over SASL_SSL FAILED"; rc=1
fi
done
$K/kafka-configs.sh --bootstrap-server $ADMIN --command-config /tmp/admin.conf --describe --entity-type users
rm -f /tmp/*.conf /tmp/pw-*
exit $rc
resources:
requests:
cpu: 50m
memory: 384Mi
volumeMounts:
- name: kafka-ca
mountPath: /kafka-ca
readOnly: true
volumes:
- name: kafka-ca
secret:
secretName: kafka-kafka-contour-tls
items:
- key: ca.crt
path: ca.crt

View File

@ -2,6 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- kafka-scram-users-job.yaml
patches:
- path: kafka.yaml

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- valkey.yaml
- outline.yaml

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: outline
labels:
istio-injection: enabled

View File

@ -1,139 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: outline
namespace: outline
spec:
interval: 10m
timeout: 10m
chart:
spec:
chart: outline
version: "0.1.0"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
dependsOn:
- name: istiod
namespace: istio-system
- name: outline-valkey
namespace: outline
values:
universal-chart:
global:
env: infra
image:
name:
infra: cr.yandex/crp3ccidau046kdj8g9q/outlinewiki/outline:1.12.0
envs:
- name: NODE_ENV
value:
_default: production
- name: URL
value:
_default: https://wiki.sarex.space
- name: PORT
value:
_default: "3000"
- name: WEB_CONCURRENCY
value:
_default: "1"
# TLS терминируется на istio ingressgateway
- name: FORCE_HTTPS
value:
_default: "false"
- name: ENABLE_UPDATES
value:
_default: "false"
- name: LOG_LEVEL
value:
_default: info
- name: PGSSLMODE
value:
_default: disable
- name: FILE_STORAGE
value:
_default: s3
- name: AWS_REGION
value:
_default: ru-msk
- name: AWS_S3_UPLOAD_BUCKET_URL
value:
_default: https://hb.ru-msk.vkcloud-storage.ru
- name: AWS_S3_UPLOAD_BUCKET_NAME
value:
_default: sarex-wiki
- name: AWS_S3_FORCE_PATH_STYLE
value:
_default: "true"
- name: AWS_S3_ACL
value:
_default: private
# VK Object Storage документирует presigned PUT; presigned POST не описан
- name: AWS_S3_UPLOAD_METHOD
value:
_default: put
- name: OIDC_DISPLAY_NAME
value:
_default: Sarex IAM
- name: OIDC_AUTH_URI
value:
_default: https://iam.sarex.io/oauth/v2/authorize
- name: OIDC_TOKEN_URI
value:
_default: https://iam.sarex.io/oauth/v2/token
- name: OIDC_USERINFO_URI
value:
_default: https://iam.sarex.io/oidc/v1/userinfo
- name: OIDC_LOGOUT_URI
value:
_default: https://iam.sarex.io/oidc/v1/end_session
- name: OIDC_USERNAME_CLAIM
value:
_default: preferred_username
- name: OIDC_SCOPES
value:
_default: openid profile email offline_access
# SMTP подключается отдельным шагом: добавить SMTP_HOST, SMTP_PORT,
# SMTP_FROM_EMAIL сюда и SMTP_USERNAME, SMTP_PASSWORD в secretEnvs.
secretEnvs:
- name: SECRET_KEY
secretName:
_default: outline
secretKey: SECRET_KEY
- name: UTILS_SECRET
secretName:
_default: outline
secretKey: UTILS_SECRET
- name: DATABASE_URL
secretName:
_default: outline-postgresql
secretKey: url
- name: REDIS_URL
secretName:
_default: outline
secretKey: REDIS_URL
- name: AWS_ACCESS_KEY_ID
secretName:
_default: outline
secretKey: AWS_ACCESS_KEY_ID
- name: AWS_SECRET_ACCESS_KEY
secretName:
_default: outline
secretKey: AWS_SECRET_ACCESS_KEY
- name: OIDC_CLIENT_ID
secretName:
_default: outline
secretKey: OIDC_CLIENT_ID
- name: OIDC_CLIENT_SECRET
secretName:
_default: outline
secretKey: OIDC_CLIENT_SECRET

View File

@ -1,62 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: outline-valkey
namespace: outline
spec:
interval: 10m
timeout: 10m
chart:
spec:
chart: valkey
version: "0.1.0"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
fullnameOverride: outline-valkey
image:
registry: cr.yandex
repository: crp3ccidau046kdj8g9q/valkey/valkey
tag: 8.0.2-debian-12-r6
global:
imagePullSecrets:
- istio-registry-secret
security:
allowInsecureImages: true
architecture: standalone
auth:
enabled: true
sentinel: false
existingSecret: outline-valkey
existingSecretPasswordKey: valkey-password
# Outline хранит в Valkey очереди фоновых задач и состояние websocket.
# Персистентность не нужна, вытеснение ключей недопустимо для очередей.
commonConfiguration: |-
appendonly no
save ""
maxmemory 256mb
maxmemory-policy noeviction
primary:
podAnnotations:
sidecar.istio.io/inject: "false"
nodeSelector:
sarex.io/pool: svc
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 384Mi
persistence:
enabled: false
metrics:
enabled: false

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: postgresql.yaml

View File

@ -1,104 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: postgresql
namespace: postgresql
spec:
interval: 5m
timeout: 2h
install:
timeout: 2h
remediation:
retries: 3
upgrade:
timeout: 2h
remediation:
retries: 3
values:
global:
security:
allowInsecureImages: true
defaultStorageClass: ${STORAGE_CLASS}
postgresql:
auth:
username: ""
database: ""
secretKeys:
userPasswordKey: "postgres-password"
auth:
username: ""
database: ""
secretKeys:
userPasswordKey: "postgres-password"
image:
registry: cr.yandex/crp3ccidau046kdj8g9q
repository: contour/postgresql
tag: 17.0.7
pullPolicy: Always
metrics:
enabled: false
prometheusRule:
enabled: false
primary:
automountServiceAccountToken: true
containerSecurityContext:
readOnlyRootFilesystem: false
persistence:
storageClass: ${STORAGE_CLASS}
size: 20Gi
customLivenessProbe:
exec:
command:
- /bin/sh
- -c
- exec pg_isready -U "postgres" -d postgres -h 127.0.0.1 -p 5432
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 6
customReadinessProbe:
exec:
command:
- /bin/sh
- -c
- exec pg_isready -U "postgres" -d postgres -h 127.0.0.1 -p 5432
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 6
customStartupProbe:
exec:
command:
- /bin/sh
- -c
- exec pg_isready -U "postgres" -d postgres -h 127.0.0.1 -p 5432
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 6
nodeSelector:
dedicated: generic
tolerations: []
contour:
enabled: true
adminUser: "postgres"
vault:
enabled: true
role: postgresql-zitadel
authPath: auth/kubernetes
secretPath: secrets/data/postgresql-zitadel/admin
secretKey: postgres-password
usersSecretPath: secrets/data/zitadel/postgresql
sharedPreloadLibraries: "timescaledb,pg_stat_statements"
databases:
- name: zitadel
user: zitadel
passwordKey: password
extensions: []
restoreFromDump: false

View File

@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: rabbitmq.yaml

Some files were not shown because too many files have changed in this diff Show More