223 lines
7.3 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
# tasks/main.yml
- name: Apply new Patroni configuration
ansible.builtin.uri:
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/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
notify: "config applied"
- name: Wait for config propagation # noqa: no-handler
ansible.builtin.wait_for:
timeout: 30
delay: 5
when: apply_result is changed
- name: Check for pending restarts # noqa: no-handler
when: apply_result is changed
run_once: true
block:
- name: Get cluster status with retry
ansible.builtin.uri:
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/cluster"
method: GET
return_content: yes
status_code: 200
register: cluster_status
until: cluster_status.json is defined
retries: 3
delay: 2
delegate_to: localhost
run_once: true
- name: Check restart flags
ansible.builtin.set_fact:
needs_restart: >-
{{
(cluster_status.json.members |
map(attribute='pending_restart', default=false) |
select('equalto', true) | list | length > 0) or
(cluster_status.json.members |
map(attribute='tags.pending_restart', default=false) |
select('equalto', true) | list | length > 0)
}}
node_names: "{{ cluster_status.json.members | map(attribute='name') | list }}"
node_info: >-
{% set info = {} %}
{% for member in cluster_status.json.members %}
{% set _ = info.update({member.name: {'role': member.role}}) %}
{% endfor %}
{{ info }}
run_once: true
rescue:
- name: Set no restart needed
ansible.builtin.set_fact:
needs_restart: false
run_once: true
- name: Display restart warning
ansible.builtin.debug:
msg: |
{% if needs_restart %}
{% if autorestart %}
================================================
ПРЕДУПРЕЖДЕНИЕ: АВТОМАТИЧЕСКИЙ ПЕРЕЗАПУСК КЛАСТЕРА
================================================
Следующие ноды будут перезапущены:
{% for node in node_names %}
- {{ node }} ({{ node_info[node].role | default('UNKNOWN') }})
{% endfor %}
Для отмены нажмите Ctrl+C в течение 10 секунд
{% else %}
============================================
ВНИМАНИЕ: НЕОБХОДИМ РУЧНОЙ ПЕРЕЗАПУСК КЛАСТЕРА
============================================
Выполните на одной из нод:
patronictl restart -c /etc/patrony.yml {{ node_names | join(' ') }}
Ноды для перезапуска:
{% for node in node_names %}
- {{ node }} ({{ node_info[node].role | default('UNKNOWN') }})
{% endfor %}
{% endif %}
{% else %}
================================
ПЕРЕЗАГРУЗКА НЕ ТРЕБУЕТСЯ
================================
{% endif %}
delegate_to: localhost
run_once: true
when:
- needs_restart is defined
- node_names is defined
- node_info is defined
- name: Confirm automatic restart
ansible.builtin.pause:
prompt: "Подтвердите автоматический перезапуск кластера (Enter - продолжить, Ctrl+C - отмена)"
seconds: 10
when:
- needs_restart | default(false)
- autorestart | default(false)
delegate_to: localhost
run_once: true
- name: Execute cluster restart
when:
- needs_restart | default(false)
- autorestart | bool
- cluster_status is defined
- cluster_status.json is defined
- cluster_status.json.members is defined
run_once: true
block:
- name: Find nodes needing restart
ansible.builtin.set_fact:
nodes_to_restart: >-
{%
set nodes = []
%}{%
for member in cluster_status.json.members
%}{%
if member.pending_restart is defined and member.pending_restart or
member.tags.pending_restart is defined and member.tags.pending_restart
%}{%
set _ = nodes.append(member)
%}{%
endif
%}{%
endfor
%}{{
nodes
}}
- name: Restart nodes via API
ansible.builtin.uri:
url: "http://{{ item.host }}:{{ patroni_api_port }}/restart"
method: POST
body_format: json
body:
restart_pending: true
timeout: 60
status_code: [200, 503]
loop: "{{ nodes_to_restart | default([]) }}"
loop_control:
label: "{{ item.name }}"
register: restart_results
ignore_errors: yes
changed_when: >
restart_results.status == 200 or
restart_results.status == 503
- name: Wait for cluster stabilization
block:
- name: Check cluster status until stable
ansible.builtin.uri:
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/cluster"
method: GET
return_content: yes
status_code: 200
register: cluster_health
until: >
cluster_health.json.members |
selectattr('state', 'match', '^(running|streaming)$') |
list | length == cluster_health.json.members | length
retries: 12
delay: 10
delegate_to: localhost
run_once: true
- name: Show restart results
ansible.builtin.debug:
msg: |
========================
РЕЗУЛЬТАТЫ ПЕРЕЗАГРУЗКИ
========================
Нода: {{ item.item.name }}
Роль: {{ item.item.role }}
Статус: {% if item.status == 200 %}Успешно перезапущена{% elif item.status == 503 %}Перезапуск в процессе{% else %}Ошибка (код {{ item.status }}){% endif %}
Время выполнения: {{ item.elapsed }} сек
{% if item.item.pending_restart_reason is defined %}
Причина перезагрузки:
{% for param, values in item.item.pending_restart_reason.items() %}
- {{ param }}: было {{ values.old_value }}, стало {{ values.new_value }}
{% endfor %}
{% endif %}
------------------------
loop: "{{ restart_results.results | default([]) }}"
loop_control:
label: ""
run_once: true
- name: Archive old configurations # noqa: no-handler
when: apply_result is changed
run_once: true
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
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ (old_configs.files | sort(attribute='mtime'))[:-10] }}"
when: old_configs.matched > 10
delegate_to: localhost
connection: local
notify: "Log cleanup"