232 lines
6.5 KiB
Bash
Executable File
232 lines
6.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Portainer Stack Backup Script
|
|
# This script connects to a Portainer instance and exports all stacks with their configurations
|
|
|
|
set -e
|
|
|
|
# Configuration - Update these variables
|
|
PORTAINER_URL="http://192.168.11.12:9000" # Change this to your Portainer URL
|
|
USERNAME="admin" # Change this to your username
|
|
PASSWORD="JohnWayne#21" # Change this to your password or leave empty for prompt
|
|
OUTPUT_DIR="portainer-stacks-backup"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Function to print colored output
|
|
print_status() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Function to check if required tools are installed
|
|
check_dependencies() {
|
|
print_status "Checking dependencies..."
|
|
|
|
if ! command -v curl &> /dev/null; then
|
|
print_error "curl is required but not installed."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v jq &> /dev/null; then
|
|
print_error "jq is required but not installed. Please install it with: brew install jq"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "All dependencies are available."
|
|
}
|
|
|
|
# Function to prompt for configuration if not set
|
|
get_config() {
|
|
if [ -z "$PORTAINER_URL" ]; then
|
|
read -p "Enter Portainer URL (e.g., http://localhost:9000): " PORTAINER_URL
|
|
fi
|
|
|
|
if [ -z "$USERNAME" ]; then
|
|
read -p "Enter Portainer username: " USERNAME
|
|
fi
|
|
|
|
if [ -z "$PASSWORD" ]; then
|
|
read -s -p "Enter Portainer password: " PASSWORD
|
|
echo
|
|
fi
|
|
|
|
# Remove trailing slash from URL if present
|
|
PORTAINER_URL=$(echo "$PORTAINER_URL" | sed 's/\/$//g')
|
|
}
|
|
|
|
# Function to authenticate with Portainer
|
|
authenticate() {
|
|
print_status "Authenticating with Portainer..."
|
|
|
|
AUTH_RESPONSE=$(curl -s -X POST \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \
|
|
"$PORTAINER_URL/api/auth")
|
|
|
|
if [ $? -ne 0 ]; then
|
|
print_error "Failed to connect to Portainer at $PORTAINER_URL"
|
|
exit 1
|
|
fi
|
|
|
|
JWT_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.jwt // empty')
|
|
|
|
if [ -z "$JWT_TOKEN" ] || [ "$JWT_TOKEN" = "null" ]; then
|
|
print_error "Authentication failed. Please check your credentials."
|
|
print_error "Response: $AUTH_RESPONSE"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "Authentication successful."
|
|
}
|
|
|
|
# Function to get all environments (endpoints)
|
|
get_environments() {
|
|
print_status "Getting environments..."
|
|
|
|
ENVIRONMENTS_RESPONSE=$(curl -s -X GET \
|
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
"$PORTAINER_URL/api/endpoints")
|
|
|
|
if [ $? -ne 0 ]; then
|
|
print_error "Failed to get environments"
|
|
exit 1
|
|
fi
|
|
|
|
echo "$ENVIRONMENTS_RESPONSE" | jq -r '.[].Id' 2>/dev/null || echo "1"
|
|
}
|
|
|
|
# Function to get all stacks
|
|
get_stacks() {
|
|
print_status "Getting list of stacks..." >&2
|
|
|
|
STACKS_RESPONSE=$(curl -s -X GET \
|
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
"$PORTAINER_URL/api/stacks")
|
|
|
|
if [ $? -ne 0 ]; then
|
|
print_error "Failed to get stacks" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "$STACKS_RESPONSE"
|
|
}
|
|
|
|
# Function to get detailed stack information
|
|
get_stack_details() {
|
|
local stack_id=$1
|
|
local endpoint_id=$2
|
|
|
|
curl -s -X GET \
|
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
"$PORTAINER_URL/api/stacks/$stack_id?endpointId=$endpoint_id"
|
|
}
|
|
|
|
# Function to get stack file content
|
|
get_stack_file() {
|
|
local stack_id=$1
|
|
|
|
curl -s -X GET \
|
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
"$PORTAINER_URL/api/stacks/$stack_id/file"
|
|
}
|
|
|
|
# Function to save stack data
|
|
save_stack_data() {
|
|
local stack_info=$1
|
|
local stack_name=$(echo "$stack_info" | jq -r '.Name')
|
|
local stack_id=$(echo "$stack_info" | jq -r '.Id')
|
|
local endpoint_id=$(echo "$stack_info" | jq -r '.EndpointId')
|
|
|
|
print_status "Processing stack: $stack_name (ID: $stack_id)"
|
|
|
|
# Create directory for this stack
|
|
local stack_dir="$OUTPUT_DIR/$stack_name"
|
|
mkdir -p "$stack_dir"
|
|
|
|
# Save basic stack information
|
|
echo "$stack_info" | jq '.' > "$stack_dir/stack-info.json"
|
|
|
|
# Extract and save environment variables
|
|
echo "$stack_info" | jq '.Env // []' > "$stack_dir/environment-variables.json"
|
|
|
|
# Save environment variables in .env format
|
|
echo "$stack_info" | jq -r '.Env[]? | select(.name != null and .value != null) | "\(.name)=\(.value)"' > "$stack_dir/.env"
|
|
|
|
# Get and save stack file content (docker-compose.yml)
|
|
print_status "Getting stack file for: $stack_name"
|
|
STACK_FILE_RESPONSE=$(get_stack_file "$stack_id")
|
|
|
|
if [ $? -eq 0 ] && [ "$STACK_FILE_RESPONSE" != "null" ]; then
|
|
echo "$STACK_FILE_RESPONSE" | jq -r '.StackFileContent // empty' > "$stack_dir/docker-compose.yml"
|
|
else
|
|
print_warning "Could not retrieve stack file for: $stack_name"
|
|
fi
|
|
|
|
# Save additional metadata
|
|
cat > "$stack_dir/metadata.txt" << EOF
|
|
Stack Name: $stack_name
|
|
Stack ID: $stack_id
|
|
Endpoint ID: $endpoint_id
|
|
Creation Date: $(echo "$stack_info" | jq -r '.CreationDate // "N/A"')
|
|
Update Date: $(echo "$stack_info" | jq -r '.UpdateDate // "N/A"')
|
|
Status: $(echo "$stack_info" | jq -r '.Status // "N/A"')
|
|
Type: $(echo "$stack_info" | jq -r '.Type // "N/A"')
|
|
Entry Point: $(echo "$stack_info" | jq -r '.EntryPoint // "N/A"')
|
|
EOF
|
|
|
|
print_status "Stack $stack_name saved to $stack_dir"
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
print_status "Starting Portainer stack backup..."
|
|
|
|
check_dependencies
|
|
get_config
|
|
authenticate
|
|
|
|
# Create output directory
|
|
mkdir -p "$OUTPUT_DIR"
|
|
|
|
# Get all stacks
|
|
STACKS_JSON=$(get_stacks)
|
|
|
|
if [ -z "$STACKS_JSON" ] || [ "$STACKS_JSON" = "null" ]; then
|
|
print_warning "No stacks found or unable to retrieve stacks."
|
|
exit 1
|
|
fi
|
|
|
|
# Count stacks
|
|
STACK_COUNT=$(echo "$STACKS_JSON" | jq '. | length')
|
|
print_status "Found $STACK_COUNT stack(s)"
|
|
|
|
if [ "$STACK_COUNT" -eq 0 ]; then
|
|
print_warning "No stacks to backup."
|
|
exit 0
|
|
fi
|
|
|
|
# Process each stack
|
|
echo "$STACKS_JSON" | jq -c '.[]' | while read -r stack; do
|
|
save_stack_data "$stack"
|
|
echo "---"
|
|
done
|
|
|
|
print_status "Backup completed! All stacks saved to: $OUTPUT_DIR"
|
|
print_status "Summary:"
|
|
ls -la "$OUTPUT_DIR"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |