Slack Bot para Spring Info Actuator

WARNING

Este post se puede considerar una continuación a mi-primer-slack-bot.html, de hecho sólo voy a contar la parte de código de interés porque todo lo demás ya se explica en ese post

Si tu aplicación usa el framework Spring o Grails entonces dispones de la funcionalidad Spring Actuator, que te permite monitorizar y obtener información sobre tu aplicación en tiempo real a través de unos endpoints, como el típico /health , /env o /info

En concreto /info ofrece información como el nombre de la aplicacion, version de la misma, o del java, etc. Además si encuentra un fichero git.properties es capaz de adjuntar la información del mismo en la response

Poder acceder a esta información de forma fácil puede ser una gran utilidad. Por ejemplo poder consultar qué versión de API está corriendo en el entorno de desarrollo puede servirle al desarrollador de front para comprobar si es la correcta contra la que quiere probar sus desarrollos.

Aplicación

En el caso en concreto que vamos a ver en este post, se trata de una aplicación Grails la cual incluye por defecto tanto la dependencia a spring actuator como una configuración mínima para exportar estos endpoints (más info en http://docs.grails.org/3.3.4/guide/spring.html#actuators )

Así mismo, como norma básica, el acceso a dicho endpoint estará protegido con Spring Security por lo que primero habrá que hacer un login para obtener tokens de acceso para poder acceder al endpoint indicado.

Por último, para poder ofrecer la máxima cantidad de información al usuario (por ejemplo el mensaje del último commit), especificaremos que el endpoint use el formato full (https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-application-info-git)

Conectividad

Dependiendo de dónde se encuentre alojada la aplicación, así como el bot, tendrás que lidiar con diferentes aspectos de conectividad.

En este caso en concreto el bot se va a alojar en Netlify, el cual lo expone mediante una url pública en Internet. Así mismo la aplicación es abierta (pero protegida) al público, por lo que también cuenta con una url pública. Por último, para este ejemplo vamos a contar con que los 3 entornos, dev, staging y prod, están accesibles desde Internet

Si no fuera así, por ejemplo tu api de prod está abierta al público, pero los entornos de desarrollo están en una red interna, tendrías que buscar una forma de unir los 3 "mundos": slack → bot → aplicación

Creación del proyecto

Para la creación del proyecto y su publicación te remito al post mi-primer-slack-bot.html donde se explica de forma detallada. De hecho si has conseguido seguirlo y publicar tu primer bot puedes reutilizarlo pues lo que vamos a hacer es añadir una función nueva (y algunas variables de entorno)

Nueva function

Siguiendo los pasos del post anterior crearemos una nueva function git-status:

netlify function:create

(O simplemente ya que tenemos todo montado crearemos un nuevo subdirectorio dentro de functions y añadiremos el código siguiente)

git-status.js
const querystring = require("querystring");
const axios = require("axios");

const { SLACK_BOT_TOKEN } = process.env;
const { REPO_GIT } = process.env;

const handler = async (event) => {
  try {

    if (event.httpMethod !== "POST") {
      return { statusCode: 405, body: "Method Not Allowed" };
    }

    const params = querystring.parse(event.body);

    const environment = params.command.toUpperCase().substring(1)

    const REMOTE_API = process.env[`${environment}_API`];
    const REMOTE_USERNAME = process.env[`${environment}_USERNAME`];
    const REMOTE_PASSWORD = process.env[`${environment}_PASSWORD`];

    let login = await axios.post(`${REMOTE_API}/api/login`,{
      username:REMOTE_USERNAME,
      password:REMOTE_PASSWORD,
    })
    let access_token = login.data.access_token

    let version = await axios.get(`${REMOTE_API}/info`,{
      headers:{
        'Authorization':`Bearer ${access_token}`
      }
    })

    const data = {
      text: `
*${environment}* *${version.data.git.commit.message.short}
Version *${version.data.app.version}*
Cooked at *${version.data.git.commit.time}*
last commit: *${version.data.git.commit.id}*
${REPO_GIT}/${version.data.git.commit.id}
`,
      channel: params.channel_id,
      as_user: true
    };

    const result = await axios.post('https://slack.com/api/chat.postMessage', data, {
      headers:{
        'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
        'X-Slack-User': params.user_id
      }
    })

    return {
      statusCode: 200
    }

  } catch (error) {
    console.log(error)
    return { statusCode: 500, body: error.toString() }
  }
}

module.exports = { handler }

Como puedes ver el código es muy similar al del comando anterior. Simplemente usaremos el comando que nos envía el usuario para detectar que entorno es el que nos está pidiendo. Así por ejemplo si el usuario ejecuta el comando /prod pasaremos a mayusculas esta cadena y quitaremos el primer caracter

const environment = params.command.toUpperCase().substring(1)

y usaremos la constante environment como prefijo para buscar las credenciales y url a invocar

const REMOTE_API = process.env[`${environment}_API`];
const REMOTE_USERNAME = process.env[`${environment}_USERNAME`];
const REMOTE_PASSWORD = process.env[`${environment}_PASSWORD`];

Básicamente realizaremos un login y obtendremos el access_token para poder invocar al endpoint de Spring Actuator.

Por último renderizamos toda la información como una cadena Markdown:

const data = {
  text: `
*${environment}* *${version.data.git.commit.message.short}
Version *${version.data.app.version}*
Cooked at *${version.data.git.commit.time}*
last commit: *${version.data.git.commit.id}*
${REPO_GIT}/${version.data.git.commit.id}
`,

Netlify

Por la parte del bot simplemente resta configurar todas las variables de entorno en Netlify:

PROD_API=http://xxxxx.com/

STAGING_API=http://yyyyy.com/

DEV_API=http://zzzzz.com/

etc

Slack

Por último habilitamos nuevos comandos desde la consola de nuestra App de Slack apuntando a la nueva function creada (los 3 comandos apuntan a la misma url pues en base a el comando la function ya sabe qué ejecutar)

Follow comments at Telegram groupOr subscribe to the Channel Telegram channel

2019 - 2020 | Mixed with Bootstrap | Baked with JBake v2.6.5