Свой state для saltstack через cmd.run

Предполагается, что читающий знаком с 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, теперь мы меняем параметр БД только когда это нужно!

Please publish modules in offcanvas position.