ChatOps utilizando Errbot 1 Junio 2022

ChatOps utilizando Errbot

Hace un tiempo, uno de nuestros clientes nos contactó por una necesidad recurrente: obtener copias de las bases de datos de sus sistemas para que el equipo de desarrollo y QA puedan analizar datos. El problema, es que el acceso a estas bases de datos está restringido y los usuarios no acceden a servidores en producción.

Fue así como propusimos el desarrollo de un robot que se encargue de realizar las tareas en nombre de aquellos usuarios permitidos para solicitar estos pedidos. La forma más simple de hacerlo, fue pensar en cómo nos solicitaban los pedidos al personal de Mikroways y replicar el comportamiento de personas con un robot. Como la herramienta de comunicación con el cliente es Slack, procedimos en configurar un ChatBot que atienda pedidos en un canal al que solamente determinados usuarios pertenecen.

Hay que recalcar que esta tendencia, también conocida como ChatOps, presenta una mejora sustancial a los procesos manuales ya que permite a los usuarios finales ejecutar tareas repetitivas de manera desatendida y con la seguridad de no cometer errores.

Siendo así, en este post realizaremos un breve aporte respecto al uso de robots de chat para la automatización de tareas relacionadas a la operación de herramientas de software. Por lo cual seguiremos dos ejemplos, uno básico que muestra las estructuras principales de la herramienta utilizada y otro donde se ejemplifica un hipotético caso real.

Errbot

Errbot es un ChatBot desarrollado en Python, creado para conectar servicios de chats llamados Backends, con distintos plugins Python llamados BotPlugins. Su instalación se puede realizar siguiendo la documentacion oficial. En este tutorial haremos uso de la imagen oficial de Docker para las pruebas. Por eso sera necesario contar con Docker instalado previamente.

Nota: Los archivos que necesitaremos en este tutorial serán creados sobre la carpeta /tmp/errbot/ para organizar el trabajo.

BotPlugins

Para comprender el funcionamiento de los BotPlugins se suele recurrir a mostrar ejemplos. De esta forma, para comenzar, vamos a crear un plugin de HelloWorld. El primer paso entonces será el de crear la carpeta en donde se alojará nuestro plugin:

mkdir -p /tmp/errbot/plugins/hello/

Como paso siguiente debemos ejecutar el comando para crear un archivo cuyo contenido es un simple BotPlugin de ejemplo que llamaremos helloworld.py:

cat <<EOT > /tmp/errbot/plugins/hello/helloworld.py
from errbot import BotPlugin, botcmd

class HelloWorld(BotPlugin):
    """Example 'Hello, world!' plugin for Errbot."""

    @botcmd
    def hello(self, msg, args):
        """Say hello to the world."""
        return "Hello, world!"
EOT

Se puede observar que este plugin agrega la posibilidad de ejecutar el comando !hello (debido al decorator sobre la función) en nuestro Backend y como resultado devolverá el mensaje "Hello, world!" al ser invocado. Además, el comando !help devolverá la ayuda que se corresponde con el docstring diciendo: Say hello to the world.

Los plugins demás deben estar acompañados de un archivo .plug que contiene los metadatos del plugin. Con el siguiente bloque de código crearemos el archivo que contiene la estructura de metadatos del plugin llamado helloworld.plug.

cat <<EOT > /tmp/errbot/plugins/hello/helloworld.plug
[Core]
Name = HelloWorld
Module = helloworld

[Python]
Version = 3

[Documentation]
Description = Example "Hello, world!" plugin
EOT

Así tendremos nuestro primer plugin llamado helloworld creado.

Backends

Los Backends son interfaces definidas en el nucleo de Errbot que implementan un conjunto de comandos para poder comunicarse con diferentes servicios de chat. Algunos ejemplos de éstos son Discord, Slack, Telegram, entre otros. Además debemos tener en cuenta los Backends desarrollados por la comunidad, por ejemplo el de SlackV3 e inclusive Mattermost. La ventaja que tiene esta herramienta es que los incorpora transparentemente, es decir, si en algún momento se necesita cambiar de un Backend a otro se puede realizar sin mayores cambios en los plugin que hemos desarrollado. De todas formas, no necesariamente implementaran todas las funciones de la interfaz. Por ejemplo, el backend de Discord oficial no tiene implementada la función de envió de archivos. Esto puede verificarse desde la implementacion de cada backend.

Para seguir con el tutorial del plugin HelloWorld crearemos el archivo config.py que contiene la configuración de Errbot e indicaremos un backend a utilizar. Para esto podremos ejecutar el siguiente comando:

cat <<EOT > /tmp/errbot/config.py
import logging

BACKEND = "Text"
BOT_IDENTITY = {
  "username": "@errbot"
}
STORAGE = "Memory"
BOT_DATA_DIR = "/tmp/"
BOT_LOG_FILE = None
BOT_LOG_LEVEL = logging.INFO
BOT_EXTRA_PLUGIN_DIR = "plugins/"
BOT_ADMINS = ("@localhost",)
EOT

Esta configuración, elige como backend Text el cual es muy útil para entornos de prueba ya que inicia Errbot en modo consola.

Finalmente tendremos que ejecutar el servicio desde nuestra consola con el comando que observaremos a continuación:

# Creara una consola en el servicio de Errbot y nos permite ejecutar comandos
docker run -it --rm -v /tmp/errbot/:/home/errbot/ errbotio/errbot
# Para ver la lista de comandos disponibles
!help
# Para ejecutar el comando del plugin
!hello

Solución propuesta

Para solucionar el problema mencionado en la introducción, crearemos un nuevo plugin llamado dumps que utiliza la libreria subprocess para invocar procesos en el host. La intención es utilizar herramientas externas como mysqldump y gzip para hacer los resguardos de la base y luego comprimirlos.

Nuevo plugin

De esta forma, procedemos con la creación del plugin considerando únicamente el motor MySQL o Mariadb, realizando un dump y luego mostrando el resultado del comando mysqldump en la consola. Entonces, primero debemos crear la carpeta donde se alojará nuestro plugin:

mkdir -p /tmp/errbot/plugins/dumps/

A continuación creamos el archivo dumps.py. Podemos ejecutar el siguiente comando para crearlo:

cat <<EOT > /tmp/errbot/plugins/dumps/dumps.py
# Importamos las librerias necesarias
import os
import sys
import time
import pipes
import subprocess
import time
from errbot import BotPlugin, arg_botcmd, botcmd, ValidationException

class Dumps(BotPlugin):
  # Creamos el comando dumpdb al cual se le pasa un argumento
  # Este argumento es el nombre de la base de datos
  @arg_botcmd('database', type=str)
  def dumpdb(self, msg, database=None):
    """Realiza el dump de de la db especificada."""

    # En este string se genera el comando que se ejecuta
    # Hay que recalcar que mysqldump debe estar instalado en el host
    dumpcmd = ["mysqldump",
              "-h","mariadb",
              "-u","user",
              "-p"+"password",
              database]

    # Ejecutamos el comando de mysqldump
    dump = subprocess.Popen(dumpcmd,
                      stdout=subprocess.PIPE,
                      stderr=subprocess.PIPE)
    out, err = dump.communicate()

    # Devolvemos el resultado
    return out.decode() + err.decode()
EOT

Y siguiendo tenemos su correspondiente dumps.plug. Podemos ejecutar el comando para crearlo:

cat <<EOT > /tmp/errbot/plugins/dumps/dumps.plug
[Core]
Name = Dumps
Module = dumps

[Documentation]
Description = A errbot plugin for mysql dumps.

[Python]
Version = 3
EOT

Con ambos archivos tendremos nuestro plugin dumps armado.

Configuración

El siguiente paso es configurar Errbot teniendo en cuenta el Backend que utilizaremos. La documentación oficial nos provee un archivo de configuración de ejemplo en este enlace. Debemos observar con atención que en cada Backend las identidades que necesitan configurarse son distintas.

Para seguir el ejemplo, podremos utilizar el archivo de configuración que ya hemos creado, aunque se sugiere verificar la documentación para trabajar con otros Backends y agregar la funcionalidad de send_stream_request para dejar los resguardos en un servicio de mensajería (por ejemplo con SlackV3).

Para finalizar, podremos ejecutar los siguiente comandos para verificar que el nuevo plugin que hemos armado funciona correctamente:

# Creamos una nueva red Docker donde se hara la prueba
docker network create errbot-test

# Iniciamos la base de datos Mariadb en un contenedor
docker run --detach --network errbot-test --name mariadb -e MARIADB_USER=user \
  -e MARIADB_PASSWORD=password -e MARIADB_ROOT_PASSWORD=password2 \
  -e MARIADB_DATABASE=test  mariadb:latest

# Iniciarmos Errbot y deja la consola activa para interactuar
# Nota: la imagen se modifico para agregarle el binario de mysqldump
docker run -it --network errbot-test --name errbot --rm \
  -v /tmp/errbot/:/home/errbot/ manuelchichi/errbot-mysqldump:1.0

# Ejecutamos el comando en la consola del contenedor
!dumpdb test
# Podemos ver el backup arrojado como texto plano por la herramienta

# Una vez finalizado podremos ejecutar los siguiente comandos para limpiar la
# prueba
docker stop mariadb
docker rm mariadb
docker network rm errbot-test
rm -rf /tmp/errbot

El siguiente video muestra cómo es la interacción con el bot:

Conclusiones

La solución que se propuso demostró gran satisfacción del lado del cliente ya que se automatizo una tarea que era muy frecuente y que muchas veces ralentizaba el trabajo de un área de desarrollo completa. De nuestro lado, saco el trabajo adicional ocasionado por tener que realizar estas tareas en situaciones especificas.

Como experiencia nos hemos llevado la sorpresa de que configurar la herramienta es una tarea sencilla, que tiene mucha versatilidad y que podría ser de gran utilidad en equipos de DevOps. De nuestro lado, estamos ansiosos de identificar otros escenarios en los cuales puedan ofrecerse este tipo de soluciones.