portainer image / container updater
The snippet can be accessed without any authentication.
Authored by
Santiago Corrao
Edited
deploy-portainer.sh 8.80 KiB
#!/bin/bash
# Script para redeployar stacks en Portainer, actualizando todas sus imágenes
# Implementa filosofía GitOps para despliegue continuo
# Soporta stacks basados en archivos y stacks conectados a repositorios Git
# Configuración por defecto
PRUNE_IMAGES=false
# Función para mostrar ayuda
function show_help {
echo "Uso: $0 [opciones]"
echo ""
echo "Opciones:"
echo " --url URL URL de Portainer (obligatorio)"
echo " --username USUARIO Nombre de usuario de Portainer (obligatorio)"
echo " --password PASSWORD Contraseña de Portainer (obligatorio)"
echo " --environment ENTORNO Nombre del entorno en Portainer (obligatorio)"
echo " --stack STACK Nombre del stack a actualizar (obligatorio)"
echo " --prune Eliminar imágenes no utilizadas después del despliegue"
echo " --help Mostrar esta ayuda"
exit 1
}
# Verificar que los comandos necesarios estén disponibles
if ! command -v curl &> /dev/null; then
echo "Error: curl no está instalado. Por favor, instálalo e inténtalo de nuevo."
exit 1
fi
if ! command -v jq &> /dev/null; then
echo "Error: jq no está instalado. Por favor, instálalo e inténtalo de nuevo."
exit 1
fi
# Procesar argumentos
while [[ $# -gt 0 ]]; do
case $1 in
--url)
PORTAINER_URL="${2}"
shift 2
;;
--username)
PORTAINER_USERNAME="${2}"
shift 2
;;
--password)
PORTAINER_PASSWORD="${2}"
shift 2
;;
--environment)
ENVIRONMENT_NAME="${2}"
shift 2
;;
--stack)
STACK_NAME="${2}"
shift 2
;;
--prune)
PRUNE_IMAGES=true
shift
;;
--help)
show_help
;;
*)
echo "Opción desconocida: $1"
show_help
;;
esac
done
# Verificar argumentos obligatorios
if [ -z "$PORTAINER_URL" ] || [ -z "$PORTAINER_USERNAME" ] || [ -z "$PORTAINER_PASSWORD" ] || [ -z "$ENVIRONMENT_NAME" ] || [ -z "$STACK_NAME" ]; then
echo "Error: Faltan argumentos obligatorios."
show_help
fi
# Eliminar la barra final de la URL si existe
PORTAINER_URL=${PORTAINER_URL%/}
# Función para autenticar con Portainer
function authenticate {
echo "Autenticando con Portainer..."
AUTH_RESPONSE=$(curl -s -X POST \
"$PORTAINER_URL/api/auth" \
-H "Content-Type: application/json" \
-d "{\"Username\":\"$PORTAINER_USERNAME\",\"Password\":\"$PORTAINER_PASSWORD\"}")
if [ $? -ne 0 ]; then
echo "Error al conectar con Portainer"
exit 1
fi
# Extraer token JWT
JWT_TOKEN=$(echo $AUTH_RESPONSE | jq -r '.jwt')
if [ "$JWT_TOKEN" == "null" ] || [ -z "$JWT_TOKEN" ]; then
echo "Error de autenticación. Verifica tus credenciales."
exit 1
fi
echo "Autenticación exitosa"
}
# Función para obtener el ID del entorno
function get_environment_id {
echo "Buscando entorno '$ENVIRONMENT_NAME'..."
ENVIRONMENTS_RESPONSE=$(curl -s -X GET \
"$PORTAINER_URL/api/endpoints" \
-H "Authorization: Bearer $JWT_TOKEN")
ENVIRONMENT_ID=$(echo $ENVIRONMENTS_RESPONSE | jq -r ".[] | select(.Name == \"$ENVIRONMENT_NAME\" ) | .Id")
if [ -z "$ENVIRONMENT_ID" ]; then
echo "Error: Entorno '$ENVIRONMENT_NAME' no encontrado"
exit 1
fi
echo "Entorno '$ENVIRONMENT_NAME' encontrado con ID: $ENVIRONMENT_ID"
}
# Función para obtener stacks
function get_stacks {
echo "Obteniendo stacks del entorno..."
STACKS_RESPONSE=$(curl -s -X GET \
"$PORTAINER_URL/api/stacks" \
-H "Authorization: Bearer $JWT_TOKEN")
if [ $? -ne 0 ]; then
echo "Error al obtener stacks"
exit 1
fi
# Filtrar stacks por ID de entorno
ENVIRONMENT_STACKS=$(echo $STACKS_RESPONSE | jq -c "[.[] | select(.EndpointId == $ENVIRONMENT_ID)]")
# Contar total de stacks
STACK_COUNT=$(echo $ENVIRONMENT_STACKS | jq '. | length')
echo "Encontrados $STACK_COUNT stacks en el entorno"
}
# Función para encontrar el stack por nombre
function find_target_stack {
echo "Buscando stack '$STACK_NAME'..."
TARGET_STACK=$(echo $ENVIRONMENT_STACKS | jq -c "[.[] | select(.Name == \"$STACK_NAME\")][0]")
if [ "$(echo $TARGET_STACK | jq -r '.')" == "null" ]; then
echo "Error: Stack '$STACK_NAME' no encontrado en el entorno '$ENVIRONMENT_NAME'"
exit 1
fi
STACK_ID=$(echo $TARGET_STACK | jq -r '.Id')
STACK_ENV=$(echo $TARGET_STACK | jq -r '.Env')
STACK_ENDPOINT=$(echo $TARGET_STACK | jq -r '.EndpointId')
STACK_TYPE=$(echo $TARGET_STACK | jq -r '.GitConfig | if . == null then "file" else "git" end')
echo "Stack '$STACK_NAME' encontrado con ID: $STACK_ID (Tipo: $STACK_TYPE)"
}
# Función para actualizar un stack basado en archivo
function update_file_stack {
echo "Actualizando stack basado en archivo..."
curl -s -X GET \
"$PORTAINER_URL/api/stacks/$STACK_ID/file" \
-H "Authorization: Bearer $JWT_TOKEN" > /tmp/stack_details.json
# Extraer información relevante
STACK_CONTENT=$(jq -r '.StackFileContent' /tmp/stack_details.json)
# Escapar correctamente el contenido del archivo para JSON
STACK_CONTENT_ESCAPED=$(echo "$STACK_CONTENT" | jq -Rs .)
# Construir cuerpo de la solicitud para actualización
UPDATE_BODY=$(cat << EOF
{
"stackFileContent": ${STACK_CONTENT_ESCAPED},
"env": $STACK_ENV,
"prune": true,
"pullImage": true
}
EOF
)
# Actualizar stack
echo "Redesplegando stack $STACK_NAME con pull de todas las imágenes..."
UPDATE_RESPONSE=$(curl -s -X PUT \
"$PORTAINER_URL/api/stacks/$STACK_ID?endpointId=$STACK_ENDPOINT" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT_TOKEN" \
-d "$UPDATE_BODY")
if [ $? -ne 0 ] || [ "$(echo $UPDATE_RESPONSE | jq -r '.message // empty')" != "" ]; then
echo "Error al actualizar stack: $(echo $UPDATE_RESPONSE | jq -r '.message // "Error desconocido"')"
exit 1
fi
echo "Stack actualizado correctamente"
}
# Función para actualizar un stack basado en Git
function update_git_stack {
echo "Actualizando stack conectado a Git..."
# Obtener detalles del stack
STACK_DETAILS=$(curl -s -X GET \
"$PORTAINER_URL/api/stacks/$STACK_ID" \
-H "Authorization: Bearer $JWT_TOKEN")
# Extraer información Git
GIT_CONFIG=$(echo $STACK_DETAILS | jq '.GitConfig')
AUTO_UPDATE=$(echo $GIT_CONFIG | jq '.AutoUpdate')
# Construir payload para actualización
UPDATE_BODY=$(cat << EOF
{
"env": $(echo $STACK_DETAILS | jq '.Env'),
"prune": true,
"pullImage": true
}
EOF
)
# Para stacks Git, usamos el endpoint específico para pull
echo "Solicitando pull de repositorio Git y actualización de imágenes..."
UPDATE_RESPONSE=$(curl -s -X POST \
"$PORTAINER_URL/api/stacks/$STACK_ID/git/redeploy?endpointId=$ENVIRONMENT_ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT_TOKEN" \
-d "$UPDATE_BODY")
if [ $? -ne 0 ] || [ "$(echo $UPDATE_RESPONSE | jq -r '.message // empty')" != "" ]; then
echo "Error al actualizar stack Git: $(echo $UPDATE_RESPONSE | jq -r '.message // "Error desconocido"')"
exit 1
fi
echo "Stack Git actualizado correctamente"
}
# Función para limpiar imágenes no utilizadas
function prune_images {
if [ "$PRUNE_IMAGES" = true ]; then
echo "Eliminando imágenes no utilizadas..."
PRUNE_RESPONSE=$(curl -s -X POST \
"$PORTAINER_URL/api/endpoints/$ENVIRONMENT_ID/docker/images/prune?filters=%7B%22dangling%22%3A%7B%22true%22%3Atrue%7D%7D" \
-H "Authorization: Bearer $JWT_TOKEN")
if [ $? -eq 0 ]; then
SPACE_RECLAIMED=$(echo $PRUNE_RESPONSE | jq -r '.SpaceReclaimed')
SPACE_MB=$(echo "scale=2; $SPACE_RECLAIMED / 1024 / 1024" | bc)
echo "Limpieza completada. Espacio recuperado: $SPACE_MB MB"
else
echo "Error durante la limpieza de imágenes"
fi
fi
}
# Función para verificar el estado del stack después de la actualización
function verify_stack_status {
echo "Verificando estado del stack..."
# Esperar un momento para que el stack se actualice
sleep 5
# Obtener detalles actualizados del stack
STACK_STATUS=$(curl -s -X GET \
"$PORTAINER_URL/api/stacks/$STACK_ID" \
-H "Authorization: Bearer $JWT_TOKEN")
# Verificar el estado
STACK_STATUS_VALUE=$(echo $STACK_STATUS | jq -r '.Status')
if [ "$STACK_STATUS_VALUE" == "1" ]; then
echo "Stack '$STACK_NAME' actualizado y en ejecución correctamente"
else
echo "Advertencia: El stack puede no estar completamente activo. Status: $STACK_STATUS_VALUE"
echo "Verifica el estado en la interfaz de Portainer para más detalles"
fi
}
# Ejecución principal
authenticate
get_environment_id
get_stacks
find_target_stack
# Actualizar según el tipo de stack
if [ "$STACK_TYPE" == "file" ]; then
update_file_stack
elif [ "$STACK_TYPE" == "git" ]; then
update_git_stack
fi
# Acciones posteriores
verify_stack_status
prune_images
echo ""
echo "Proceso de actualización de stack completado"
exit 0
Please register or sign in to comment