Sucede mucho, se escribe un script de bash y, a mitad de camino, se cierra debido a un error. Corrige el error en su sistema y ejecuta el script nuevamente, pero a la mitad de los pasos tu script falla inmediatamente porque ya se aplicaron a tu sistema. Para crear sistemas resistentes, necesita escribir software que sea idempotente.
Que es idempotente.
Lo primero que debemos saber es el significado de idempotente “realizar una acción determinada varias veces y aun así conseguir el mismo resultado que se obtendría si se realizase una sola vez.”
El buen software siempre se escribe de forma idempotente, especialmente si está trabajando en sistemas distribuidos, donde las operaciones pueden ser finalmente coherentes y podría terminar llamando a las funciones varias veces debido a solicitudes duplicadas.
Lenguajes de Bash.
Permítame mostrarle un par de consejos de lenguajes de bash que puede usar para cambiar sus scripts para que sean idempotentes. Probablemente esté usando la mayoría de ellos sin ser consciente de los efectos secundarios:
Crear un archivo
Touch es por defecto idempotente. Esto significa que puede llamarlo varias veces sin ningún problema. Una segunda llamada no tendrá ningún efecto en el contenido del archivo. Tenga en cuenta que actualizará la hora de modificación del archivo, por lo que si depende de ello, tenga cuidado.
touch example.txt
Crear un directorio
Nunca use mkdir directamente, en vez de eso, utilícelo con -p. Esto asegura que mkdir no tendrá error si el directorio existe:
mkdir -p mydir
Crear a symbolic link
Creamos a symbolic link con el siguiente comando.
ln -s source target
Pero esto fallará si lo vuelves a llamar en el mismo objetivo. Para hacerlo idempotente, pase la variable -f:
La marca -f elimina el destino antes de crear el enlace simbólico, por lo que siempre tendrá éxito.
Al vincular un directorio, también necesita pasar -n. De lo contrario, al volver a llamarlo creará un enlace simbólico dentro del directorio.
ln -sfn source target
Remover archivos
Utilice el indicador -f que ignora los archivos no existentes.
rm -f example.txt
Modificar un archivos
A veces, está agregando una nueva línea a un archivo existente (es decir: / etc / fstab). Esto significa que debe asegurarse de no agregarla la segunda vez si ejecuta su script. Supongamos que tienes esto:
echo"/dev/sda1 /mnt/dev ext4 defaults 0 0" | sudo tee -a /etc/fstab
Si se ejecuta nuevamente, terminará teniendo entradas duplicadas en su / etc / fstab. Una forma de hacer que este sea idempotente es asegurarse de verificar ciertos marcadores de posición a través de grep:
if
! grep -qF "/mnt/dev" /etc/fstab;
then
echo"/dev/sda1 /mnt/dev ext4 defaults 0 0" | sudo tee -a /etc/fstab
fi
Aquí el -q significa modo silencioso y -F habilita el modo de cadena fija. Grep fallará silenciosamente si / mnt / dev no existe, por lo que la declaración de eco nunca se llamará.
Compruebe con la variable If si un archivo a directorio existe.
La mayoría de las veces estás escribiendo en un directorio, leyendo un archivo o haciendo simples manipulaciones de cadenas con una variable. Por ejemplo, puede tener una herramienta que crea un nuevo archivo basado en ciertas entradas:
if[
! -f "/etc/conf/foo.txt"
]
;
then
echo"complex set of rules" > /etc/conf/foo.txt
fi
-F es solamente un ejemplo, existen muchas otras variables que usted puede usar, tales como:
-z
: string of zero length
-d
: directory
-p
: pipe
-x
: file and has execute permission
Por ejemplo, suponga que desea instalar un binario, pero solo si no existe en su host, puede usar el -x de esta forma:
if
!
[
-x "
$(
command -v op
)
"
]
;
then
exportOP_VERSION
=
"v0.5.6-003"
curl -sS -o 1password.zip https://cache.agilebits.com/dist/1P/op/pkg/${OP_VERSION}/op_linux_amd64_${OP_VERSION}.zip
unzip 1password.zip op -d /usr/local/bin
rm -f 1password.zip
fi
Esto instala el binario op en / usr / local / bin. Si vuelve a ejecutar el script, no lo instalará más. Otro beneficio es que puede actualizar fácilmente el binario a una nueva versión con solo eliminarlo de su sistema, actualizar el entorno OP_VERSION y volver a ejecutar su script.
Formateando un dispositivo
Para formatear un volumen, digamos con un formato ext4, puede usar un comando como el siguiente:
mkfs.ext4 "$VOLUME_NAME"
Por supuesto, esto fallaría inmediatamente si lo llamas de nuevo. Para hacer que esta llamada sea idempotente, la presentamos con blkid:
blkid "$VOLUME_NAME"
||
mkfs.ext4 "$VOLUME_NAME"
Este comando imprime atributos para un dispositivo de bloque dado. Por lo tanto, anteponserse significa básicamente continuar con el formateo cuando falla blkid, lo que es una indicación de que el volumen dado aún no está formateado.
Montando un dispositivo
Se puede intentar montar un volumen en un directorio existente con el siguiente comando de ejemplo.
mount -o discard,defaults,noatime "$VOLUME_NAME""$DATA_DIR"
Sin embargo, esto fallará si ya está montado. Una forma es verificar la salida del comando de montaje y ver si el volumen ya está montado. Pero hay una mejor manera de hacerlo. Usando el comando mountpoint.
if
! mountpoint -q "$DATA_DIR";
then
mount -o discard,defaults,noatime "$VOLUME_NAME""$DATA_DIR"
fi
El comando mountpoint verifica si un archivo o directorio es un punto de montaje. El indicador -q solo se asegura de que no emita nada y se cierre silenciosamente. En este caso, si el punto de montaje no existe, avanzará y montará el volumen
La mayoría de estos consejos ya son conocidos, pero cuando escribimos scripts de Bash, estos pueden ser fácilmente ignorados sin siquiera pensarlo. Algunos de estos son muy específicos (como montaje o formateo), pero como vimos, crear software idempotente y resistente siempre es beneficioso a largo plazo. Así que conocerlos es útil.