feat: Enhance stack deployment process in Jenkins pipeline with improved payload handling and validation

This commit is contained in:
Thibault Pouch
2026-03-19 21:06:09 +01:00
parent 8f5b5d5e8d
commit 3d255bdea2

View File

@@ -58,36 +58,89 @@ pipeline {
}
}
stage('Deploy to Portainer') {
stage('Prepare compose payload') {
steps {
sh '''
base64 -w 0 docker-compose.prod.yml > compose-content.b64
echo "Compose payload prepared"
'''
}
}
stage('Fetch Portainer stacks') {
steps {
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
sh '''
COMPOSE_CONTENT=$(base64 -w 0 docker-compose.prod.yml)
curl -s -X GET "''' + env.PORTAINER_URL + '''/api/stacks" \
-H "X-API-Key: $PORTAINER_TOKEN" > portainer-stacks.json
echo "Portainer stacks fetched"
'''
}
}
}
# Get the list of stacks
STACKS=$(curl -s -X GET "''' + env.PORTAINER_URL + '''/api/stacks" \
-H "X-API-Key: $PORTAINER_TOKEN")
stage('Resolve target stack') {
steps {
script {
env.STACK_ID = sh(
script: "jq -r '.[] | select(.Name == \"${params.STACK_NAME}\") | .Id' portainer-stacks.json | head -n 1",
returnStdout: true
).trim()
# Check whether the stack already exists
STACK_ID=$(echo "$STACKS" | jq -r '.[] | select(.Name == "''' + params.STACK_NAME + '''") | .Id')
if (env.STACK_ID) {
echo "Existing stack found (ID: ${env.STACK_ID})"
} else {
echo "Stack does not exist and will be created"
}
}
}
}
if [ -n "$STACK_ID" ]; then
echo "Existing stack found (ID: $STACK_ID), updating..."
RESPONSE=$(curl -s -X PUT "''' + env.PORTAINER_URL + '''/api/stacks/$STACK_ID?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
stage('Update existing stack') {
when {
expression { return env.STACK_ID?.trim() }
}
steps {
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
sh '''
COMPOSE_CONTENT=$(cat compose-content.b64)
curl -s -X PUT "''' + env.PORTAINER_URL + '''/api/stacks/$STACK_ID?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
-H "X-API-Key: $PORTAINER_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"stackFileContent\": \"$COMPOSE_CONTENT\", \"prune\": true, \"pullImage\": true}")
else
echo "Creating stack ''' + params.STACK_NAME + '''..."
RESPONSE=$(curl -s -X POST "''' + env.PORTAINER_URL + '''/api/stacks/create/standalone/string?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
-d "{\"stackFileContent\": \"$COMPOSE_CONTENT\", \"prune\": true, \"pullImage\": true}" > portainer-response.json
echo "Stack updated"
'''
}
}
}
stage('Create new stack') {
when {
expression { return !env.STACK_ID?.trim() }
}
steps {
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
sh '''
COMPOSE_CONTENT=$(cat compose-content.b64)
curl -s -X POST "''' + env.PORTAINER_URL + '''/api/stacks/create/standalone/string?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
-H "X-API-Key: $PORTAINER_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"''' + params.STACK_NAME + '''\", \"stackFileContent\": \"$COMPOSE_CONTENT\"}")
fi
-d "{\"name\": \"''' + params.STACK_NAME + '''\", \"stackFileContent\": \"$COMPOSE_CONTENT\"}" > portainer-response.json
# Check whether the response contains an error
if echo "$RESPONSE" | jq -e '.message' > /dev/null 2>&1; then
echo "Portainer error: $(echo $RESPONSE | jq -r '.message')"
echo "Stack created"
'''
}
}
}
stage('Validate Portainer response') {
steps {
sh '''
if jq -e '.message' portainer-response.json > /dev/null 2>&1; then
echo "Portainer error: $(jq -r '.message' portainer-response.json)"
exit 1
fi
@@ -95,7 +148,6 @@ pipeline {
'''
}
}
}
}