8.4 KiB
CF Deployer - Deployment Scripts Documentation
Overview
This repository contains a Spring Boot application for deploying JAR files to Cloud Foundry/Tanzu environments using chunked uploads. There are two deployment scripts designed for different network paths.
Architecture
┌─────────────────────┐
│ deploy-chunked.sh │──────► nginx ──────► Spring Boot App
│ (Direct to nginx) │ (multipart endpoint)
└─────────────────────┘
┌─────────────────────────┐
│deploy-chunked-simple.sh │──► Java Proxy ──► nginx ──► Spring Boot App
│ (Through Java proxy) │ (adds headers) (base64 endpoint)
└─────────────────────────┘
Deployment Scripts
1. deploy-chunked.sh
Use when: Direct access to nginx endpoint with cert/key/headers
Features:
- Sends chunks as multipart form data (
-Fflags) - Supports client certificates (
--cert,--key) - Supports custom headers (
X-Forwarded-For,My-APIM-KEY) - Uses Spring Boot endpoint:
POST /upload/chunk(multipart)
Configuration:
# Lines 49-55
CERT_FILE="/path/to/cert.pem"
KEY_FILE="/path/to/key.pem"
X_FORWARDED_FOR="192.168.1.100"
MY_APIM_KEY="your-api-key"
Curl format:
curl POST /upload/chunk \
--cert cert.pem --key key.pem \
-H "X-Forwarded-For: ..." \
-H "My-APIM-KEY: ..." \
-F "uploadSessionId=..." \
-F "fileType=..." \
-F "chunk=@chunk_file"
2. deploy-chunked-simple.sh
Use when: Going through Java proxy that adds cert/headers automatically
Features:
- Sends chunks as Base64-encoded text (to work with Java proxy)
- Query parameters in URL (for Java proxy's
request.getQueryString()) - No cert/key/headers needed (Java proxy adds them)
- Uses Spring Boot endpoint:
POST /upload/chunk(text/plain, base64)
Configuration:
# Line 24
API_BASE="https://myapp.com/v1/utility"
Curl format:
curl POST /upload/chunk?uploadSessionId=...&fileType=... \
-H "Content-Type: text/plain" \
-H "X-Chunk-Encoding: base64" \
-d @base64_chunk_file
Why Two Different Scripts?
The Java Proxy Problem
The Java proxy that sits in front of the Spring Boot app reads the request body as a String:
@RequestBody(required = false) String body
Problem: Binary multipart data gets corrupted when read as String Solution: Base64 encode chunks as text before sending through the proxy
Deploy Script Comparison
| Feature | deploy-chunked.sh | deploy-chunked-simple.sh |
|---|---|---|
| Network Path | Direct to nginx | Through Java proxy |
| Chunk Format | Multipart binary | Base64 text |
| Query Params | No (uses -F form fields) |
Yes (in URL) |
| Cert/Key | Required in script | Added by Java proxy |
| Headers | Required in script | Added by Java proxy |
| Spring Endpoint | multipart/form-data | text/plain |
Spring Boot Endpoints
The Spring Boot app has three chunk upload endpoints:
1. Multipart Endpoint (Original)
@PostMapping("/upload/chunk")
// Consumes: multipart/form-data
// Parameters: All as form fields (-F)
// File: @RequestPart("chunk") MultipartFile
Used by: deploy-chunked.sh (direct to nginx)
2. Raw Binary Endpoint
@PostMapping(value = "/upload/chunk", consumes = "application/octet-stream")
// Consumes: application/octet-stream
// Parameters: Query params in URL
// File: @RequestBody byte[]
Used by: Not currently used (would fail through Java proxy)
3. Base64 Text Endpoint
@PostMapping(value = "/upload/chunk", consumes = "text/plain")
// Consumes: text/plain
// Parameters: Query params in URL
// File: @RequestBody String (Base64 decoded)
// Header: X-Chunk-Encoding: base64
Used by: deploy-chunked-simple.sh (through Java proxy)
Spring Boot routes to the correct endpoint based on the Content-Type header!
Common Configuration (Both Scripts)
Both scripts share these configuration options:
# Files to deploy
JAR_FILE="./app.jar"
MANIFEST_FILE="./manifest.yml"
# Chunk size (1MB recommended for Tanzu)
CHUNK_SIZE=1048576
# 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"
# Polling configuration
POLL_INTERVAL=5
MAX_WAIT=600
# Debug mode
DEBUG_MODE="false" # Set to "true" for verbose output
Deployment Flow
Both scripts follow the same 5-step process:
- Initialize Upload Session: POST
/upload/initwith CF credentials - Upload JAR Chunks: POST
/upload/chunkfor each chunk - Upload Manifest Chunks: POST
/upload/chunkfor manifest.yml - Finalize Upload: POST
/upload/finalize?uploadSessionId=...&async=true - Poll Deployment Status: GET
/deployment/status/{uploadSessionId}
Troubleshooting
"Required part 'chunk' is not present"
- Cause: Nginx stripped multipart body or wrong Content-Type
- Solution: Use deploy-chunked-simple.sh with Base64 encoding
"504 Gateway Timeout" on chunk upload
- Cause: Java proxy trying to read binary data as String
- Solution: Use Base64 encoding (deploy-chunked-simple.sh)
"Argument list too long"
- Cause: Base64 string passed as command argument instead of file
- Solution: Already fixed - script writes Base64 to temp file and uses
-d @file
"Missing uploadSessionId parameter"
- Cause: Nginx or proxy stripping query parameters
- For deploy-chunked.sh: Parameters should be in form fields (
-F) - For deploy-chunked-simple.sh: Parameters should be in query string (
?uploadSessionId=...)
Technical Notes
Why Not Fix the Java Proxy?
The Java proxy is shared by multiple services, so modifying it could break other applications. Instead, we adapted the deployment script to work with the proxy's limitations.
Why Base64 Encoding?
When the Java proxy reads binary data as @RequestBody String body, it:
- Corrupts binary data (non-UTF8 bytes)
- May hang or timeout on large binary payloads
- Cannot properly forward multipart boundaries
Base64 encoding converts binary to safe ASCII text that the proxy can handle as a String.
Why Query Parameters for Simple Script?
The Java proxy reconstructs the request using:
String queryParams = request.getQueryString();
String completeRequest = WSGURL + req;
if (queryParams != null) {
completeRequest = completeRequest + "?" + queryParams;
}
It only forwards query parameters, not form field parameters, so we must use query strings.
Performance Impact of Base64
Base64 encoding increases payload size by ~33%:
- 1MB binary chunk → ~1.33MB Base64 text
- Adds CPU overhead for encoding/decoding
- Acceptable tradeoff for proxy compatibility
Testing
Test deploy-chunked.sh (Direct to nginx)
# Configure cert/key/headers in script
vim deploy-chunked.sh
# Run with debug
DEBUG_MODE="true" ./deploy-chunked.sh
Test deploy-chunked-simple.sh (Through proxy)
# Configure API base URL
vim deploy-chunked-simple.sh
# Run with debug
DEBUG_MODE="true" ./deploy-chunked-simple.sh
Dependencies
Shell Requirements
bash4.0+curlawk(replacesbcfor file size calculation)base64(for deploy-chunked-simple.sh)mktempsplitstat
Backend Requirements
- Spring Boot 3.2.0+
- Java 17+
- Gradle 8.14
File Reference
| File | Purpose |
|---|---|
deploy-chunked.sh |
Direct nginx deployment with cert/headers |
deploy-chunked-simple.sh |
Java proxy deployment with Base64 |
CfDeployController.java |
REST endpoints (3 chunk upload variants) |
ChunkedUploadService.java |
Chunk processing (multipart + raw bytes) |
AsyncDeploymentService.java |
Background deployment execution |
Quick Start
For direct nginx access:
cp deploy-chunked.sh my-deploy.sh
# Edit configuration
vim my-deploy.sh
# Run
./my-deploy.sh
For Java proxy access:
cp deploy-chunked-simple.sh my-deploy.sh
# Edit API_BASE
vim my-deploy.sh
# Run
./my-deploy.sh
Support
For issues or questions:
- Enable
DEBUG_MODE="true"in the script - Check the curl commands and responses
- Review Spring Boot application logs
- Verify nginx/proxy logs for request forwarding