Uso de Bash para la automatización | Activar administrador del sistema

Dada la cantidad reciente de artículos que cubren los fundamentos de Bash (enumerados al final de este artículo), es inevitable que uno de sus nuevos colegas arroje uno a la nube. A medida que estas cosas avanzan, los siguientes pasos lógicos son:

  1. Comprueba que algo absolutamente crítico depende del perfecto funcionamiento del script "nube".
  2. Comprueba que el autor original del guión haya olvidado por completo cómo funciona realmente.
  3. Compruebe que el último administrador es responsable de cambiarlo fundamentalmente sin ninguna validación.

En este artículo, ayudo a todos los administradores y los ayudo a evitar todos los errores anteriores. El resultado, a su vez, conduce a una administración más feliz y, con suerte, a nuestro empleo continuo.

Índice

Cómo se escribe "script bash"

Para iluminarte (y por tu amor a $DEITY), verifica tu código en una herramienta de administración de código fuente (SCM). Incluso mientras estás aprendiendo, usa un repositorio local como tu patio de recreo. Esta práctica no solo te permite registrar tus esfuerzos a lo largo del tiempo, sino que también te permite corregir fácilmente los errores. Hay muchos artículos maravillosos sobre Empezar con git que recomiendo encarecidamente.

Una nota rápida sobre el uso y el aprendizaje de Bash, ya que este lenguaje de secuencias de comandos ofrece un conjunto único de preferencias y estilos estilísticos del autor: tan pronto como detecte algo que le resulte nuevo (sintaxis, estilo o construcción del lenguaje), busque por ello inmediatamente. Dedique su tiempo a comprender el nuevo elemento de la página del manual (primera opción) o el Guía avanzada de secuencias de comandos de Bash (ambos son accesibles sin conexión). Hacer esto lo ralentizará al principio, pero con el tiempo esta práctica lo ayudará a profundizar su conocimiento sobre dónde encontrar respuestas.

Escribir pepitas de Bash reutilizables como bibliotecas

Los scripts de automatización se escriben mejor adoptando la filosofía de Unix: muchas herramientas pequeñas que hacen una sola cosa. Esto significa que tendrá mucho más éxito escribiendo scripts y bibliotecas pequeñas y especializadas que con un solo "fregadero de cocina" gigante. Aunque, es cierto, he escrito y mantenido algunos gigantes (a veces tienen un propósito).

Los scripts de automatización a menudo deben ser comprensibles y mantenibles por varios autores. Con muchos scripts pequeños volando (y rastreados en el control de versiones), rápidamente tendrá que compartir una referencia a los valores de nombres, versiones, rutas o URL. Escribir estos elementos comunes en las bibliotecas también proporciona un espacio mental adicional para que los mantenedores disfruten de la documentación en línea. Además, hace que el uso de pruebas unitarias sea casi trivial (lo cubriremos al final).

Practiquemos una buena higiene del código desde el principio creando un repositorio local de "juego". En su nuevo repositorio, cree una ubicación para almacenar nuestros scripts y archivos de biblioteca. Me gusta apegarme a los estándares FHS bien entendidos por simplicidad. Crear directorios ./bin/ y ./lib/ en la raíz del repositorio. En un proyecto de automatización más grande, todavía usaría estos nombres, pero podrían estar enterrados profundamente (por ejemplo, debajo de un scripts Donde tools subcarpeta).

Hablar de caminos me lleva a un gran tema para una primera biblioteca. Necesitamos una forma para que nuestros componentes futuros hagan referencia a elementos estructurales y valores de alto nivel. Usando su editor favorito, cree el archivo ./lib/anchors.sh y agregue el contenido a continuación:



# A Library of fundamental values
# Intended for use by other scripts, not to be executed directly.

# Set non-'false' by nearly every CI system in existence.
CI="${CI:-false}"  # true: _unlikely_ human-presence at the controls.
[[ $CI == "false" ]] || CI='true'  # Err on the side of Automatización

# Absolute realpath anchors for important directory tree roots.
LIB_PATH=$(realpath $(dirname "${BASH_SOURCE[0]}"))
REPO_PATH=$(realpath "$LIB_PATH/../")  # Specific to THIS repository
SCRIPT_PATH=$(realpath "$(dirname $0)")

El archivo comienza con dos líneas vacías y el primer comentario explica por qué. La biblioteca debe estar definida como ejecutable (a pesar de que el nombre termine en .sh, indicando su tipo). Si la biblioteca se ejecutó directamente, el shell del usuario puede desaparecer (o empeorar). Deshabilitar la ejecución directa (chmod -x ./lib/anchors.sh) es el primer nivel de protección para los administradores novatos. El comentario al principio del archivo es el segundo nivel.

Por convención, los comentarios siempre deben describir el (no el) de las declaraciones que siguen. Un lector puede simplemente leer la declaración para entender lo que hace, pero no puede adivinar de manera confiable lo que el autor estaba pensando en ese momento. Sin embargo, antes de continuar, necesito detallar un problema que a menudo toma a las personas con la guardia baja con Bash.

Los valores predeterminados de Bash proporcionan una cadena vacía cuando se refieren a una variable indefinida. el CI variable (o algo similar en su automatización) está destinado a indicar la probable ausencia de un ser humano. Desafortunadamente, para los maestros de bots, los humanos probablemente tendrán que ejecutar el script manualmente al menos una vez. En este punto, es probable que se olviden de establecer un valor para CI antes de presionar Entrar.

Por lo tanto, debemos establecer un valor predeterminado para el valor y asegurarnos de que siempre sea verdadero o falso. El código de la biblioteca de muestra anterior muestra cómo probar si una cadena está vacía y cómo forzar la cadena para que contenga uno de un par de valores. La forma en que leo el primer grupo de declaraciones en anchors.sh es:

Definir 'CI' adelante siguiendo:

  1. Examen del valor anterior de CI (puede ser indefinido, por lo que una cadena vacía).
  2. El ':-' parte significa:
    1. Si el valor era una cadena vacía, representa la cadena 'false' bastante.
    2. Si el valor no era una cadena vacía, use lo que fuera (incluido 'DaRtH VaDeR').

Probar la cosa dentro'[[ ' and ' ]]':

  1. Si el nuevo valor de 'CI' es igual a la cadena literal "false", lanza el código de salida 0 (que significa éxito o verdad).
  2. De lo contrario, descartar el código de salida 1 (que significa fracaso o falsedad)

Si la prueba terminó con 0, vaya a la línea siguiente, o (el ' || ' parte), supongamos un conjunto de administradores novatos CI=YES!PLEASE o un juego de máquinas perfecto CI=true. Inmediatamente establezca el valor de 'CI' a literal de cadena 'true'; Porque las máquinas perfectas son mejores, no se equivoquen.

Para el resto de esta biblioteca, los valores de ruta de anclaje casi siempre son útiles en los scripts que se ejecutan en la automatización desde un repositorio. Cuando se usa en un proyecto más grande, deberá ajustar la ubicación relativa del directorio de la biblioteca a la raíz del repositorio. De lo contrario, dejaré de entender estas afirmaciones, como un ejercicio de investigación para el lector (hazlo ahora, te ayudará más adelante).

Uso de bibliotecas Bash en scripts Bash

Para cargar una biblioteca, utilice el source control integrado. Este comando no es elegante. Déle la ubicación de un archivo, luego lee y ejecuta el contenido sobre la marcha, lo que significa que ejecutar el código de la biblioteca será en realidad el script que sourceél.

Para evitar que gran parte de su cerebro se drene por sus oídos, aquí hay un simple ./bin/example.sh escenario para ilustrar:

#!/bin/bash

LIB_PATH="$PWD/$(dirname $0)/../lib/anchors.sh"
echo "Before loading: $LIB_PATH"
set -ax
cd /var/tmp
source $LIB_PATH
echo "After loading: $(export -p | grep ' LIB_PATH=')"

Puede notar inmediatamente que la secuencia de comandos cambió el contexto de ejecución antes de cargar la biblioteca. también define LIB_PATH localmente y lo apunta a un archivo (confusamente, en lugar de un directorio) con una ruta relativa (con fines ilustrativos).

Continúe, ejecute este script y examine el resultado. Tenga en cuenta que todas las operaciones de la biblioteca anchors.sh corrió dentro del /var/tmp/ directorio y exportó automáticamente sus definiciones. La antigua definición de LIB_PATH fue apaleado y exportado por el a en el set -ax. Este hecho es visible en la salida del declare -x procedente de export pedido. Esperemos que la salida de depuración (la x en el set -ax) es entendible.

Al depurar de esta manera, Bash imprime todos los valores intermedios a medida que analiza cada línea. Incluí este guión aquí para mostrar por qué nunca querrías set -ax o cambiar directorios utilizando los comandos de nivel superior de una biblioteca. Recuerde que las declaraciones de la biblioteca se evalúan en el momento de la carga en el script. Entonces, cambiar el entorno en una biblioteca causa efectos secundarios en el tiempo de ejecución en el script utilizado source para cargarlo Se garantiza que los efectos secundarios como este volverán loco al menos a un administrador del sistema. Nunca se sabe, ese administrador podría ser yo, así que no lo hagas.

Como ejemplo práctico, considere una biblioteca imaginaria que define una función utilizando una variable de entorno de nombre de usuario/contraseña para acceder a un servicio remoto. Si la biblioteca ha realizado un análisis de nivel superior set -ax antes de esta función, cada vez que se carga, la salida de depuración incluirá la visualización de estas variables, salpicando sus secretos por todos lados para que todos los vean. Peor aún, será difícil (desde la perspectiva de un guión de llamada) para un compañero de trabajo novato apagar la salida sin gritarle al teclado.

En conclusión, la clave aquí es ser consciente de que las bibliotecas "ocurren" en el contexto de su llamador. Este factor es también la razón por la cual el ejemplo anchors.sh puedo usar $0 (la ruta al script ejecutable y el nombre del archivo), pero la ruta a la biblioteca en sí solo está disponible a través de la "magia" '${BASH_SOURCE[0]}' (elemento de matriz). Este factor puede ser confuso al principio, pero debes tratar de mantenerte disciplinado. Evite los comandos amplios y extensos en las bibliotecas. Cuando lo haga, todos los nuevos administradores insistirán en pagar sus donas.

Escritura de pruebas unitarias para bibliotecas

Escribir pruebas unitarias puede parecer una tarea desalentadora hasta que te das cuenta de que una cobertura perfecta suele ser una pérdida de tiempo. Sin embargo, es una buena práctica usar y actualizar siempre su código de prueba cuando accede a su código de biblioteca. El objetivo de la escritura de prueba es admitir los casos de uso más comunes y obvios y luego continuar. No preste mucha atención a los casos de esquina o menos que los usos permanentes. También sugiero centrar inicialmente sus esfuerzos de prueba de la biblioteca en el Pruebas unitarias nivel en lugar de pruebas de integración.

Tomemos otro ejemplo: el script ejecutable ./lib/test-anchors.sh:

#!/bin/bash

# Unit-tests for library script in the current directory
# Also verifies test script is derived from library filename

TEST_FILENAME=$(basename $0)  # prefix-replace needs this in a variable
SUBJ_FILENAME="${TEST_FILENAME#test-}"; unset TEST_FILENAME
TEST_DIR=$(dirname $0)/

ANY_FAILED=0

# Print text after executing command, set ANY_FAILED non-zero on failure
# usage: test_cmd "description" <command> [arg...]

test_cmd() {
   local text="${1:-no test text given}"
   shift
   if ! "[email protected]"; then
      echo "fail - $text"; ANY_FAILED=1;
   else
      echo "pass - $text"
   fi
}

test_paths() {
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME is not executable" 
      test ! -x "$SCRIPT_PATH/$SUBJ_FILENAME"
   test_cmd "Unit-test and library in same directory" 
      test "$LIB_PATH" == "$SCRIPT_PATH"
   for path_var in LIB_PATH REPO_PATH SCRIPT_PATH; do
      test_cmd "$$path_var is defined and non-empty: ${!path_var}" 
         test -n "${!path_var}"
      test_cmd "$$path_var referrs to existing directory" 
         test -d "${!path_var}"
   done
}

# CI must only/always be either 'true' or 'false'.
# Usage: test_ci <initial value> <expected value>

test_ci() {
   local prev_CI="$CI"  # original value restored at the end
   CI="$1"
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME loaded from $TEST_DIR" 
      test "$?" -eq 0
   test_cmd "$CI='$1' becomes 'true' or 'false'" 
      test "$CI" = "true" -o "$CI" = "false"
   test_cmd "$CI value '$2' was expected" 
      test "$CI" = "$2"
   CI="$prev_CI"
}

test_paths
test_ci "" "false"
test_ci "$RANDOM" "true"
test_ci "FoObAr" "true"
test_ci "false" "false"
test_ci "true" "true"

# Always run all tests and report, exit non-zero if any failed

test_cmd "All tests passed" 
   test "$ANY_FAILED" -eq 0
[[ "$CI" == "false" ]] || exit $ANY_FAILED  # useful to Automatización
exit(0)

La razón por la que puse este script en ./lib (Opuesto a ./bin) es tanto por conveniencia como porque las pruebas nunca deben depender del uso del código que están verificando. Dado que esta prueba necesita verificar las rutas, es más fácil colocarla en la misma ruta que la biblioteca. De lo contrario, este enfoque es una cuestión de preferencia personal. Siéntase libre de ejecutar la prueba ahora, ya que podría ayudarlo a comprender el código.

Envoltura

Este artículo de ninguna manera representa la totalidad del uso de Bash en la automatización. Sin embargo, he tratado de impartir conocimientos básicos y recomendaciones que (si se siguen) sin duda le harán la vida más fácil. Entonces, incluso cuando las cosas se pongan difíciles, una buena comprensión de la importancia del contexto del tiempo de ejecución será útil.

Finalmente, la creación de scripts en o para la automatización puede ser despiadada con los errores. Tener incluso pruebas de unidades básicas implementadas para sus bibliotecas generará confianza y ayudará a la próxima persona que venga (que podría ser usted después de cinco años de ser olvidado). Puede encontrar todos los ejemplos de código utilizados en este artículo. en línea aquí.

¿Interesado en repasar los fundamentos de Bash? Para verificar:

[ Want to try out Red Hat Enterprise Linux? Download it now for free. ]

Artículos de interés

Subir