Lenguaje declarativo
La programación imperativa se centra en cómo se deben realizar las tareas y emite comandos específicos que cambian el estado actual del sistema para lograr el objetivo deseado. En otras palabras, la programación imperativa especifica exactamente cómo se deben realizar las acciones necesarias para lograr un objetivo.
Por otro lado, la programación declarativa se enfoca en lo que se desea lograr y describe el estado deseado del sistema. En lugar de especificar cómo deben llevarse a cabo las acciones para lograr un objetivo, la programación declarativa describe el objetivo en sí y permite que el sistema decida cómo alcanzar ese estado deseado.
Ejemplo de uso lenguaje declarativo
La razón por la que necesita dejar de lado la programación imperativa es para manejar mejor el cambio.
Cuando escribe código que realiza una secuencia de operaciones, esa secuencia hará el cambio deseado la primera vez que se ejecute. Si ejecuta el mismo código por segunda vez consecutiva, las mismas operaciones fallarán o crearán un estado diferente al deseado. Aquí hay un ejemplo:
$ sudo useradd -u 1001 -g 1001 -c "Joe User" -m joe
$ sudo useradd -u 1001 -g 1000 -c "Joe User" -m joe
useradd: user 'joe' already exists
Entonces necesitas cambiar el código para manejar esa situación:
# bash excerpt
getent passwd $USERNAME > /dev/null 2> /dev/null
if [ $? -ne 0 ]; then
useradd -u $UID -g $GID -c "$COMMENT" -s $SHELL -m $USERNAME
else
usermod -u $UID -g $GID -c "$COMMENT" -s $SHELL -m $USERNAME
fi
Bien, son seis líneas de código y todo lo que hemos hecho es asegurarnos de que el nombre de usuario no esté ya en uso. ¿Qué sucede si necesitamos verificar que el UID sea único, que el GID sea válido y que se establezca la caducidad de la contraseña? Puede ver que este será un script muy largo incluso antes de que lo ajustemos para asegurarnos de que funcione correctamente en múltiples sistemas operativos. Es por eso que decimos que la programación imperativa no maneja muy bien el cambio. Se necesita mucho código para cubrir todas las situaciones que necesita probar.
Usando la idempotencia
Al administrar sistemas informáticos, desea que las operaciones aplicadas sean idempotentes, donde la operación logra los mismos resultados cada vez que se ejecuta. La idempotencia le permite aplicar y volver a aplicar (o converger) un manifiesto de configuración y lograr siempre el estado deseado.
Para que el código imperativo sea idempotente, debe tener instrucciones sobre cómo comparar, evaluar y aplicar no solo cada recurso, sino también cada atributo del recurso. Como vio en la sección anterior, incluso las operaciones más simples rápidamente se volverán pesadas y difíciles de mantener.
¿Qué es una operación idempotente?
La idempotencia es un concepto en programación que se refiere a la propiedad de una operación que se puede realizar varias veces sin cambiar el resultado más allá de la primera vez que se ejecuta. En otras palabras, si una operación se realiza una vez o varias veces, el resultado final es el mismo.
En el contexto de la informática, la idempotencia es una característica deseable para muchas operaciones de sistema, como la configuración de servidores o la actualización de bases de datos. Una operación idempotente garantiza que se puede repetir la operación sin generar errores o efectos secundarios no deseados.
Estos son algunos ejemplos de código y matemáticas idempotentes y no idempotentes:
- any number^1. Idempotente, un número elevado a 1 es igual.
- value = value * 2. No idempotente, se duplicará cada vez.
- value = value * 2/2. Idempotente, se mantiene el mismo valor.
- echo "Good!" >> /some/file. No idempotente, el archivo seguirá creciendo.
- echo "Good!" > /some/file. Idempotente, el archivo siempre tendrá el mismo contenido.
El ejemplo final simplista evita tener que comparar el estado del elemento simplemente sobrescribiéndolo cada vez. Esto solo funciona en un conjunto limitado de situaciones. La mayoría de los cambios requieren una evaluación para determinar qué cambios son necesarios.
Declaración de estado final
La declaración de estado final (en inglés "desired state declaration") es un concepto utilizado en la gestión de configuración y en la programación declarativa que se refiere a la descripción del estado deseado de un sistema o recurso en lugar de las acciones que se deben realizar para alcanzar ese estado.
En la programación declarativa, el programador describe el estado deseado del sistema, en lugar de especificar los pasos para llegar a ese estado. Por ejemplo, en lugar de escribir un script que instale un paquete de software, el programador declararía que el paquete de software debe estar instalado. El sistema se encargaría de averiguar si el paquete está instalado y tomar las medidas necesarias para instalarlo si no lo está.
La declaración de estado final se utiliza en la gestión de configuración para describir el estado deseado de un sistema, como la configuración de servidores o la instalación de software. Por ejemplo, en la herramienta de gestión de configuración Puppet, los manifiestos de Puppet describen el estado final deseado del sistema, y Puppet se encarga de asegurarse de que el sistema esté en ese estado.
La declaración de estado final es una forma poderosa de gestionar sistemas y recursos de forma eficiente, ya que permite que los sistemas se configuren automáticamente y se mantengan en un estado consistente sin la necesidad de realizar tareas manuales o repetitivas. Además, permite una mayor flexibilidad y escalabilidad en la gestión de sistemas, ya que los cambios pueden realizarse fácilmente en la declaración de estado final sin tener que modificar scripts o comandos específicos.
Aquí le presentaremos su primera parte del lenguaje de configuración de Puppet, una declaración de recursos para el mismo usuario que creamos anteriormente:
user {'joe':
ensure => present,
uid => '1001',
gid => '1000',
comment => 'Joe User',
managehome => true,
}
Como puede ver, el código no es mucho más que una simple explicación de texto del estado deseado. Debería estar presente un usuario llamado Joe User, se debería crear un directorio de inicio para el usuario, etc. Es muy claro, muy fácil de leer. Exactamente cómo se debe crear el usuario no está dentro del código, ni tampoco las instrucciones para manejar diferentes sistemas operativos.
El lenguaje declarativo es mucho más fácil de leer y menos propenso a romperse debido a las diferencias ambientales. Puppet fue diseñado para lograr resultados consistentes y repetibles. Usted describe cuál debería ser el estado final del recurso y Puppet evaluará el recurso y aplicará los cambios necesarios para alcanzar ese estado.
Revisión de la programación declarativa
Los lenguajes de programación convencionales crean cambios al enumerar las operaciones exactas que deben realizarse. El código que define cada cambio de estado y el orden de los cambios se conoce como programación imperativa.
Los buenos manifiestos de Puppet se escriben con programación declarativa. En lugar de definir exactamente cómo realizar cambios, en los que debe escribir código para probar y comparar el estado del sistema antes de realizar ese cambio, declara cómo debe ser. Depende del agente de Títeres evaluar el estado actual y aplicar los cambios necesarios.