CF Deployer - Cloud Foundry Deployment Service
A Spring Boot application that provides a REST API for deploying Java applications to Cloud Foundry/Tanzu environments.
Prerequisites
- Java 21
- Gradle 8.14
Build and Run
Build the application
./gradlew clean build
Run the application
./gradlew bootRun
Or run the JAR directly:
java -jar build/libs/cf-uploader-1.0.0.jar
The application will start on http://localhost:8080
API Endpoints
Deployment Options
This service provides two deployment methods:
- Traditional Upload (
/api/cf/deploy) - Single request deployment (may hit nginx size/timeout limits) - Chunked Upload (
/api/cf/upload/*) - Chunked upload with async deployment (recommended for production)
Option 1: Traditional Deploy (Simple, may timeout with large files)
Endpoint: POST /api/cf/deploy
Content-Type: multipart/form-data
Request Parts:
request(JSON string): Deployment configurationjarFile(file): The Java application JAR filemanifest(file): Cloud Foundry manifest.yml file
Sample cURL Request
curl -X POST http://localhost:8080/api/cf/deploy \
-F 'request={
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "my-app",
"skipSslValidation": false
}' \
-F 'jarFile=@/path/to/your/application.jar' \
-F 'manifest=@/path/to/manifest.yml'
Sample Request with Skip SSL Validation
curl -X POST http://localhost:8080/api/cf/deploy \
-F 'request={
"apiEndpoint": "https://api.sys.tanzu.example.com",
"username": "admin",
"password": "admin123",
"organization": "development",
"space": "dev",
"appName": "sample-app",
"skipSslValidation": true
}' \
-F 'jarFile=@./sample-app.jar' \
-F 'manifest=@./manifest.yml'
Option 2: Chunked Upload with Async Deployment (Recommended for Production)
Why use chunked upload?
- Bypasses nginx
client_max_body_sizerestrictions - Avoids nginx timeout issues during deployment
- Better for large JAR files (>10MB)
- More resilient to network interruptions
Quick Start: Using the Deployment Script
The easiest way to deploy using chunked upload is with the provided bash script:
-
Configure the script:
# Edit deploy-chunked.sh and set your values: API_BASE="https://your-cf-deployer.example.com/api/cf" JAR_FILE="./your-app.jar" MANIFEST_FILE="./manifest.yml" 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" -
Make it executable and run:
chmod +x deploy-chunked.sh ./deploy-chunked.sh
The script handles:
- File chunking (1MB chunks by default)
- Progress tracking
- Async deployment
- Status polling
- Error handling
Manual cURL Commands (For Custom Integration)
Step 1: Initialize Upload Session
SESSION_ID=$(curl -s -X POST "http://localhost:8080/api/cf/upload/init" \
-H "Content-Type: application/json" \
-d '{
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "my-app",
"skipSslValidation": false
}' | grep -o '"uploadSessionId":"[^"]*' | cut -d'"' -f4)
echo "Session ID: $SESSION_ID"
Step 2: Upload JAR File in Chunks
# Split your JAR into 1MB chunks and upload each
CHUNK_SIZE=1048576 # 1MB
FILE="./app.jar"
TOTAL_CHUNKS=$(( ($(stat -c%s "$FILE") + CHUNK_SIZE - 1) / CHUNK_SIZE ))
# Split file and upload each chunk
split -b $CHUNK_SIZE "$FILE" chunk_
CHUNK_INDEX=0
for chunk_file in chunk_*; do
curl -X POST "http://localhost:8080/api/cf/upload/chunk" \
-F "uploadSessionId=$SESSION_ID" \
-F "fileType=jarFile" \
-F "chunkIndex=$CHUNK_INDEX" \
-F "totalChunks=$TOTAL_CHUNKS" \
-F "fileName=app.jar" \
-F "chunk=@$chunk_file"
CHUNK_INDEX=$((CHUNK_INDEX + 1))
done
rm chunk_* # Cleanup
Step 3: Upload Manifest
curl -X POST "http://localhost:8080/api/cf/upload/chunk" \
-F "uploadSessionId=$SESSION_ID" \
-F "fileType=manifest" \
-F "chunkIndex=0" \
-F "totalChunks=1" \
-F "fileName=manifest.yml" \
-F "chunk=@./manifest.yml"
Step 4: Start Async Deployment
curl -X POST "http://localhost:8080/api/cf/upload/finalize?uploadSessionId=$SESSION_ID&async=true"
# Response: {"uploadSessionId":"...","status":"IN_PROGRESS","message":"Deployment started...","progress":0}
Step 5: Poll Deployment Status
# Check deployment status every 5 seconds
while true; do
STATUS=$(curl -s "http://localhost:8080/api/cf/deployment/status/$SESSION_ID")
echo "$STATUS"
# Check if completed or failed
if echo "$STATUS" | grep -q '"status":"COMPLETED"'; then
echo "Deployment successful!"
break
elif echo "$STATUS" | grep -q '"status":"FAILED"'; then
echo "Deployment failed!"
break
fi
sleep 5
done
For more details, see:
deploy-chunked.sh- Direct nginx deployment with cert/headersdeploy-chunked-simple.sh- Java proxy deployment with Base64 encodingDEPLOYMENT_SCRIPTS.md- Comprehensive guide for both deployment scripts ⭐CHUNKED_UPLOAD_GUIDE.md- Detailed API documentationTIMEOUT_SOLUTION.md- Architecture and design detailsCHUNK_SIZE_GUIDE.md- Chunk size recommendationsMEMORY_FIX.md- JVM memory configuration for Tanzu
Two Deployment Scripts: Which One to Use?
We provide two deployment scripts for different network configurations:
| Script | Use When | Features |
|---|---|---|
| deploy-chunked.sh | Direct access to nginx | Multipart uploads, cert/key support, custom headers |
| deploy-chunked-simple.sh | Through Java proxy | Base64 encoded uploads, proxy adds cert/headers |
📖 See DEPLOYMENT_SCRIPTS.md for detailed comparison, troubleshooting, and technical explanations.
Quick Decision Guide:
- ✅ Use
deploy-chunked.shif you have direct nginx access and need to provide certificates/headers - ✅ Use
deploy-chunked-simple.shif you go through a Java proxy that adds authentication automatically
2. List Applications
Endpoint: POST /api/cf/apps
Content-Type: application/json
Lists all applications in the specified organization and space.
Sample cURL Request
curl -X POST http://localhost:8080/api/cf/apps \
-H "Content-Type: application/json" \
-d '{
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "",
"skipSslValidation": false
}'
3. List Routes
Endpoint: POST /api/cf/routes
Content-Type: application/json
Lists all routes in the specified organization and space.
Sample cURL Request
curl -X POST http://localhost:8080/api/cf/routes \
-H "Content-Type: application/json" \
-d '{
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "",
"skipSslValidation": false
}'
4. Get Application Details
Endpoint: POST /api/cf/app/{appName}
Content-Type: application/json
Gets detailed information about a specific application.
Sample cURL Request
curl -X POST http://localhost:8080/api/cf/app/my-app \
-H "Content-Type: application/json" \
-d '{
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "",
"skipSslValidation": false
}'
5. Get Application Logs
Endpoint: POST /api/cf/logs/{appName}?recent=true
Content-Type: application/json
Query Parameters:
recent(optional, default:true): If true, gets recent logs; if false, tails logs
Gets logs for a specific application.
Sample cURL Request (Recent Logs)
curl -X POST "http://localhost:8080/api/cf/logs/my-app?recent=true" \
-H "Content-Type: application/json" \
-d '{
"apiEndpoint": "https://api.cf.example.com",
"username": "your-username",
"password": "your-password",
"organization": "your-org",
"space": "your-space",
"appName": "",
"skipSslValidation": false
}'
Sample Manifest File (manifest.yml)
---
applications:
- name: sample-app
memory: 1G
instances: 2
path: sample-app.jar
buildpacks:
- java_buildpack
env:
SPRING_PROFILES_ACTIVE: production
Common Request Parameters
All endpoints require the following CF credentials and target information:
| Field | Type | Required | Description |
|---|---|---|---|
apiEndpoint |
String | Yes | Cloud Foundry API endpoint URL |
username |
String | Yes | CF username |
password |
String | Yes | CF password |
organization |
String | Yes | Target CF organization |
space |
String | Yes | Target CF space |
appName |
String | No* | Application name (required only for /deploy endpoint) |
skipSslValidation |
Boolean | Yes | Skip SSL certificate validation |
Note: Even though appName is not used by utility endpoints (/apps, /routes, /app/{appName}, /logs/{appName}), it must still be included in the JSON request body (can be empty string). The organization and space determine which apps/routes are listed or queried.
Response Format
Success Response
{
"success": true,
"message": "Application deployed successfully",
"deploymentId": "123e4567-e89b-12d3-a456-426614174000",
"output": "CF CLI output logs...",
"error": null
}
Error Response
{
"success": false,
"message": "Application deployment failed",
"deploymentId": "123e4567-e89b-12d3-a456-426614174000",
"output": "CF CLI output logs...",
"error": "Error details..."
}
Configuration
Application settings can be configured in src/main/resources/application.properties:
# Server port
server.port=8080
# Max file size (default: 500MB)
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB
# CF CLI timeout in seconds (default: 600)
cf.cli.timeout=600
# Optional: Custom CF CLI path (if not using bundled binaries)
cf.cli.path=
Features
- Application Deployment: Deploy JAR files to Cloud Foundry with manifest support
- Chunked Upload Support: Upload large files in chunks to bypass nginx size restrictions (NEW)
- Async Deployment: Non-blocking deployment with status polling to avoid timeout issues (NEW)
- Application Management: List apps, view details, and access logs
- Route Management: List all routes in your CF space
- Automatic CF CLI Management: Bundled CF CLI binaries for Linux, macOS, and Windows
- Secure Password Handling: Passwords are masked in all log output
- Comprehensive Logging: Detailed DEBUG-level logging for troubleshooting deployments
- Configurable Timeouts: Adjustable timeout for long-running deployments (default: 600s)
- Large File Support: Chunked uploads support files of any size
- Automatic Cleanup: Temporary files are automatically cleaned up after operations
- Error Handling: Comprehensive exception handling with detailed error messages
- Production Ready: Memory-optimized for Tanzu deployments with low-memory instances
Error Handling
The API handles various error scenarios:
- 400 Bad Request: Invalid request parameters or file validation errors
- 413 Payload Too Large: Files exceed 500MB limit
- 500 Internal Server Error: Deployment failures or unexpected errors
Development
Project Structure
src/main/java/com/cfdeployer/
├── controller/ # REST controllers
├── service/ # Business logic
├── model/ # DTOs
└── exception/ # Exception handlers
src/main/resources/
├── application.properties
└── cf-cli/ # CF CLI binaries (auto-downloaded)
Build with Custom CF CLI Version
Edit build.gradle to change the CF CLI version:
def cfCliVersion = '8.7.10' // Change this version
License
This project is licensed under the MIT License.