Предполагается, что читающий знаком с saltstack (https://www.saltstack.com/), писал для него стейты, подключал пиллары или еще чего-нибудь). В этой заметке попробуем реализовать свой state через cmd.run - он будет изменять данные только в случае необходимости плюс покажет нам, что он будет менять в режиме test=true.
Задачка - пытаемся написать свой самописный state через использование cmd.run. Один из примеров использования - планируем через salt менять переменную mysql MAX_USER_CONNECTIONS для пользователей БД. Прямо точного стейта солта под этого функционал нет, обычно используют для этого модуль mysql.query.run (позволяет выполнять произвольные sql-команды).
Если опустить частности, то реализация через mysql.query.run может выглядеть так:
У нас на одном сервере может быть несколько рабочих инстансов БД, поэтому задаем такую структуру, pillar example:
mysql: system: server_pkg: percona-server-server-5.7 instances: master1: mysql_users: cacti: {{ credentials(password='*9D26') }} databases_privileges: "*.*": present: SELECT, PROCESS, SUPER max_user_connections: '20' ...
master1 - это название инстанса, у него есть пользователи (cacti и т.д.)
Стейт файл mysql.users (папка mysql/, файл users.sls в проекте salt):
{% for instance, data in mysql.instances.iteritems() %} {% for user, properties in data.mysql_users.iteritems() %} ... {% if properties.max_user_connections is defined %} limit_number_connections-{{ instance }}-{{ user }}: mysql_query.run: - connection_default_file: {{ data.debian.debian_cnf }} - database: 'mysql' - query: {{ "ALTER USER " ~ user ~ "@'%' WITH MAX_USER_CONNECTIONS " ~ properties.max_user_connections ~ ';' }} - output: "/tmp/salt_user_connections" {% endif %} {% endfor %}
В этом коде говорим, что для каждого инстанса mysql для каждого пользователя будет выполняться данный sql-запрос. Все бы хорошо, но неудобно то, что этот модуль будет исполняться каждый раз и каждый раз будет в терминале подсвечиваться что данный стейт выполнился, что он поменял значение перемнной mysql у пользователя, хотя может это и не требовалось (тоже самое будет и в режиме test=true), вдобавок пользователей может пару десятков => вывод в терминале будет захламлен информацией, можем пропустить что-то действительно важное, что может изменить state mysql.users.
Хочется сделать такую же логику, как и в стандартных стейтах солта - в test=true покажет, что он собирается изменить (в случае если это действительно требуется), в рабочем режиме - покажет что изменил, в последующем "прогоне" стейта не будет пытаться что-то менять. Все это также выражается в соответствующей подсветке терминала (желтым подсвечивает тогда, когда солт планирует изменить или уже поменял, зеленым - если ничего не требуется делать (расцветка может отличаться в зависимости от расцветки фона искомого терминала - он у меня белый, а не черный как обычно)).
Можно написать свой отдельный стейт именно под эту задачу (думаю это будет сложнее), а можно воспользоваться модулем cmd.run. Для этого нам понадобится stateful аргумент в cmd стейте. Документация солта на этот параметр.
Выполним 3 действия:
- напишем специальный shell скрипт
- копирование этого shell-скрипта на минион
- запуск скрипта в стейт-файле mysql.users через state cmd.run
Интересующая нас подсветка в терминале в стейте солта регулируется через простой протокол, на stdout подаем следующее:
echo "changed=yes comment='some comment1' - подсветит в терминале echo "changed=no comment="some comment2" - ничего не подсветит
Стало
{# копируем наш shell скрипт #} {% set script_path = '/root/mysql-max-user-connections.sh' %} copy-script-check_max_user_connections: file.managed: - name: {{ script_path }} - source: "salt://mysql/files/mysql-max-user-connections.sh" - user: root - group: root - mode: 700 ... {% if properties.max_user_connections is defined %} run-script-mysql-max-user-connections-{{ instance }}-{{ user }}: cmd.run: - name: /root/mysql-max-user-connections.sh -i {{ instance }} -u {{ user }} -l {{ properties.max_user_connections }} - stateful: - test_name: /root/mysql-max-user-connections.sh -i {{ instance }} -u {{ user }} -l {{ properties.max_user_connections }} -t true {% endif %}
Будет вызываться скрипт, который проверяет текущий max_user_connections, если он не отличается от заданного в пилларе, то ничего не делать; если отличается - применить изменения.
Соответственно, мы в нашем shell скрипте реализуем данную логику - передаем ему аргументами инстанс и пользователя, сверяем все, если надо - меняем параметр в БД, дополнительно в зависимости от варианта формируем нужный stdout, по которому salt будет понимать изменилось что-либо или нет.
Если бы просто написали
- stateful: True
то наш стейт cmd.run работал бы корректно только для варианта test=false (т.е. когда мы применяем изменения), для test=true подсветка в терминале говорила бы, что этот стейт будет менять переменную max_user_connections каждый раз. Чтобы и для test=true все работало, мы аргументу stateful передаем название скрипта, который будет исполняться; для удобства это тот же самый shell-скрипт, которому дополнительно передаем аргумент -t true.
Т.е. для salt-call state.sls mysql.users test=true будет вызываться скрипт
/root/mysql-max-user-connections.sh -i {{ instance }} -u {{ user }} -l {{ properties.max_user_connections }} -t true
Пример как это будет выглядеть после вызова (изменена настройка max_user_connections у пользователя cacti):
ID: run-script-mysql-max-user-connections-master1-cacti Function: cmd.run Name: /root/mysql-max-user-connections.sh -i master1 -u cacti -l 20 Result: None Comment: Test mode: Will alter MAX_USER_CONNECTIONS for cacti in instance master1 Started: 15:17:21.734542 Duration: 23.457 ms Changes:
Для salt-call state.sls mysql.users:
/root/mysql-max-user-connections.sh -i {{ instance }} -u {{ user }} -l {{ properties.max_user_connections }}
Что увидим в терминале:
ID: run-script-mysql-max-user-connections-apr-cacti_monitor Function: cmd.run Name: /tmp/mysql-max-user-connections.sh -i master1 -u cacti -l 20 Result: True Comment: Alter MAX_USER_CONNECTIONS for cacti in instance master1 Started: 15:19:26.544489 Duration: 29.948 ms Changes: ---------- changed: yes pid: 28250 retcode: 0 stderr: stdout:
Наш shell скрипт:
#!/bin/bash while getopts "i:u:l:t:" opt do case $opt in i) instance=${OPTARG};; u) user=${OPTARG};; l) desire_user_limit=${OPTARG};; t) test=${OPTARG};; esac done sql_get_info="SELECT max_user_connections FROM mysql.user where User='$user' and Host='%';" current_user_limit=`mysql --defaults-file=/etc/mysql/debian-$instance.cnf -s -N -e "$sql_get_info"` if [ "$test" = "true" ] then if [ "$desire_user_limit" = "$current_user_limit" ] then echo "" ## This echos an empty line and is required echo "changed=no comment='Test mode: MAX_USER_CONNECTIONS is actual for $user in instance $instance'" else echo "" ## This echos an empty line and is required echo "changed=yes comment='Test mode: Will alter MAX_USER_CONNECTIONS for $user in instance $instance'" fi exit 0 else if [ "$desire_user_limit" = "$current_user_limit" ] then echo "" ## This echos an empty line and is required echo "changed=no comment='MAX_USER_CONNECTIONS is actual for $user in instance $instance'" else sql_edit_max_user_connections="ALTER USER $user@'%' WITH MAX_USER_CONNECTIONS $desire_user_limit;" edit_max_user_connections=`mysql --defaults-file=/etc/mysql/debian-$instance.cnf -s -N -e "$sql_edit_max_user_connections"` echo "" ## This echos an empty line and is required echo "changed=yes comment='Alter MAX_USER_CONNECTIONS for $user in instance $instance'" fi fi
Bash скрипт для версии mysql 5.7 и выше (там синтаксис изменения переменной max_user_connections чуть другой, чем в 5.6 и ниже).
Вот так, набросали свой простенький стейт через готовый функционал saltstack, теперь мы меняем параметр БД только когда это нужно!