Tareas dinámicas en Pipelines
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í.
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...
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.
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.
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.