Files
cf-uploader/deploy-chunked.sh
2025-10-22 11:22:53 -05:00

309 lines
9.1 KiB
Bash

#!/bin/bash
#############################################################################
# CF Deployer - Chunked Upload Deployment Script
#
# This script deploys a Java application to Cloud Foundry using chunked
# uploads to bypass nginx size restrictions and async deployment to avoid
# timeout issues.
#
# Usage:
# ./deploy-chunked.sh
#
# Configuration:
# Edit the variables below to match your environment
#############################################################################
set -e # Exit on error
#############################################################################
# CONFIGURATION - Update these values for your deployment
#############################################################################
# API endpoint
API_BASE="http://localhost:8080/api/cf"
# Files to deploy
JAR_FILE="./app.jar"
MANIFEST_FILE="./manifest.yml"
# Chunk size (bytes)
# Recommended: 1MB (1048576) for Tanzu with memory constraints
# Options: 512KB (524288), 1MB (1048576), 2MB (2097152), 5MB (5242880)
CHUNK_SIZE=1048576 # 1MB
# Cloud Foundry configuration
CF_API_ENDPOINT="https://api.cf.example.com"
CF_USERNAME="your-username"
CF_PASSWORD="your-password"
CF_ORGANIZATION="your-org"
CF_SPACE="your-space"
CF_APP_NAME="your-app"
CF_SKIP_SSL="false" # Use "true" for self-signed certificates
# Polling configuration
POLL_INTERVAL=5 # seconds between status checks
MAX_WAIT=600 # maximum wait time in seconds (10 minutes)
# Security configuration (optional)
CERT_FILE="" # Path to client certificate file (leave empty to skip)
X_FORWARDED_FOR="" # X-Forwarded-For header value (leave empty to skip)
MY_APIM_KEY="" # My-APIM-KEY header value (leave empty to skip)
#############################################################################
# SCRIPT - Do not modify below this line
#############################################################################
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Check if files exist
if [ ! -f "$JAR_FILE" ]; then
log_error "JAR file not found: $JAR_FILE"
exit 1
fi
if [ ! -f "$MANIFEST_FILE" ]; then
log_error "Manifest file not found: $MANIFEST_FILE"
exit 1
fi
# Check if curl is available
if ! command -v curl &> /dev/null; then
log_error "curl is not installed. Please install curl to use this script."
exit 1
fi
# Build curl options based on configuration
CURL_OPTS=""
if [ -n "$CERT_FILE" ]; then
if [ ! -f "$CERT_FILE" ]; then
log_error "Certificate file not found: $CERT_FILE"
exit 1
fi
CURL_OPTS="$CURL_OPTS --cert $CERT_FILE"
fi
if [ -n "$X_FORWARDED_FOR" ]; then
CURL_OPTS="$CURL_OPTS -H \"X-Forwarded-For: $X_FORWARDED_FOR\""
fi
if [ -n "$MY_APIM_KEY" ]; then
CURL_OPTS="$CURL_OPTS -H \"My-APIM-KEY: $MY_APIM_KEY\""
fi
# Build CF configuration JSON
CF_CONFIG=$(cat <<EOF
{
"apiEndpoint": "$CF_API_ENDPOINT",
"username": "$CF_USERNAME",
"password": "$CF_PASSWORD",
"organization": "$CF_ORGANIZATION",
"space": "$CF_SPACE",
"appName": "$CF_APP_NAME",
"skipSslValidation": $CF_SKIP_SSL
}
EOF
)
log_info "Starting deployment of $CF_APP_NAME to Cloud Foundry"
log_info "JAR: $JAR_FILE"
log_info "Manifest: $MANIFEST_FILE"
log_info "Chunk size: $CHUNK_SIZE bytes ($(($CHUNK_SIZE / 1024))KB)"
echo ""
#############################################################################
# STEP 1: Initialize Upload Session
#############################################################################
log_info "Step 1/5: Initializing upload session..."
INIT_RESPONSE=$(eval curl -s -X POST "$API_BASE/upload/init" \
-H "Content-Type: application/json" \
$CURL_OPTS \
-d "'$CF_CONFIG'")
if [ $? -ne 0 ]; then
log_error "Failed to initialize upload session"
echo "$INIT_RESPONSE"
exit 1
fi
SESSION_ID=$(echo "$INIT_RESPONSE" | grep -o '"uploadSessionId":"[^"]*' | cut -d'"' -f4)
if [ -z "$SESSION_ID" ]; then
log_error "Failed to get session ID from response:"
echo "$INIT_RESPONSE"
exit 1
fi
log_success "Upload session created: $SESSION_ID"
echo ""
#############################################################################
# FUNCTION: Upload file in chunks
#############################################################################
upload_file_in_chunks() {
local file_path=$1
local file_type=$2
local file_name=$(basename "$file_path")
# Get file size (cross-platform)
if [[ "$OSTYPE" == "darwin"* ]]; then
local file_size=$(stat -f%z "$file_path")
else
local file_size=$(stat -c%s "$file_path")
fi
local total_chunks=$(( ($file_size + $CHUNK_SIZE - 1) / $CHUNK_SIZE ))
local file_size_mb=$(awk "BEGIN {printf \"%.2f\", $file_size / 1048576}")
log_info "Uploading $file_type: $file_name (${file_size_mb}MB, $total_chunks chunks)"
# Create temporary directory for chunks
local temp_dir=$(mktemp -d)
# Split file into chunks
split -b $CHUNK_SIZE "$file_path" "$temp_dir/chunk_"
local chunk_index=0
for chunk_file in "$temp_dir"/chunk_*; do
printf " Chunk %3d/%3d... " "$((chunk_index + 1))" "$total_chunks"
RESPONSE=$(eval curl -s -X POST "$API_BASE/upload/chunk" \
$CURL_OPTS \
-F "uploadSessionId=$SESSION_ID" \
-F "fileType=$file_type" \
-F "chunkIndex=$chunk_index" \
-F "totalChunks=$total_chunks" \
-F "fileName=$file_name" \
-F "chunk=@$chunk_file")
SUCCESS=$(echo "$RESPONSE" | grep -o '"success":[^,}]*' | cut -d':' -f2)
if [ "$SUCCESS" != "true" ]; then
echo -e "${RED}FAILED${NC}"
log_error "Failed to upload chunk $((chunk_index + 1))/$total_chunks"
echo "$RESPONSE"
rm -rf "$temp_dir"
exit 1
fi
echo -e "${GREEN}OK${NC}"
chunk_index=$((chunk_index + 1))
done
# Cleanup temporary directory
rm -rf "$temp_dir"
log_success "$file_type upload completed ($total_chunks chunks)"
echo ""
}
#############################################################################
# STEP 2: Upload JAR file
#############################################################################
log_info "Step 2/5: Uploading JAR file..."
upload_file_in_chunks "$JAR_FILE" "jarFile"
#############################################################################
# STEP 3: Upload manifest file
#############################################################################
log_info "Step 3/5: Uploading manifest file..."
upload_file_in_chunks "$MANIFEST_FILE" "manifest"
#############################################################################
# STEP 4: Start async deployment
#############################################################################
log_info "Step 4/5: Starting async deployment..."
FINALIZE_RESPONSE=$(eval curl -s -X POST "$API_BASE/upload/finalize?uploadSessionId=$SESSION_ID&async=true" \
$CURL_OPTS)
STATUS=$(echo "$FINALIZE_RESPONSE" | grep -o '"status":"[^"]*' | cut -d'"' -f4)
if [ "$STATUS" != "IN_PROGRESS" ]; then
log_error "Failed to start deployment. Status: $STATUS"
echo "$FINALIZE_RESPONSE"
exit 1
fi
log_success "Deployment started successfully"
log_info "Step 5/5: Polling deployment status (max wait: ${MAX_WAIT}s)..."
echo ""
#############################################################################
# STEP 5: Poll deployment status
#############################################################################
elapsed=0
last_message=""
while [ $elapsed -lt $MAX_WAIT ]; do
sleep $POLL_INTERVAL
elapsed=$((elapsed + POLL_INTERVAL))
STATUS_RESPONSE=$(eval curl -s "$API_BASE/deployment/status/$SESSION_ID" \
$CURL_OPTS 2>/dev/null)
if [ $? -ne 0 ]; then
log_warning "Failed to fetch status, retrying..."
continue
fi
CURRENT_STATUS=$(echo "$STATUS_RESPONSE" | grep -o '"status":"[^"]*' | cut -d'"' -f4)
MESSAGE=$(echo "$STATUS_RESPONSE" | grep -o '"message":"[^"]*' | cut -d'"' -f4)
PROGRESS=$(echo "$STATUS_RESPONSE" | grep -o '"progress":[0-9]*' | cut -d':' -f2)
# Only print if message changed to reduce clutter
if [ "$MESSAGE" != "$last_message" ]; then
printf " [%3ds] Status: %-15s Progress: %3s%% - %s\n" \
"$elapsed" "$CURRENT_STATUS" "${PROGRESS:-0}" "$MESSAGE"
last_message="$MESSAGE"
fi
# Check if deployment completed
if [ "$CURRENT_STATUS" = "COMPLETED" ]; then
echo ""
log_success "Deployment completed successfully!"
echo ""
log_info "Deployment details:"
echo "$STATUS_RESPONSE" | python -m json.tool 2>/dev/null || echo "$STATUS_RESPONSE"
exit 0
fi
# Check if deployment failed
if [ "$CURRENT_STATUS" = "FAILED" ]; then
echo ""
log_error "Deployment failed!"
echo ""
log_info "Error details:"
echo "$STATUS_RESPONSE" | python -m json.tool 2>/dev/null || echo "$STATUS_RESPONSE"
exit 1
fi
done
# Timeout reached
echo ""
log_warning "Deployment timeout reached after ${MAX_WAIT}s"
log_info "The deployment may still be running. Check status manually:"
echo " curl $API_BASE/deployment/status/$SESSION_ID"
exit 1