# 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 ```bash ./gradlew clean build ``` ### Run the application ```bash ./gradlew bootRun ``` Or run the JAR directly: ```bash 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: 1. **Traditional Upload** (`/api/cf/deploy`) - Single request deployment (may hit nginx size/timeout limits) 2. **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 configuration - `jarFile` (file): The Java application JAR file - `manifest` (file): Cloud Foundry manifest.yml file #### Sample cURL Request ```bash 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 ```bash 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_size` restrictions - 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: 1. **Configure the script:** ```bash # 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" ``` 2. **Make it executable and run:** ```bash 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** ```bash 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** ```bash # 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** ```bash 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** ```bash 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** ```bash # 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/headers - `deploy-chunked-simple.sh` - Java proxy deployment with Base64 encoding - **`DEPLOYMENT_SCRIPTS.md`** - **Comprehensive guide for both deployment scripts** ⭐ - `CHUNKED_UPLOAD_GUIDE.md` - Detailed API documentation - `TIMEOUT_SOLUTION.md` - Architecture and design details - `CHUNK_SIZE_GUIDE.md` - Chunk size recommendations - `MEMORY_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](DEPLOYMENT_SCRIPTS.md) for detailed comparison, troubleshooting, and technical explanations.** **Quick Decision Guide:** - ✅ Use `deploy-chunked.sh` if you have direct nginx access and need to provide certificates/headers - ✅ Use `deploy-chunked-simple.sh` if 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 ```bash 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 ```bash 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 ```bash 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) ```bash 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) ```yaml --- 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 ```json { "success": true, "message": "Application deployed successfully", "deploymentId": "123e4567-e89b-12d3-a456-426614174000", "output": "CF CLI output logs...", "error": null } ``` ### Error Response ```json { "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`: ```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: ```groovy def cfCliVersion = '8.7.10' // Change this version ``` ## License This project is licensed under the MIT License.