Haz más con menos. Aplicando DRY a Jenkins

0
2985

Índice de contenidos


1. Introducción

DevOps ha venido para quedarse. Y con ello todas las tareas de administración asociadas. Cada día aparecen nuevas herramientas o se potencian las existentes, pero un fiel compañero siempre ha sido Jenkins. Hoy vamos a comprobar cómo podemos aplicar el principio DRY (don’t repeat yourself) a nuestros jobs de Jenkins.

Digamos que tenemos nuestras tareas de administración automatizadas y nos damos cuenta de que estamos empezando a repetir un patrón de actuación: preparar entorno, ejecutar acción, validar estado.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 17′ (2’5 Ghz i7, 16GB DDR3).
  • Sistema Operativo: Mac OS High Sierra 10.13
  • Entorno de desarrollo: JetBrains IntelliJ
  • Jenkins 2.60.3 con los plugins de pipeline instalados


3. Crear una shared library en Jenkins


3.1. Definiendo el pipeline común

Lo primero que necesitamos a la hora de definir una librería es conocer qué queremos hacer. En nuestro caso vamos a definir un pipeline muy sencillo que únicamente haga un echo de los parámetros recibidos.

TestPipeline.goovy
import org.jenkinsci.plugins.*

String parm1
Boolean parm2
String parm3

TestPipeline withParm1(String parm1) {
    this.parm1 = parm1
    return this
}

TestPipeline withParm2(Boolean parm2) {
    this.parm2 = parm2
    return this
}

TestPipeline withParm3(String parm3) {
    this.parm3 = parm3
    return this
}

def execute() {
    pipeline {
        stage("Setup environment") {
            echo "Parameter: ${parm1}"
        }
        stage("Should execute action: ${parm2}") {
            if (parm2) {
                echo "Executing action as stated by parm2: ${parm2}"
            }
        }
        stage("Verify state") {
            echo "Executing ${parm3}"
        }
    }
}

return this

Es muy importante que la última sentencia del fichero sea return this. Si no, Jenkins no será capaz de cargar correctamente el pipeline y nos fallará.

Para hacer uso de las librerías, estas se deben importar desde un sistema de control de versiones (GitLab, GitHub, etc). Además, el repositorio debe seguir una estructura determinada. Queda bastante bien explicado en la página oficial de documentación:

    (root)
    +- src                     # Groovy source files
    |   +- org
    |       +- foo
    |           +- Bar.groovy  # for org.foo.Bar class
    +- vars
    |   +- foo.groovy          # for global 'foo' variable
    |   +- foo.txt             # help for 'foo' variable
    +- resources               # resource files (external libraries only)
    |   +- org
    |       +- foo
    |           +- bar.json    # static helper data for org.foo.Bar

Esta estructura nos da la potencia suficiente como para organizar nuestras librerías siguiendo una jerarquía que permita reutilización. Por ejemplo, podemos definir una serie de métodos dentro de una clase Maven.groovy que nos faciliten las tareas de compilación, generación de release, análisis con SonarQube, etc. De esta forma, cualquier pipeline que quiera utilizarlas podrá hacerlo sin tener que definir todas las opciones necesarias y sólo las imprescindibles.


3.2. Definir la shared library en Jenkins

Para definirla de forma efectiva en Jenkins, nos dirigiremos a la sección de administración, configurar el sistema y a la sección Global Pipeline Libraries. Añadiremos nuestra ruta al repositorio Git y le daremos un nombre por el que la identificaremos. Es importante definir como versión por defecto master, ya que como veremos después se puede sobreescribir, pero lo lógico es que sea master la que nos interese siempre.

Jenkins-Shared-Libraries
Definición de Jenkins Shared Libraries


4. Creando nuestro Jenkinsfile

Tomemos como ejemplo un Jenkinsfile mínimo:

Jenkinsfile
#!/usr/bin/env groovy

@Library("Test") _
import com.autentia.test.TestPipeline

new TestPipeline()
    .withParm1(params.environment)
    .withParm2(params.dryRun)
    .withParm3(params.verifier)
    .execute()

Con la directiva @Library estamos importando la librería definida con ese nombre. El guión bajo (_) hace referencia a la versión, en este caso a la por defecto, ¿recordáis haberla indicado como master?, pues era para simplificar este punto. Además, si tenemos en cuenta que cada vez que hacemos referencia a params son parámetros del Job de Jenkins, acabamos de construir un Jenkinsfile genérico para todos nuestros Jobs. Lo único que tendrán que hacer es fijar los parámetros necesarios, pero el flujo de ejecución será común para todos. ¿No es más sencillo de mantener? ¿Y de entender? ¿Y de mantener ;)?


5. Conclusiones

Con un poco de cariño podemos empezar a construir una serie de librerías con funcionalidad común que nos faciliten la vida, todo manteniendo una coherencia entre los jobs que lo utilicen. El mantenimiento de las librerías está centralizado, por lo que será más sencillo y tendremos menos errores. Una de las grandes ventajas que tienen es que al actualizar el código de la librería, todo job que la utilice refrescará inmediatamente los cambios.

Estamos acostumbrados a aplicar patrones y principios de diseño a nuestro código, y cuando nos enfrentamos a un pipeline descuidamos lo aprendido. No nos olvidemos que tenemos que aplicar el mismo mimo al ciclo de vida.


6. Referencias

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad