Роль сделал без автоматической перезагрузки
This commit is contained in:
parent
2e68a84ead
commit
e4e7d94694
@ -2,6 +2,6 @@ skip_list:
|
|||||||
- fqcn
|
- fqcn
|
||||||
- yaml[new-line-at-end-of-file]
|
- yaml[new-line-at-end-of-file]
|
||||||
- yaml[truthy]
|
- yaml[truthy]
|
||||||
- yaml[line-length]
|
- yaml[line-length]
|
||||||
- var-naming[no-role-prefix]
|
- var-naming[no-role-prefix]
|
||||||
- 'ignore-errors'
|
- 'ignore-errors'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
stages:
|
stages:
|
||||||
- lint
|
- lint
|
||||||
- test
|
# - test
|
||||||
- deploy
|
- deploy
|
||||||
- notify
|
- notify
|
||||||
|
|
||||||
@ -33,18 +33,18 @@ lint:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "master"
|
- if: $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "master"
|
||||||
|
|
||||||
test:
|
#test:
|
||||||
stage: test
|
# stage: test
|
||||||
script:
|
# script:
|
||||||
- echo "Распаковываем секреты..."
|
# - echo "Распаковываем секреты..."
|
||||||
- ansible-vault decrypt --vault-password-file ./vault-password.txt vars/secrets.yml
|
# - ansible-vault decrypt --vault-password-file ./vault-password.txt vars/secrets.yml
|
||||||
- echo "Запускаем тесты через Молекулу..."
|
# - echo "Запускаем тесты через Молекулу..."
|
||||||
- molecule test --parallel --destroy=always
|
# - molecule test --parallel --destroy=always
|
||||||
- echo "Упаковываем секреты..."
|
# - echo "Упаковываем секреты..."
|
||||||
- ansible-vault encrypt vars/secrets.yml --encrypt-vault-id default --vault-password-file ./vault-password.txt
|
# - ansible-vault encrypt vars/secrets.yml --encrypt-vault-id default --vault-password-file ./vault-password.txt
|
||||||
allow_failure: false
|
# allow_failure: false
|
||||||
rules:
|
# rules:
|
||||||
- if: $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "master"
|
# - if: $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "master"
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
@ -74,9 +74,9 @@ notify:
|
|||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
if [ "$CI_JOB_STATUS" == "success" ]; then
|
if [ "$CI_JOB_STATUS" == "success" ]; then
|
||||||
MESSAGE="✅ Пайплайн успешно завершен!%0AПроект: $CI_PROJECT_NAME%0AВетка: $CI_COMMIT_REF_NAME%0AСтатус: $CI_JOB_STATUS"
|
MESSAGE="✅ Настройки кластера успешно завершены!%0AПроект: $CI_PROJECT_NAME%0AВетка: $CI_COMMIT_REF_NAME%0AСтатус: $CI_JOB_STATUS"
|
||||||
else
|
else
|
||||||
MESSAGE="❌ Пайплайн завершен с ошибкой!%0AПроект: $CI_PROJECT_NAME%0AВетка: $CI_COMMIT_REF_NAME%0AСтатус: $CI_JOB_STATUS"
|
MESSAGE="❌ Настройки кластера были произведены с ошибкой!%0AПроект: $CI_PROJECT_NAME%0AВетка: $CI_COMMIT_REF_NAME%0AСтатус: $CI_JOB_STATUS"
|
||||||
fi
|
fi
|
||||||
# curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
|
# curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
|
||||||
# -d "chat_id=$TELEGRAM_CHAT_ID" \
|
# -d "chat_id=$TELEGRAM_CHAT_ID" \
|
||||||
@ -86,4 +86,3 @@ notify:
|
|||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- echo "Работа пайплайна завершена"
|
- echo "Работа пайплайна завершена"
|
||||||
|
|
||||||
|
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/roles/deploy.yaml" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -28,6 +28,8 @@ RUN apt-get update && \
|
|||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg \
|
||||||
|
diffutils \
|
||||||
|
jq \
|
||||||
lsb-release \
|
lsb-release \
|
||||||
&& mkdir -p /etc/apt/keyrings \
|
&& mkdir -p /etc/apt/keyrings \
|
||||||
&& curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
|
&& curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
|
||||||
|
@ -7,7 +7,7 @@ USER root
|
|||||||
|
|
||||||
# Обновляем пакеты и устанавливаем systemd
|
# Обновляем пакеты и устанавливаем systemd
|
||||||
RUN dnf -y update && \
|
RUN dnf -y update && \
|
||||||
dnf -y install systemd rsync && \
|
dnf -y install systemd rsync diffutils jq && \
|
||||||
dnf clean all && \
|
dnf clean all && \
|
||||||
rm -rf /var/cache/dnf /tmp/* /var/tmp/*
|
rm -rf /var/cache/dnf /tmp/* /var/tmp/*
|
||||||
|
|
||||||
@ -19,4 +19,4 @@ STOPSIGNAL SIGRTMIN+3
|
|||||||
VOLUME [ "/sys/fs/cgroup" ]
|
VOLUME [ "/sys/fs/cgroup" ]
|
||||||
|
|
||||||
# Запускаем systemd при старте контейнера
|
# Запускаем systemd при старте контейнера
|
||||||
CMD ["/sbin/init"]
|
CMD ["/sbin/init"]
|
||||||
|
@ -5,7 +5,7 @@ FROM geerlingguy/docker-ubuntu2004-ansible:latest
|
|||||||
|
|
||||||
# Обновляем пакеты и устанавливаем systemd
|
# Обновляем пакеты и устанавливаем systemd
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y systemd systemd-sysv rsync && \
|
apt-get install -y systemd systemd-sysv rsync diffutils jq && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
@ -17,4 +17,4 @@ STOPSIGNAL SIGRTMIN+3
|
|||||||
VOLUME [ "/sys/fs/cgroup" ]
|
VOLUME [ "/sys/fs/cgroup" ]
|
||||||
|
|
||||||
# Запускаем systemd при старте контейнера
|
# Запускаем systemd при старте контейнера
|
||||||
CMD ["/sbin/init"]
|
CMD ["/sbin/init"]
|
||||||
|
6
Makefile
6
Makefile
@ -1,7 +1,7 @@
|
|||||||
# Глобальные переменные
|
# Глобальные переменные
|
||||||
IMAGE ?= ansible
|
IMAGE ?= ansible
|
||||||
TAG ?= 0.1
|
TAG ?= 0.1
|
||||||
REGISTRY ?= hub.cism-ms.ru/ansible
|
REGISTRY ?= hub.antropoff.ru/ansible
|
||||||
# По умолчанию используем docker. Для локальной разработки используйте docker-compose
|
# По умолчанию используем docker. Для локальной разработки используйте docker-compose
|
||||||
RUN_MODE ?= docker
|
RUN_MODE ?= docker
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ role:
|
|||||||
clear; \
|
clear; \
|
||||||
echo "Running test roles..."; \
|
echo "Running test roles..."; \
|
||||||
$(RUN) bash -c "ansible-vault decrypt --vault-password-file vault-password.txt vars/secrets.yml"; \
|
$(RUN) bash -c "ansible-vault decrypt --vault-password-file vault-password.txt vars/secrets.yml"; \
|
||||||
$(RUN) bash -c "docker login $(REGISTRY) && molecule test --parallel --destroy=always"; \
|
$(RUN) bash -c "molecule test --parallel --destroy=always"; \
|
||||||
$(RUN) bash -c "ansible-vault encrypt vars/secrets.yml --encrypt-vault-id default --vault-password-file vault-password.txt";; \
|
$(RUN) bash -c "ansible-vault encrypt vars/secrets.yml --encrypt-vault-id default --vault-password-file vault-password.txt";; \
|
||||||
deploy) \
|
deploy) \
|
||||||
clear; \
|
clear; \
|
||||||
@ -148,4 +148,4 @@ git:
|
|||||||
git checkout -b $$NEW_BRANCH; \
|
git checkout -b $$NEW_BRANCH; \
|
||||||
echo "Создана и переключена на новую ветку: $$NEW_BRANCH";; \
|
echo "Создана и переключена на новую ветку: $$NEW_BRANCH";; \
|
||||||
*) echo "Unknown action. Available actions: push, pull, cluster-branch";; \
|
*) echo "Unknown action. Available actions: push, pull, cluster-branch";; \
|
||||||
esac
|
esac
|
||||||
|
63
README.md
63
README.md
@ -1,6 +1,63 @@
|
|||||||
# AnsibleTemplate
|
# Patroni GitOps Config Manager
|
||||||
|
|
||||||
Темплейт для создания, проверки и тестирование ролей Ansible с помощью контейнеров Docker.
|
### Назначение роли
|
||||||
|
Эта роль предназначена для безопасного управления конфигурацией кластера Patroni. Она предоставляет следующие возможности:
|
||||||
|
|
||||||
|
- Создание резервных копий текущей конфигурации с timestamp
|
||||||
|
- Валидация изменений конфигурации перед применением
|
||||||
|
- Наглядное отображение различий между текущей и новой конфигурацией
|
||||||
|
- Безопасное применение изменений через REST API Patroni
|
||||||
|
- Проверка состояния кластера после изменений
|
||||||
|
- Уведомление о необходимости перезагрузки нод (если требуется)
|
||||||
|
- Автоматическое управление историей конфигурационных файлов
|
||||||
|
|
||||||
|
### Требования
|
||||||
|
- Ansible 2.9+
|
||||||
|
- Доступ к Patroni REST API (порт 8008)
|
||||||
|
- Python 3 для валидации YAML
|
||||||
|
- Утилита diff с поддержкой цветного вывода
|
||||||
|
|
||||||
|
### Переменные роли
|
||||||
|
Основные переменные, которые можно переопределить:
|
||||||
|
|
||||||
|
- config_dir (по умолчанию: "/ansible/history") - директория для хранения истории конфигураций
|
||||||
|
- config_file (по умолчанию: "/ansible/patroni_config.yaml") - путь к файлу с изменениями конфигурации
|
||||||
|
- patroni_host (по умолчанию: "10.14.0.180") - хост кластера Patroni
|
||||||
|
|
||||||
|
### Анализ результатов
|
||||||
|
После выполнения этапа подготовки:
|
||||||
|
|
||||||
|
- Будет показан цветной diff изменений
|
||||||
|
- В директории config_dir появятся:
|
||||||
|
- Резервная копия текущей конфигурации с timestamp
|
||||||
|
- Файл last.yaml с новой конфигурацией
|
||||||
|
|
||||||
|
После выполнения этапа применения:
|
||||||
|
|
||||||
|
- Будет выведен статус кластера
|
||||||
|
- Появится предупреждение, если требуется перезагрузка нод
|
||||||
|
- Старые конфигурации будут автоматически удалены (остаются последние 10)
|
||||||
|
|
||||||
|
### Особенности работы
|
||||||
|
Безопасность:
|
||||||
|
- Конфигурация всегда сохраняется перед изменениями
|
||||||
|
- Изменения проверяются на валидность перед применением
|
||||||
|
- Показывается подробный diff перед применением
|
||||||
|
|
||||||
|
Информирование:
|
||||||
|
- Явное предупреждение о необходимости перезагрузки
|
||||||
|
- Подробный вывод статуса кластера после изменений
|
||||||
|
- Сохранение истории изменений
|
||||||
|
|
||||||
|
Автоматизация:
|
||||||
|
- Управление историей конфигураций (автоочистка старых файлов)
|
||||||
|
- Проверка состояния кластера после изменений
|
||||||
|
|
||||||
|
### Важные примечания
|
||||||
|
- Роль не выполняет автоматическую перезагрузку нод Patroni, так как это потенциально опасная операция
|
||||||
|
- При необходимости перезагрузки будет показана команда для ручного выполнения
|
||||||
|
- Все изменения конфигурации сохраняются с timestamp для возможности отката
|
||||||
|
- Для работы требуется доступ к API Patroni (порт 8008)
|
||||||
|
|
||||||
### С чего начать?
|
### С чего начать?
|
||||||
|
|
||||||
@ -58,4 +115,4 @@ molecule/default/molecule.yml
|
|||||||
volumes:
|
volumes:
|
||||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||||
```
|
```
|
||||||
Помните, что образ обязательно должен содержать python не ниже версии 3.12 и systemd для нормального тестирования ролей.
|
Помните, что образ обязательно должен содержать python не ниже версии 3.12 и systemd для нормального тестирования ролей.
|
||||||
|
@ -5,4 +5,7 @@ remote_user = devops
|
|||||||
host_key_checking = False
|
host_key_checking = False
|
||||||
enable_plugins = yaml, ini
|
enable_plugins = yaml, ini
|
||||||
roles_path = roles/
|
roles_path = roles/
|
||||||
interpreter_python = auto
|
interpreter_python = auto
|
||||||
|
stdout_callback = yaml
|
||||||
|
bin_ansible_callbacks = True
|
||||||
|
force_color = 1
|
||||||
|
0
history/.gitkeep
Normal file
0
history/.gitkeep
Normal file
@ -3,4 +3,5 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
vars_files:
|
vars_files:
|
||||||
- ../../vars/secrets.yml
|
- ../../vars/secrets.yml
|
||||||
roles:
|
roles:
|
||||||
|
- ../../roles/prepare
|
||||||
|
@ -9,18 +9,18 @@ driver:
|
|||||||
name: docker
|
name: docker
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
- name: centos
|
# - name: centos
|
||||||
image: "hub.cism-ms.ru/ansible/centos:latest"
|
# image: "hub.antropoff.ru/ansible/centos:latest"
|
||||||
privileged: true
|
# privileged: true
|
||||||
pre_build_image: true
|
# pre_build_image: true
|
||||||
volumes:
|
# volumes:
|
||||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
# - /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
# - /var/run/docker.sock:/var/run/docker.sock
|
||||||
tmpfs:
|
# tmpfs:
|
||||||
- /tmp
|
# - /tmp
|
||||||
- /run
|
# - /run
|
||||||
- name: ubuntu
|
- name: ubuntu
|
||||||
image: "hub.cism-ms.ru/ansible/ubuntu:latest"
|
image: "hub.antropoff.ru/ansible/ubuntu:latest"
|
||||||
privileged: true
|
privileged: true
|
||||||
pre_build_image: true
|
pre_build_image: true
|
||||||
volumes:
|
volumes:
|
||||||
@ -58,4 +58,3 @@ scenario:
|
|||||||
- verify
|
- verify
|
||||||
- cleanup
|
- cleanup
|
||||||
- destroy
|
- destroy
|
||||||
|
|
||||||
|
5
patroni_config.yaml
Normal file
5
patroni_config.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
postgresql:
|
||||||
|
parameters:
|
||||||
|
max_connections: 300
|
||||||
|
shared_buffers: "12GB"
|
||||||
|
use_pg_rewind: true
|
0
roles/apply/defaults/.gitkeep
Normal file
0
roles/apply/defaults/.gitkeep
Normal file
11
roles/apply/deploy.yaml
Normal file
11
roles/apply/deploy.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy roles
|
||||||
|
hosts: localhost
|
||||||
|
become: true
|
||||||
|
become_user: root
|
||||||
|
become_method: ansible.builtin.sudo
|
||||||
|
gather_facts: true
|
||||||
|
vars_files:
|
||||||
|
- ../../vars/secrets.yml
|
||||||
|
roles:
|
||||||
|
- apply
|
0
roles/apply/files/.gitkeep
Normal file
0
roles/apply/files/.gitkeep
Normal file
0
roles/apply/handlers/.gitkeep
Normal file
0
roles/apply/handlers/.gitkeep
Normal file
4
roles/apply/handlers/main.yaml
Normal file
4
roles/apply/handlers/main.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: Log cleanup results
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Removed {{ (old_configs.files | sort(attribute='mtime'))[:-10] | length }} old config files"
|
0
roles/apply/meta/.gitkeep
Normal file
0
roles/apply/meta/.gitkeep
Normal file
3
roles/apply/tasks/main.yaml
Normal file
3
roles/apply/tasks/main.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
- name: "Подключаем таски"
|
||||||
|
include_tasks: "role/main.yaml"
|
130
roles/apply/tasks/role/main.yaml
Normal file
130
roles/apply/tasks/role/main.yaml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
- name: Apply new configuration
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ patroni_host }}:8008/config"
|
||||||
|
method: PATCH
|
||||||
|
body: "{{ new_config | to_json }}"
|
||||||
|
body_format: json
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
Content-Type: "application/json"
|
||||||
|
register: apply_result
|
||||||
|
changed_when: apply_result.status == 200
|
||||||
|
|
||||||
|
- name: Force wait for config to apply # noqa: no-handler
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
timeout: 30
|
||||||
|
delay: 5
|
||||||
|
when: apply_result is changed
|
||||||
|
|
||||||
|
- name: Get verified cluster status # noqa: no-handler
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ patroni_host }}:8008/cluster"
|
||||||
|
method: GET
|
||||||
|
return_content: yes
|
||||||
|
status_code: 200
|
||||||
|
register: verified_cluster_status
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
when: apply_result is changed
|
||||||
|
|
||||||
|
- name: Display confirmed cluster status
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: |
|
||||||
|
=== CONFIRMED CLUSTER STATUS ===
|
||||||
|
Leader: {{ (verified_cluster_status.json.members | selectattr('role', 'equalto', 'leader') | map(attribute='name') | first) | default('UNKNOWN') }}
|
||||||
|
Members:
|
||||||
|
{% for member in verified_cluster_status.json.members %}
|
||||||
|
- {{ member.name }} [{{ member.role | upper }}]
|
||||||
|
State: {{ member.state | default('UNKNOWN') }}
|
||||||
|
Lag: {{ member.lag | default(0) }}MB
|
||||||
|
Timeline: {{ member.timeline | default('N/A') }}
|
||||||
|
Pending restart: {{ member.pending_restart | default(false) | ternary('YES', 'NO') }}
|
||||||
|
{% endfor %}
|
||||||
|
Config Applied: {{ apply_result is changed | ternary('YES', 'NO') }}
|
||||||
|
================================
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Refresh cluster status
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ patroni_host }}:8008/cluster"
|
||||||
|
method: GET
|
||||||
|
return_content: yes
|
||||||
|
status_code: 200
|
||||||
|
register: refreshed_cluster_status
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
when: verified_cluster_status is defined
|
||||||
|
|
||||||
|
- name: Safe check for pending restarts
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
needs_restart: >-
|
||||||
|
{{
|
||||||
|
(refreshed_cluster_status.json.members |
|
||||||
|
map(attribute='pending_restart', default=false) |
|
||||||
|
select('equalto', true) | list | count > 0) or
|
||||||
|
(refreshed_cluster_status.json.members |
|
||||||
|
map(attribute='tags.pending_restart', default=false) |
|
||||||
|
select('equalto', true) | list | count > 0)
|
||||||
|
}}
|
||||||
|
node_names: >-
|
||||||
|
{{
|
||||||
|
refreshed_cluster_status.json.members |
|
||||||
|
map(attribute='name') |
|
||||||
|
list
|
||||||
|
}}
|
||||||
|
when:
|
||||||
|
- refreshed_cluster_status.json is defined
|
||||||
|
- refreshed_cluster_status.json.members is defined
|
||||||
|
run_once: true
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Show restart warning if needed
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: |
|
||||||
|
{% if needs_restart %}
|
||||||
|
==================================
|
||||||
|
ВНИМАНИЕ: ТРЕБУЕТСЯ ПЕРЕЗАГРУЗКА
|
||||||
|
==================================
|
||||||
|
|
||||||
|
Не, я конечно могу и сам ролью, но вдруг кластер в проде или еще где!!!
|
||||||
|
Так что лучше выполнить следующую команду на одной из нод кластера:
|
||||||
|
|
||||||
|
patronictl restart {{ node_names | join(' ') }}
|
||||||
|
|
||||||
|
Затронутые ноды:
|
||||||
|
{% for node in node_names %}
|
||||||
|
- {{ node }}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
==================================
|
||||||
|
СТАТУС: Перезагрузка не требуется
|
||||||
|
==================================
|
||||||
|
{% endif %}
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Archive old configurations
|
||||||
|
block:
|
||||||
|
- name: Find old config files
|
||||||
|
ansible.builtin.find:
|
||||||
|
path: "{{ config_dir }}"
|
||||||
|
pattern: "*-config.yaml"
|
||||||
|
age: "10s"
|
||||||
|
register: old_configs
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
- name: Remove excess configs (keep last 10)
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ (old_configs.files | sort(attribute='mtime'))[:-10] }}"
|
||||||
|
when:
|
||||||
|
- old_configs.matched > 10
|
||||||
|
- apply_result is changed
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
notify: Log cleanup results
|
0
roles/apply/templates/.gitkeep
Normal file
0
roles/apply/templates/.gitkeep
Normal file
0
roles/apply/tests/.gitkeep
Normal file
0
roles/apply/tests/.gitkeep
Normal file
@ -1 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
- name: Подготовка ро<D180>к изменению конфнастроек кла<D0BB>стера
|
||||||
|
import_playbook: prepare/deploy.yaml
|
||||||
|
- name: Применение изменений нас<D0B0>троек кластера
|
||||||
|
import_playbook: apply/deploy.yaml
|
||||||
|
0
roles/prepare/defaults/.gitkeep
Normal file
0
roles/prepare/defaults/.gitkeep
Normal file
11
roles/prepare/deploy.yaml
Normal file
11
roles/prepare/deploy.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy roles
|
||||||
|
hosts: localhost
|
||||||
|
become: true
|
||||||
|
become_user: root
|
||||||
|
become_method: ansible.builtin.sudo
|
||||||
|
gather_facts: true
|
||||||
|
vars_files:
|
||||||
|
- ../../vars/secrets.yml
|
||||||
|
roles:
|
||||||
|
- prepare
|
0
roles/prepare/files/.gitkeep
Normal file
0
roles/prepare/files/.gitkeep
Normal file
0
roles/prepare/handlers/.gitkeep
Normal file
0
roles/prepare/handlers/.gitkeep
Normal file
0
roles/prepare/meta/.gitkeep
Normal file
0
roles/prepare/meta/.gitkeep
Normal file
15
roles/prepare/tasks/main.yaml
Normal file
15
roles/prepare/tasks/main.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# - name: "Определяем ОС"
|
||||||
|
# set_fact:
|
||||||
|
# os_family: "{{ ansible_facts['os_family'] }}"
|
||||||
|
|
||||||
|
# - name: "Подключаем таски для RedHat совместимых"
|
||||||
|
# include_tasks: "redhat/main.yaml"
|
||||||
|
# when: os_family == "RedHat"
|
||||||
|
|
||||||
|
# - name: "Подключаем таски для Debian/Ubuntu совместимых"
|
||||||
|
# include_tasks: "debian/main.yaml"
|
||||||
|
# when: os_family == "Debian"
|
||||||
|
|
||||||
|
- name: "Подключаем таски"
|
||||||
|
include_tasks: "role/main.yaml"
|
128
roles/prepare/tasks/role/main.yaml
Normal file
128
roles/prepare/tasks/role/main.yaml
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
---
|
||||||
|
- name: Validate config file exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ config_file }}"
|
||||||
|
register: config_check
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
- name: Fail if config missing
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "Configuration file {{ config_file }} not found!"
|
||||||
|
when: not config_check.stat.exists
|
||||||
|
|
||||||
|
- name: Ensure history directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ config_dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0750'
|
||||||
|
owner: "{{ ansible_user_id | default(omit) }}"
|
||||||
|
group: "{{ ansible_user_gid | default(omit) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get current Patroni configuration
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ patroni_host }}:8008/config"
|
||||||
|
method: GET
|
||||||
|
return_content: yes
|
||||||
|
status_code: 200
|
||||||
|
register: patroni_config
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Save current config with timestamp
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: |
|
||||||
|
# Original config from {{ ansible_date_time.iso8601 }}
|
||||||
|
{{ patroni_config.content | from_json | to_nice_yaml }}
|
||||||
|
dest: "{{ config_dir }}/{{ ansible_date_time.iso8601 | replace('T', '_') | replace(':', '-') | replace('+', '-UTC') }}-config.yaml"
|
||||||
|
mode: '0640'
|
||||||
|
owner: "{{ ansible_user_id | default(omit) }}"
|
||||||
|
group: "{{ ansible_user_gid | default(omit) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Load configuration changes
|
||||||
|
ansible.builtin.include_vars:
|
||||||
|
file: "{{ config_file }}"
|
||||||
|
name: config_changes
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
- name: Create merged configuration
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
new_config: "{{ patroni_config.content | from_json | combine(config_changes, recursive=True) }}"
|
||||||
|
|
||||||
|
- name: Validate configuration changes
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: |
|
||||||
|
python3 -c '
|
||||||
|
import yaml, json, sys;
|
||||||
|
try:
|
||||||
|
yaml.safe_load(sys.stdin.read())
|
||||||
|
sys.exit(0)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Invalid YAML: {str(e)}")
|
||||||
|
sys.exit(1)'
|
||||||
|
stdin: "{{ new_config | to_nice_yaml }}"
|
||||||
|
register: config_validation
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
- name: Save new config as last.yaml
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: |
|
||||||
|
# Updated at {{ ansible_date_time.iso8601 }}
|
||||||
|
{{ new_config | to_nice_yaml }}
|
||||||
|
dest: "{{ config_dir }}/last.yaml"
|
||||||
|
mode: '0640'
|
||||||
|
owner: "{{ ansible_user_id | default(omit) }}"
|
||||||
|
group: "{{ ansible_user_gid | default(omit) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Generate and display colored diff
|
||||||
|
block:
|
||||||
|
- name: Create temp files for diff
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
cat > /tmp/old_config.yml << 'EOL'
|
||||||
|
{{ patroni_config.content | from_json | to_nice_yaml }}
|
||||||
|
EOL
|
||||||
|
cat > /tmp/new_config.yml << 'EOL'
|
||||||
|
{{ new_config | to_nice_yaml }}
|
||||||
|
EOL
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Execute diff with colors
|
||||||
|
ansible.builtin.command: >
|
||||||
|
diff --color=always -u /tmp/old_config.yml /tmp/new_config.yml
|
||||||
|
register: diff_result
|
||||||
|
changed_when: diff_result.rc in [0,1]
|
||||||
|
failed_when: diff_result.rc > 1
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
- name: Cleanup temp files
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/tmp/{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- old_config.yml
|
||||||
|
- new_config.yml
|
||||||
|
delegate_to: localhost
|
||||||
|
connection: local
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display diff line by line
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ item }}"
|
||||||
|
loop: "{{ diff_result.stdout_lines }}"
|
||||||
|
when: diff_result.stdout_lines | length > 0
|
||||||
|
loop_control:
|
||||||
|
label: ""
|
0
roles/prepare/templates/.gitkeep
Normal file
0
roles/prepare/templates/.gitkeep
Normal file
0
roles/prepare/tests/.gitkeep
Normal file
0
roles/prepare/tests/.gitkeep
Normal file
10
vars/secrets.yml
Normal file
10
vars/secrets.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
37376136623761343135636239653137353661303631663536613265366431333339663866643265
|
||||||
|
3033653765613632313661393166363238643137346330620a643233623433633963333035646466
|
||||||
|
34633366623262643165326331333937623064356131306663623362663663343861383735616365
|
||||||
|
3363646132393166310a353965346531616330396666383732656430633630323438326161323965
|
||||||
|
64323865636265303331663166393232376138663965613361623361303663353737623238373435
|
||||||
|
30316161616234356264643762653036626132613664316137646665323335663232393535353131
|
||||||
|
37636331646364313839653438323461353638363936623131626161353936303839393533326162
|
||||||
|
31623833313834646233303961656633633933386135396439373463623362316561313138643631
|
||||||
|
6663
|
@ -1 +1 @@
|
|||||||
password123
|
password123
|
||||||
|
Loading…
x
Reference in New Issue
Block a user