El cambio roto del viernes
Alguien edita un archivo a las cuatro, lo sube y se va a casa. Nadie ejecutó las comprobaciones y el siguiente apply falla a medias.
Un pipeline ejecuta las mismas comprobaciones, en el mismo orden, para cada cambio. Se detiene en la primera que falla: un cambio malo nunca llega al apply.
Al final de esta lección
Nota
Puedo escribir un pipeline en etapas que se detiene en el primer fallo.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Pipeline | pasos que se ejecutan en un orden fijo | |
| Etapa | un paso de un pipeline | |
| Código de salida | el número que devuelve un comando, donde 0 significa éxito | echo $? |
| Barrera | una etapa que detiene el pipeline cuando una comprobación falla | |
| Archivo de plan | un plan guardado que apply ejecuta tal cual | plan.bin |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Todo comando termina con un código de salida
Cuando un comando termina, devuelve un número, su código de salida. Cero significa que funcionó. Cualquier otro número significa que no.
El shell guarda el último en $?. Un pipeline solo lee códigos de salida: una comprobación que devuelve un número distinto de cero es una comprobación que detiene la ejecución.
Crea una carpeta para el pipeline
mkdir -p ~/iac-demo/pipe && cd ~/iac-demo/pipeSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe un proyecto con un recurso
printf '%s\n' 'variable "shop" {' ' type = string' ' default = "Tienda de la esquina"' '}' '' 'resource "terraform_data" "door" {' ' input = format("%s: abierto", var.shop)' '}' > main.tfSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Prepara la carpeta
tofu init -no-color | grep successfullySe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe un segundo archivo con espacios desordenados
printf '%s\n' 'resource "terraform_data" "sign" {' ' input = "Abierto"' '}' > sign.tfSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Comprueba el formato y lee el código
tofu fmt -check; echo "código de salida $?"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un pipeline se detiene en el primer fallo
Un script puede ejecutar cada etapa y detenerse cuando una falla. Aquí una pequeña función, stage, ejecuta un comando y sale si devuelve algo distinto de cero.
Las etapas siguen el orden en que las ejecutarías a mano: format, validate, plan, barrera, apply. Una etapa fallida impide que arranquen las siguientes.
Escribe la barrera
printf '%s\n' '[.resource_changes[].change.actions[]] | index("delete") | not' > gate.jqSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del pipeline
printf '%s\n' '#!/bin/bash' 'stage() { echo "== etapa: $1"; name=$1; shift; "$@" > /dev/null || { echo "etapa $name fallida, se detiene"; exit 1; }; }' 'gate() { tofu show -json plan.bin | jq -e -f gate.jq; }' 'stage format tofu fmt -check -no-color' 'stage validate tofu validate -no-color' 'stage plan tofu plan -no-color -out=plan.bin' 'stage gate gate' 'stage apply tofu apply -no-color plan.bin' 'echo "pipeline superado"' > ci.shSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecútalo con sign.tf desordenado
bash ci.sh; echo "salida del pipeline $?"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Arregla el formato y ejecútalo otra vez
tofu fmt -no-color; bash ci.sh; echo "salida del pipeline $?"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que un pipeline verde prueba que el cambio es seguro. Solo prueba que pasó cada comprobación escrita. Un cambio puede ser válido, planificado y aplicado, y aun así ser el cambio equivocado. La barrera y una persona que lee el plan son la seguridad.
Una barrera lee el plan antes del apply
La etapa plan guardó el plan en plan.bin, y la etapa apply ejecuta ese archivo y nada más. Así que lo que aprobó la barrera es lo que se ejecuta.
tofu show -json convierte el plan en datos. El filtro de la barrera dice que no cuando algún recurso va a borrarse.
Borra un recurso y ejecútalo
rm sign.tf; bash ci.sh; echo "salida del pipeline $?"; tofu state listSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un pipeline alojado es un archivo
En un servicio que aloja pipelines, las etapas se escriben en un archivo del repositorio y el servicio las ejecuta en su propia máquina. Un job puede decir needs para esperar a otro.
Aquí no puedes ejecutar uno, porque no hay runner alojado. Puedes escribir uno y leerlo.
Escribe un archivo de workflow
mkdir -p .github/workflows && printf '%s\n' 'name: infra' 'on:' ' pull_request:' ' push:' ' branches: [main]' 'jobs:' ' check:' ' runs-on: ubuntu-latest' ' steps:' ' - uses: actions/checkout@v4' ' - run: tofu fmt -check' ' - run: tofu validate' ' - run: tofu plan -out=plan.bin' ' apply:' ' needs: check' " if: github.ref == 'refs/heads/main'" ' runs-on: ubuntu-latest' ' steps:' ' - run: tofu apply plan.bin' > .github/workflows/infra.ymlSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Encuentra el orden y los comandos
grep -nE "^ (check|apply):|needs:|run:" .github/workflows/infra.ymlSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una ejecución calculada en Python
# Ejecuta un pipeline cuyas etapas dependen de etapas anteriores.
jobs = {"check": [], "plan": ["check"], "apply": ["plan"]}
failing = {"plan"}
state = {}
for name in ["check", "plan", "apply"]:
blocked = False
for need in jobs[name]:
if state[need] != "passed":
blocked = True
if blocked:
state[name] = "skipped"
elif name in failing:
state[name] = "failed"
else:
state[name] = "passed"
print(name, state[name])
# Vacía el conjunto failing y mira otra vez.