Создание администратора платформы перенесено в под backend
Учётка создавалась задачей ansible через kubectl exec — по явной команде оператора и в обход GitOps. Теперь это делает сам под при каждом старте: стартовый скрипт подхватывает креды из Vault и вызывает штатную createsuperuser --noinput. Идемпотентность обеспечена ветвлением, а не подавлением ошибки: на уже существующем логине Django возвращает "That username is already taken" с ненулевым кодом, что под set -e из command базы уронило бы под на втором запуске. Ошибка ожидаема и гасится веткой else. Заодно в стартовый скрипт добавлен migrate. В entrypoint.sh образа он уже есть, но запущен без set -e — его падение не мешает uwsgi стартовать. Именно так контур несколько часов отдавал 200 на /admin/ с пустой схемой: backend не мог аутентифицироваться в СУБД, миграции падали, приложение работало. Здесь команда идёт под set -e, поэтому отказ виден сразу. Ansible остаётся владельцем значений: генерирует логин и пароль (теперь безусловно, а не по флагу — их ждёт vault-agent) и кладёт в secrets/apps/django/superuser. Задача poe superuser печатает креды. Убраны временные диагностические задачи с ignore_errors, добавленные при разборе отказа createsuperuser.
This commit is contained in:
parent
ee1881e516
commit
bddcc955fe
@ -44,7 +44,8 @@ platform = "ansible-playbook deploy.yml -e aero_platform_up=true"
|
||||
gen-env = "ansible-playbook deploy.yml --tags env"
|
||||
# docker login в приватный реестр по ключу json_key
|
||||
login = "ansible-playbook deploy.yml --tags login"
|
||||
# Сгенерировать логин+пароль и создать Django-суперпользователя в backend
|
||||
# Показать логин+пароль администратора платформы. Саму учётку создаёт под
|
||||
# backend при старте (apps/django/aero), эта задача только печатает креды.
|
||||
superuser = "ansible-playbook deploy.yml -e sarex_create_superuser=true --tags superuser"
|
||||
lint = "ansible-lint"
|
||||
|
||||
|
||||
@ -263,6 +263,15 @@ vault_app_secrets:
|
||||
secret_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
|
||||
buckets:
|
||||
- name: "{{ sarex_django_s3_bucket }}"
|
||||
# Учётка администратора платформы. Её разбирает не приложение, а стартовый
|
||||
# скрипт пода backend: он экспортирует ключи как DJANGO_SUPERUSER_* — ровно
|
||||
# те имена, которые ждёт штатная manage.py createsuperuser --noinput.
|
||||
# См. apps/django/aero/kustomization.yaml.
|
||||
- path: secrets/apps/django/superuser
|
||||
data:
|
||||
username: "{{ superuser_name }}"
|
||||
password: "{{ superuser_password }}"
|
||||
email: "{{ superuser_name }}@{{ platform_domain }}"
|
||||
# JWT-ключи RS512. Те же самые, что роль генерирует для compose-стека, —
|
||||
# контур переезжает, и токены должны остаться совместимыми.
|
||||
- path: secrets/vault/common/rsa_keys
|
||||
|
||||
@ -92,6 +92,28 @@
|
||||
no_log: true
|
||||
tags: [env]
|
||||
|
||||
# Учётка администратора платформы. Живёт отдельно от общего цикла выше по двум
|
||||
# причинам: у логина свой алфавит (Django не примет username со спецсимволами)
|
||||
# и оба значения не должны попасть в .env — compose-стек их не использует,
|
||||
# потребитель один, и это Vault.
|
||||
#
|
||||
# Генерируется всегда, а не по флагу sarex_create_superuser: значения уезжают
|
||||
# в secrets/apps/django/superuser (см. vault_app_secrets), откуда их забирает
|
||||
# стартовый скрипт пода backend. Без них vault-agent не отрендерит шаблон и
|
||||
# под встанет в Init:Error. lookup('password') идемпотентен — на повторных
|
||||
# прогонах возвращает уже сохранённое значение, а не генерирует новое.
|
||||
- name: Сгенерировать/загрузить логин администратора платформы
|
||||
ansible.builtin.set_fact:
|
||||
superuser_name: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_NAME chars=ascii_lowercase,digits length=10') }}"
|
||||
no_log: true
|
||||
tags: [env, superuser]
|
||||
|
||||
- name: Сгенерировать/загрузить пароль администратора платформы
|
||||
ansible.builtin.set_fact:
|
||||
superuser_password: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_PASSWORD chars=ascii_letters,digits length=' ~ secret_length) }}"
|
||||
no_log: true
|
||||
tags: [env, superuser]
|
||||
|
||||
# RSA-ключи JWT (RS512): генерируем на control-node, persist в secrets_store.
|
||||
- name: Сгенерировать приватный RSA-ключ JWT
|
||||
ansible.builtin.command: "openssl genrsa -out {{ jwt_private_key_path }} 4096"
|
||||
@ -181,89 +203,16 @@
|
||||
changed_when: true
|
||||
tags: [up]
|
||||
|
||||
# --- Django-суперпользователь: генерация логина+пароля, создание -----------
|
||||
# Только по явной команде (uv run poe superuser передаёт sarex_create_superuser=true),
|
||||
# чтобы обычный deploy не дёргал backend. Логин/пароль персистятся в secrets_store
|
||||
# (переиспользуются при повторе — идемпотентно).
|
||||
- name: Сгенерировать логин суперпользователя
|
||||
ansible.builtin.set_fact:
|
||||
superuser_name: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_NAME chars=ascii_lowercase,digits length=10') }}"
|
||||
when: sarex_create_superuser | default(false) | bool
|
||||
no_log: true
|
||||
tags: [superuser]
|
||||
|
||||
- name: Сгенерировать пароль суперпользователя
|
||||
ansible.builtin.set_fact:
|
||||
superuser_password: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_PASSWORD chars=ascii_letters,digits length=' ~ secret_length) }}"
|
||||
when: sarex_create_superuser | default(false) | bool
|
||||
no_log: true
|
||||
tags: [superuser]
|
||||
|
||||
# Backend теперь живёт в k3s, а не в compose, поэтому идём через kubectl exec
|
||||
# внутри контейнера k3s-server, а не через docker compose exec.
|
||||
# --- Django-суперпользователь ----------------------------------------------
|
||||
# Самого создания здесь больше нет — его делает под backend при каждом старте
|
||||
# (apps/django/aero/kustomization.yaml, args стартового скрипта). Так учётка
|
||||
# появляется вместе с приложением, а не отдельной командой оператора, и
|
||||
# переживает пересоздание пода без ручного вмешательства.
|
||||
#
|
||||
# Пароль передаётся переменной окружения (env в kubectl exec), а не аргументом
|
||||
# командной строки: аргументы видны в ps любому, кто имеет доступ к ноде.
|
||||
# Метка именно app=backend: universal-chart не проставляет
|
||||
# app.kubernetes.io/instance, и селектор по нему возвращает пустой список.
|
||||
- name: Найти под backend в k3s
|
||||
when: sarex_create_superuser | default(false) | bool
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
{{ compose_cmd }} exec -T k3s-server
|
||||
kubectl -n {{ django_namespace }} get pods -l app=backend
|
||||
--field-selector=status.phase=Running
|
||||
-o jsonpath={.items[0].metadata.name}
|
||||
chdir: "{{ deploy_dir }}"
|
||||
register: backend_pod
|
||||
changed_when: false
|
||||
tags: [superuser]
|
||||
|
||||
# argv, а не shell: аргументы передаются процессу напрямую, без разбора
|
||||
# шеллом. Иначе python-однострочник попадает внутрь двойных кавычек внутри
|
||||
# sh -ec '...' и разваливается на вложенном экранировании.
|
||||
#
|
||||
# Пароль идёт переменной окружения, а не аргументом: аргументы видны в ps
|
||||
# любому, у кого есть доступ к ноде.
|
||||
- name: Создать/обновить Django-суперпользователя в backend (k3s)
|
||||
when: sarex_create_superuser | default(false) | bool
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- "{{ compose_cmd.split()[0] }}"
|
||||
- compose
|
||||
- exec
|
||||
- -T
|
||||
- k3s-server
|
||||
- kubectl
|
||||
- -n
|
||||
- "{{ django_namespace }}"
|
||||
- exec
|
||||
- "{{ backend_pod.stdout | trim }}"
|
||||
- -c
|
||||
- backend
|
||||
- --
|
||||
- env
|
||||
- "SU_NAME={{ superuser_name }}"
|
||||
- "SU_PASS={{ superuser_password }}"
|
||||
- "SU_EMAIL={{ superuser_name }}@{{ platform_domain }}"
|
||||
- python
|
||||
- manage.py
|
||||
- shell
|
||||
- -c
|
||||
- >-
|
||||
import os;
|
||||
from django.contrib.auth import get_user_model;
|
||||
U = get_user_model();
|
||||
u, c = U.objects.get_or_create(username=os.environ['SU_NAME'], defaults={'email': os.environ['SU_EMAIL']});
|
||||
u.is_staff = True; u.is_superuser = True; u.set_password(os.environ['SU_PASS']); u.save();
|
||||
print('created' if c else 'updated')
|
||||
chdir: "{{ deploy_dir }}"
|
||||
register: superuser_result
|
||||
changed_when: true
|
||||
no_log: true
|
||||
tags: [superuser]
|
||||
|
||||
- name: Показать учётные данные суперпользователя
|
||||
# Ansible остаётся владельцем самих значений: генерирует их выше (тег env) и
|
||||
# кладёт в Vault. Здесь — только показать логин по явному запросу
|
||||
# (uv run poe superuser передаёт sarex_create_superuser=true).
|
||||
- name: Показать учётные данные администратора платформы
|
||||
ansible.builtin.debug:
|
||||
msg: "Django superuser → login: {{ superuser_name }} password: {{ superuser_password }}"
|
||||
when: sarex_create_superuser | default(false) | bool
|
||||
|
||||
@ -83,3 +83,61 @@ patches:
|
||||
- op: replace
|
||||
path: /spec/values/services/backend/envs/2/value/_default
|
||||
value: "False"
|
||||
|
||||
# --- Администратор платформы --------------------------------------------
|
||||
# Учётка приезжает из Vault под именами, которые ждёт штатная
|
||||
# createsuperuser --noinput. Секрет кладёт ansible
|
||||
# (vault_app_secrets, secrets/apps/django/superuser).
|
||||
- op: add
|
||||
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-superuser
|
||||
value: secrets/data/apps/django/superuser
|
||||
- op: add
|
||||
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-superuser
|
||||
value: |-
|
||||
{{- with secret "secrets/data/apps/django/superuser" -}}
|
||||
DJANGO_SUPERUSER_USERNAME={{ index .Data.data "username" }}
|
||||
DJANGO_SUPERUSER_EMAIL={{ index .Data.data "email" }}
|
||||
DJANGO_SUPERUSER_PASSWORD={{ index .Data.data "password" }}
|
||||
{{- end -}}
|
||||
|
||||
# Стартовый скрипт целиком, а не точечная правка: JSON6902 умеет заменять
|
||||
# только элемент списка, а вся логика запуска — один shell-блок args[0].
|
||||
# Отличия от base:
|
||||
# * не подключается django-kafka — этого секрета в контуре нет;
|
||||
# * добавлены migrate и создание администратора.
|
||||
#
|
||||
# Почему migrate здесь, хотя его же делает /opt/sarex/entrypoint.sh:
|
||||
# там он запущен без set -e, и его падение НИКАК не мешает uwsgi
|
||||
# стартовать. Именно так контур несколько часов отдавал 200 на /admin/
|
||||
# с пустой схемой БД, пока backend не мог аутентифицироваться. Здесь
|
||||
# команда выполняется под set -e из command базы (/bin/sh -ec), поэтому
|
||||
# неудача валит под в CrashLoopBackOff — то есть становится видимой.
|
||||
# Повторный прогон внутри entrypoint.sh при уже накатанной схеме
|
||||
# отрабатывает вхолостую за секунды.
|
||||
- op: replace
|
||||
path: /spec/values/services/backend/deployment/args/_default/0
|
||||
value: |
|
||||
set -a
|
||||
[ -f /vault/secrets/django-postgresql ] && . /vault/secrets/django-postgresql
|
||||
[ -f /vault/secrets/django-rabbitmq ] && . /vault/secrets/django-rabbitmq
|
||||
[ -f /vault/secrets/django-s3 ] && . /vault/secrets/django-s3
|
||||
[ -f /vault/secrets/django-superuser ] && . /vault/secrets/django-superuser
|
||||
[ -f /vault/secrets/django-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/django-jwt-private)"
|
||||
[ -f /vault/secrets/django-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/django-jwt-public)"
|
||||
set +a
|
||||
|
||||
python manage.py migrate --noinput
|
||||
|
||||
# Идемпотентно по построению: команда отрабатывает при КАЖДОМ старте
|
||||
# пода, а на уже существующем логине штатно возвращает
|
||||
# "That username is already taken" с ненулевым кодом. Ошибка ожидаема
|
||||
# и гасится веткой else, иначе set -e ронял бы под на втором запуске.
|
||||
if [ -n "${DJANGO_SUPERUSER_USERNAME:-}" ]; then
|
||||
if python manage.py createsuperuser --noinput; then
|
||||
echo "entrypoint: администратор создан"
|
||||
else
|
||||
echo "entrypoint: администратор уже существует, пропускаем"
|
||||
fi
|
||||
fi
|
||||
|
||||
exec /opt/sarex/entrypoint.sh
|
||||
|
||||
Loading…
Reference in New Issue
Block a user