SetValue.NETSetValue.NET

Tareas dinámicas en Pipelines

January 24, 2021

Generic badge

En determinados escenarios podemos encontrarnos con la necesidad de ejecutar en los pipelines un número de operaciones idénticas en los que únicamente varía los parámetros que recibe. Aunque podríamos duplicar en el pipeline la tarea o tareas y modificar los parámetros que recibe cada una, tenemos soluciones más robustas y dinámicas con los ficheros YAML.

Para ilustrar y simplificar el ejemplo, imaginemos que necesitamos realizar una tarea que salude a una serie de usuarios por la consola. Esta operación, que a priori sería tan sencilla como pintar un "Hello Dude" por cada persona a la que queramos saludar, con la configuración clásica de un pipeline haríamos algo tal que así.

Pipelines Classic Editor

Crearíamos n tareas iguales en la que estableceríamos un mensaje por consola que hiciera el saludo. Bueno, no es difícil, se crea una operación, se clona tantas veces como se necesita y luego sólo es cambiar el nombre. Evidentemente, esto está bien si son pocas personas a las que hay que saludar y no se va a modificar nunca, te pegas la paliza y ya...

Logs Pipelines

Pero, ¿qué ocurriría sí son muchas personas y/o ese saludo variara periódicamente e hiciera otras acciones? Bien, pues este escenario dinámico lo podríamos resolver con YAML templates de una forma muy sencilla y también sería compatible con el escenario más estático.

Definición de la plantilla

En primer lugar, necesitamos definir la plantilla que va a realizar nuestro saludo, para ello creamos un fichero YAML (*.yml), e indicamos los parámetros que va a admitir la plantilla.

parameters:
  - name: 'persons'
    type: object
    default: {}

Básicamente, indicamos que va a recibir un objeto indeterminado llamado 'persons', que va a admitir recibir una estructura de datos en formato YAML. Además de este tipo, podría establecerse otros parámetros con distinto tipo. Estos son los tipos soportados:

Tipo Descripción
string Cadena de texto
number Valor numérico o cadena de texto que pueda parsearse a número. Se puede restringir a un conjunto de números
boolean Valor booleano
object Estructura de datos YAML
step Un step de ejecución de Pipeline
stepList Una secuencia de steps
job Un job de Pipeline
jobList Una lista de jobs
deployment Un despliegue de job
deploymentList Una secuencia de deployments
stage Un simple stage de Pipeline
stageList Un conjunto de stages

En el ejemplo de Pipeline con el editor clásico, saludábamos con una tarea de PowerShell, incluimos esta operación en la plantilla iterando sobre los elementos que podrían llegar en el objeto persons.

parameters:
  - name: 'persons'
    type: object
    default: {}
steps:
  - ${{ each person in parameters.persons }}:
      - task: PowerShell@2
        displayName: 'Hello ${{ person.name }} ${{ person.lastname }}'
        inputs:
          targetType: 'inline'
          script: 'Write-Host "Hello ${{ person.name }} ${{ person.lastname }}!"'

El código de la plantilla únicamente ha incluido una estructura for each que itera sobre el parámetro persons y que, por cada persona, creamos una task de PowerShell que saluda a la persona por su nombre y apellidos.

Construcción del pipeline

Una vez ya está creada la plantilla que vamos a utilizar, necesitamos definir el Pipeline que la va a ejecutar. Para ello, creamos otro fichero YAML y definimos cómo se va a desencadenar.

Como el objetivo es saludar, vamos a programar que ese saludo se produzca únicamente a las 8 de la mañana (UTC) cada día, y deshabilitamos cualquier desencadenador producido por commit o pull request. Además, vamos a utilizar la imagen de Ubuntu en el Agente del Pipeline.

trigger: none
pr: none

schedules:
- cron: '0 8 * * *'
  displayName: 'Daily Greeting'
  branches:
    include:
    - main
  always: true

pool:
  vmImage: 'ubuntu-latest'

Después, seguimos realizando la llamada a la plantilla que hemos creado y la declaración de las variables que va a recibir. Para ello, agregamos un job que utilice la plantilla y que defina el objeto persons.

trigger: none
pr: none

schedules:
- cron: '0 8 * * *'
  displayName: 'Daily greeting'
  branches:
    include:
    - main
  always: true

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: 'Greeting'
  steps:
  - template: Greeting.Template.yml
    parameters:
      persons:
      - name: 'Juan'
        lastname: 'García'
      - name: 'José'
        lastname: 'González'
      - name: 'María'
        lastname: 'Gutiérrez'
      - name: 'Lucía'
        lastname: 'Martínez'
      - name: 'Antonio'
        lastname: 'Martín'
      - name: 'Luis'
        lastname: 'Álvarez'
      - name: 'Pedro'
        lastname: 'López'
      - name: 'Ana'
        lastname: 'García'
      - name: 'Francisco'
        lastname: 'Rodríguez'
      - name: 'Patricia'
        lastname: 'Fernández'

Una vez completado el proceso, guardamos el pipeline y lo encolamos para que se ejecute inmediatamente.

Save and run pipeline

Al terminar la ejecución, en los logs se puede ver que se han creado tantas tareas como personas se han incluido en la lista, y que se ha producido un saludo individual para cada una de esas personas.

Logs

Conclusiones

El uso de Pipelines basadas en YAML en lugar del editor clásico, nos permite no sólo dinamizar operaciones utilizando parámetros y plantillas, sino que también nos permite versionar los Pipelines dentro del repositorio de código fuente.

Aunque este ejemplo de código, en un ejercicio teórico que en un escenario real no se daría, tareas como realizar un mantenimiento u operación sobre servicios de infraestructura, paradas programadas, aplicación de parches, y levantando de esas máquinas, se puede automatizar con un simple fichero de YAML y su plantilla. Otras operaciones como el rotado de secretos en un Key Vault o una integración de datos nocturna sobre un motor de base de datos podrían ser otros ejemplos de uso.

Buy Me A Coffee