Usando servicios con Nix Shell
En el post anterior tocaba el
tema de crear entornos de desarrollo reproducibles con Nix
e incluso
dejé algunos casos de uso para lenguajes en específico.
Justo cuando pensaba que todo iba de maravilla se me ocurrió la idea
de probar
Easypodcasts en modo
desarollador y de paso refrescar un poco de Elixir
. Easypodcasts
es un proyecto basado en Phoenix
LiveView y
entre otras cosas necesita PostgreSQL
como fuente de datos. En el
pasado (léase antes de usar Nix
) esto lo habría resuelto usando un
Dockerfile
para configurar y lanzar el servicio, pero ahora tenemos
otra alternativa.
Creando shellHooks
para nuestro entorno.
Buscando en la documentación de Nix
podemos ver lo siguiente:
Si una derivación
mkShell
define la variable especialshellHooks
el contenido de esta será ejecutado despúes de$stdenv/setup
Traduciendo: es posible inyectar un script arbitrario que se ejcuta con cada instancia de la derivación
Esto nos abre la puerta a otro conjunto de posibilidades.
Manos a la obra
Ahora que sabemos que es posible alterar el funcionamiento del
shell.nix
pongamos manos a la obra. Los objetivos son:
- Configurar el servidor el arranque del servidor
PostgreSQL
. - Crear un usuario y contraseña para el entorno de desarrollo.
- Exportar la configuración utilizando variables de entorno que sean
visibles desde nuestro proyecto
Elixir
.
Primero las variables de entorno.
echo 'Setting database'
export PGDATA=$PWD/postgres_data
export PGHOST=$PWD/postgres
export LOG_PATH=$PWD/postgres/LOG
export PGDATABASE=postgres
export PGSOCKET=$PWD/sockets
Listo, PostgresSQL
usará un directorio local para guardar el estado
del servicio y exportamos el directorio donde vivirá el socket
necesario para la comunicación.
Este último paso es en extremo importante, utilizar sockets en vez de comunicación TCP nos posibilita ejecutar varias instancias de nuestra base de datos sin tener que preocuparnos por cosas molestas como puertos o permisos.
Continuando con nuestro script ahora necesitamos arrancar el servicio de base de datos
mkdir $PGSOCKET
if [ ! -d $PGHOST ]; then
mkdir -p $PGHOST
fi
if [ ! -d $PGDATA ]; then
echo 'Initializing postgresql database...'
initdb $PGDATA --auth=trust >/dev/null
fi
pg_ctl start -l $LOG_PATH -o "-c listen_addresses= -c unix_socket_directories=$PGSOCKET"
createuser -d -h $PGSOCKET postgres
psql -h $PGSOCKET -c "ALTER USER postgres PASSWORD 'postgres';"
¡Perfecto! Al ejecutar nix-shell
tenemos todo listo para utilizar
nuestra instancia local de la base de datos, solo queda un pequeño
detalle. ¿Cómo detenemos el servidor una vez hayamos terminado con
el?
La forma más sencilla es definir una rutina de limpieza para cuando
salgamos del nix-shell
function cleanup {
echo "Shutting down the database..."
pg_ctl stop
echo "Removing directories..."
rm -rf $PGDATA $PGHOST $PGSOCKET
}
trap end EXIT
Usando el comando trap
de Bash
podemos capturar la señal EXIT
(que no es una verdadera señal de POSIX) para ejecutar la función
cleanup
y detener nuestra instancia de PostgreSQL
. En este caso
además elimino todos los datos temporales creados en la sesíon de
desarrollo, de ese modo arrancamos desde 0 cada vez (lo mismo si haces
un docker run
y no defines algún modo para que tu contenedor
persista su estado).
The end?
Hay pocas tareas que no se pueden ejecuta con shellHooks
En el caso de
Easypodcasts
además de lanzar la base de datos, el script se asegura
de que el entorno de Elixir esté correctamente configurado y que tanto
hex
como rebar
se encuentren en el entorno.
Como nota de advertencia: Recuerden que los archivos shell.nix
pueden ejecutar código shell arbitrario. Verifiquen que el contenido
es seguro antes de de crear un entorno nuevo.