Extendiendo sonarqube con un plugin personalizado

En este tutorial veremos cómo extender sonarqube, la herramienta open source más conocida para medir la calidad del código de nuestros proyectos, con un plugin personalizado.

Extendiendo sonarqube con un plugin personalizado.

 

0. Índice de contenidos.

1. Introducción

Ya hemos hablado en otros tutoriales sobre cómo extender sonarqube para, por ejemplo, añadir nuestras propias reglas haciendo uso de xpath. En este tutorial vamos a ir un paso más allá, creando nuestro propio plugin. Los puntos de extensión sobre los que podemos trabajar son básicamente los siguientes:
  • añadir reglas de código,
  • añadir eventos para notificar de los resultados de análisis a aplicaciones externas,
  • añadir métricas,
  • añadir el soporte de nuevos lenguajes de programación,
  • añadir el soporte de sistemas de control de versiones,
  • añadir proveedores de autenticación,
  • extender con widgets y traducir la interfaz de usuario.
A todo lo anterior podemos añadir que siempre existe la posibilidad de hacer uso del API REST para desarrollar nuestras propias aplicaciones, fuera del entorno propiamente dicho de sonarqube. El objetivo de este tutorial es examinar las posibilidades que tenemos para la creación de un plugin propio, que se instale como tal en sonarqube y que permita incluir en la interfaz de usuario un widget que explote la información de las métricas de un proyecto. Y si a alguien le sabe a poco, además vamos a trabajar con un sonarqube dockerizado, para que montarnos el entorno de desarrollo y desplegar el plugin sea sencillo.

2. Entorno.

El tutorial está escrito usando el siguiente entorno:
  • Hardware: Portátil MacBook Pro 15′ (2.5 GHz Intel Core i7, 16GB DDR3).
  • Sistema Operativo: Mac OS El Capitan 10.11
  • Sonarqube 5.6
  • Docker 1.11.2.

3. Preparación del entorno de desarrollo.

Como comentábamos en la intro vamos a trabajar con una imagen de docker para desplegar sonarqube y hacer nuestras pruebas de despliegue del plugin; en realidad vamos a trabajar con docker-compose que nos permite configurar y desplegar en una sola acción más de un contenedor de docker ya que, en nuestro caso, necesitamos una base de datos, no queremos trabajar con la embebida y la propia aplicación de sonarqube. Para ello, lo primero es crearnos un fichero docker-compose.yml con el siguiente contenido que luego ubicaremos en la raíz de nuestro proyecto.

Estamos usando la sintaxis de la última versión de docker con dos contenedores basados en las imágenes de sonar:5.6 y mysql:5.6; la de sonar depende de la de mysql y ambas se despliegan en una red creada ad hoc. La parte de “volumes” del contenedor de sonarqube nos permite configurar un volumen de datos y “exponer” un directorio de nuestra máquina hacia el contenedor, de modo que para nuestros propósitos podemos mantener los ficheros de configuración de sonar dentro de nuestro propio proyecto para configurarlos de forma externa al contenedor y distribuirlos con el propio entorno de desarrollo. Por último, haremos uso de un par de instrucciones dentro de un fichero Dockerfile para que, crear una imagen nueva copiando el plugin generado al directorio de plugins del contenedor y así poder probar nuestro desarrollo.

En este último punto nos estamos adelantando a la creación del proyecto del plugin propiamente dicho, pero es justo lo que vamos a ver a continuación.

4. Creación del proyecto.

Sonarqube proporciona soporte para crear un proyecto de plugin basado en maven, para ello no tenemos más que crearnos un proyecto java simple, haciendo uso de arquetipo maven-archetype-quickstart y sustituir el pom.xml por uno como el siguiente:

En el plugin de empaquetación debemos indicar la ruta y nombre de una clase que hará el registro de nuestros componentes del plugin en los distintos contextos.

Con el soporte del plugin de maven de sonarqube, se empaquetará un jar preparado para ser desplegado en sonarqube, justo el artefacto que configurábamos en el punto anterior para copiar en el directorio de plugins del contenedor en el Dockerfile. Solo nos falta un paso más, opcional, si queremos configurar sonar en modo desarrollo podemos extraer el fichero sonar.properties del contenedor y ubicarlo en un directorio de conf que será el que se monte como volumen en el contenedor: Si ejecutamos la siguiente secuencia de comandos, se levantarán los dos contenedores en segundo plano:

Como comentaba, podemos copiarnos el fichero de propiedades haciendo uso del siguiente comando:

En el mismo podríamos modificar la siguiente variable

y reiniciar el contenedor para que tome en caliente los cambios

Al arrancar docker, si utilizamos las tools, nos informa de la ip asociada a la máquina virtual:

Si accedemos desde un navegador a sonarqube, en mi caso http://192.168.99.100:9000, entramos con un usuario administrador (admin:admin) al update center
sonarqube-custom-plugin-01
Podríamos comprobar que el plugin se encuentra instalado
sonarqube-custom-plugin-02
Si tenemos problemas con la copia del artefacto, del jar, podemos entrar en el contenedor:

Para comprobar que el jar está en la ubicación correcta:

Si no se encontrarse, siempre podemos ejecutar una copia manual desde el contenedor, ejecutando el siguiente comando:

Si tenemos sonar en modo desarrollo se recargará, sino podemos recargar el contenedor manualmente

El modo de trabajo a partir de este punto será, modificar el código del proyecto, compilar y empaquetar con el soporte de maven:

Después, hacer una copia del artefacto generado:

y esperar a que se redespliegue o hacer un reinicio manual

También podríamos montar un volumen externo que apunte al directorio de plugins fuera, en vez de hacer copias del fichero; sería otra opción.

5. Explorando las distintas opciones.

Una vez disponemos de un entorno de desarrollo y con el objetivo en mente de crear nuestro plugin debemos ver qué tipo de funcionalidad vamos a implementar puesto que en función de la misma tenemos accesibles distintos tipos de APIs de sonarqube:
  • @Batch: para la ejecución de componentes dentro del proceso de análisis de código,
  • @ComputeEngineSide: para generar informes en base al análisis de código con la posibilidad de persistir la información en la base de datos,
  • @ServerSide: para generar componentes del lado del servidor, que respondan a llamadas HTTP
Nuestro objetivo es integrarnos en la UI de sonarqube, configurar un widget que permita realizar una llamada a un componente de servidor y probar a descargar información sobre un análisis ya realizado.

5.1. UI widget.

Lo primero que vamos a hacer es configurar un widget que permita la inclusión de un componente visual en el dashboard a nivel de proyecto. Tanto los componentes como los controladores se programan en Ruby, más bien en Ruby On Rails. Para ello, creamos una clase que implemente RubyRailsWidget con un código como el siguiente: Proporcionamos un identificador de widget, un título y una ruta a la plantilla que tendrá la “lógica de presentación”. Este componente de tipo widget lo tenemos que registrar en la clase que implementa la interfaz Plugin y que previamente ya hemos configurado en el pom.xml como pluginClass: Un último paso, sería crear la plantilla del widget, bajo la carpeta de resources de nuestro proyecto maven, en la ubicación indicada widgets, con un código similar al siguiente: Lo único que estamos haciendo por ahora es incluir un componente visual con un enlace que permite la invocación a un componente de servidor al que le pasaremos como parámetro el id del proyecto. compilamos, empaquetamos, copiamos el artefacto y redesplegamos… A nivel visual, en el dashboard del proyecto, podremos añadir el widget:
sonarqube-custom-plugin-03
Y hacer uso también del mismo
sonarqube-custom-plugin-04
Aunque ahora invoca a un recurso del servidor que aún no existe. La renderización del widget también se puede probar invocando a la siguiente URL con el id del widget:

5.2. Controlador en ROR.

La primera opción que tenemos para implementar un componente del lado del servidor es crear un controlador en ruby, que reciba la petición con el id del proyecto y devuelva información sobre las métricas del mismo. Para registrar un controlador debemos configurar una aplicación ROR añadiendo en la ruta org/sonar/ror/tntaudit/ un fichero init.rb Dentro de esa misma ruta crearemos una carpeta /app/controllers/ y otra /app/view de modo que tendremos una estructura de directorios como la siguiente:
sonarqube-custom-plugin-05
El código del controlador podría tener el siguiente contenido como prueba de descarga de información del proyecto

Para invocar al controlador no tenemos más que realizar una llamada a la siguiente URL, http://192.168.99.100:9000/export_project_issues/index?id=1, no hay prefijos, por eso recomiendan tener cuidado con la nomenclatura para no pisar los controladores propios de la aplicación. El resultado de la invocación será un código como el siguiente:

No hay documentado un API que permita el acceso a las métricas o las violaciones de código de un proyecto desde un controlador Ruby, este tipo de componente está más orientado a generar una vista, renderizaría por defecto una view en la ruta /ror/tntaudit/app/views/export_project_issues, como el nombre del controlador y la acción definida /index.erb; convención sobre configuración! La idea sería generar una vista HTML y con el soporte del API javascript del propio sonarqube consumir los servicios REST que permiten el acceso a la información de métricas de un proyecto. Cabría también la posibilidad de invocar a un componente Java desde el controlador Ruby, con un código como el siguiente:

Donde AuditReport es una clase java que se encuentra en el paquete com.autentia.sonar.plugins.reports y tntaudit el identificador de nuestro plugin. Esa clase podría tener la lógica de generación de información si bien para acceder a la misma la propuesta de sonarqube es hacer uso del API REST y no está especialmente pensada la inyección de dependencias para disponer de ese API en un componente Java.

5.3. Web service en java.

Hay una alternativa al uso de controladores Ruby si lo que pretendemos es únicamente descargar información sobre métricas del proyecto en un formato estándar; es el uso de servicios REST programados en Java. Para crear un servicio web debemos añadir una clase con un código como el siguiente:

Se pueden definir parámetros obligatorios o no. El servicio hay que registrarlo en el plugin junto con el widget:

Para consumir el servicio REST bastaría con realizar una invocación vía GET a la siguiente URL: http://192.168.99.100:9000/api/reports/audit/export?project=com.autentia.tnt:tnt-labs, la idea es realizar esa llamada desde el código HTML del widget. Ahora sí, desde el servicio web o desde una clase delegada y apoyándonos en la petición original pueden desencadenar peticiones haciendo uso de una conexión local a servicios web del api pública desde nuestro servicio web, que es el camino recomendado por el fabricante.

6. Referencias.

7. Conclusiones.

Una vez estudiadas las posibilidades y teniendo acceso a las métricas y violaciones del proyecto, solo nos queda programar la exportación en el formato que elijamos.

A disfrutarlo!. Un saludo. Jose