Skip to content
Snippets Groups Projects

portainer image / container updater

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    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
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment