// Jenkinsfile for Manual Rollback // This allows rolling back to any previous version pipeline { agent any parameters { choice( name: 'ROLLBACK_METHOD', choices: ['IMAGE_TAG', 'REVISION_NUMBER', 'GIT_COMMIT'], description: 'Select rollback method' ) string( name: 'TARGET_VERSION', defaultValue: '', description: ''' Enter target version based on method: - IMAGE_TAG: main-21, main-20, etc. - REVISION_NUMBER: 1, 2, 3 (from rollout history) - GIT_COMMIT: abc123def (git commit SHA) ''' ) booleanParam( name: 'SKIP_HEALTH_CHECK', defaultValue: false, description: 'Skip health checks (use with caution!)' ) booleanParam( name: 'DRY_RUN', defaultValue: false, description: 'Dry run - show what would happen without applying' ) } environment { APP_NAME = 'demo-nginx' NAMESPACE = 'demo-app' DOCKER_REGISTRY = 'docker.io' DOCKER_REPO = 'vladcrypto' GITEA_URL = 'http://gitea-http.gitea.svc.cluster.local:3000' HEALTH_CHECK_TIMEOUT = '300s' } stages { stage('Validate Input') { steps { script { echo "🔍 Validating rollback request..." if (params.TARGET_VERSION == '') { error("❌ TARGET_VERSION cannot be empty!") } echo """ 📋 Rollback Configuration: Method: ${params.ROLLBACK_METHOD} Target: ${params.TARGET_VERSION} Skip Health Check: ${params.SKIP_HEALTH_CHECK} Dry Run: ${params.DRY_RUN} """ } } } stage('Show Current State') { steps { script { echo "📸 Current Deployment State:" sh """ echo "=== Current Deployment ===" kubectl get deployment ${APP_NAME} -n ${NAMESPACE} echo "" echo "=== Current Image ===" kubectl get deployment ${APP_NAME} -n ${NAMESPACE} \ -o jsonpath='{.spec.template.spec.containers[0].image}' echo "" echo "" echo "=== Current Pods ===" kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} echo "" echo "=== Rollout History ===" kubectl rollout history deployment/${APP_NAME} -n ${NAMESPACE} """ } } } stage('Prepare Rollback') { steps { script { echo "🔄 Preparing rollback to: ${params.TARGET_VERSION}" if (params.ROLLBACK_METHOD == 'IMAGE_TAG') { env.TARGET_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${params.TARGET_VERSION}" sh """ echo "Target image: ${env.TARGET_IMAGE}" """ } else if (params.ROLLBACK_METHOD == 'REVISION_NUMBER') { env.REVISION = params.TARGET_VERSION sh """ echo "Rolling back to revision: ${env.REVISION}" # Verify revision exists kubectl rollout history deployment/${APP_NAME} -n ${NAMESPACE} \ --revision=${env.REVISION} """ } else if (params.ROLLBACK_METHOD == 'GIT_COMMIT') { env.GIT_SHA = params.TARGET_VERSION echo "Rolling back to git commit: ${env.GIT_SHA}" } } } } stage('Execute Rollback') { when { expression { !params.DRY_RUN } } steps { script { echo "🚀 Executing rollback..." if (params.ROLLBACK_METHOD == 'IMAGE_TAG') { // Method 1: Update image directly sh """ echo "Setting image to: ${env.TARGET_IMAGE}" kubectl set image deployment/${APP_NAME} \ ${APP_NAME}=${env.TARGET_IMAGE} \ -n ${NAMESPACE} \ --record """ // Update Git manifests withCredentials([usernamePassword( credentialsId: 'gitea-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS' )]) { sh """ rm -rf k3s-gitops || true git clone http://\${GIT_USER}:\${GIT_PASS}@gitea-http.gitea.svc.cluster.local:3000/admin/k3s-gitops.git cd k3s-gitops git config user.name "Jenkins" git config user.email "jenkins@thedevops.dev" sed -i 's|image: .*|image: ${env.TARGET_IMAGE}|' apps/demo-nginx/deployment.yaml git add apps/demo-nginx/deployment.yaml git commit -m "rollback(demo-nginx): Manual rollback to ${params.TARGET_VERSION}" || echo "No changes" git push origin main """ } } else if (params.ROLLBACK_METHOD == 'REVISION_NUMBER') { // Method 2: Rollback to specific revision sh """ kubectl rollout undo deployment/${APP_NAME} \ -n ${NAMESPACE} \ --to-revision=${env.REVISION} """ } else if (params.ROLLBACK_METHOD == 'GIT_COMMIT') { // Method 3: Checkout specific git commit withCredentials([usernamePassword( credentialsId: 'gitea-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS' )]) { sh """ rm -rf k3s-gitops || true git clone http://\${GIT_USER}:\${GIT_PASS}@gitea-http.gitea.svc.cluster.local:3000/admin/k3s-gitops.git cd k3s-gitops git config user.name "Jenkins" git config user.email "jenkins@thedevops.dev" # Get image from specific commit git checkout ${env.GIT_SHA} -- apps/demo-nginx/deployment.yaml TARGET_IMAGE=\$(grep 'image:' apps/demo-nginx/deployment.yaml | awk '{print \$2}') git checkout main git checkout ${env.GIT_SHA} -- apps/demo-nginx/deployment.yaml git add apps/demo-nginx/deployment.yaml git commit -m "rollback(demo-nginx): Rollback to commit ${env.GIT_SHA}" || echo "No changes" git push origin main echo "Rolled back to image: \${TARGET_IMAGE}" """ } } echo "✅ Rollback command executed" } } } stage('Wait for Rollout') { when { expression { !params.DRY_RUN } } steps { script { echo "⏳ Waiting for rollout to complete..." sh """ kubectl rollout status deployment/${APP_NAME} \ -n ${NAMESPACE} \ --timeout=${HEALTH_CHECK_TIMEOUT} """ echo "✅ Rollout completed" } } } stage('Health Check') { when { expression { !params.DRY_RUN && !params.SKIP_HEALTH_CHECK } } steps { script { echo "🏥 Running health checks..." sh """ # Check all pods are ready READY_PODS=\$(kubectl get deployment ${APP_NAME} -n ${NAMESPACE} -o jsonpath='{.status.readyReplicas}') DESIRED_PODS=\$(kubectl get deployment ${APP_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.replicas}') echo "Ready pods: \${READY_PODS}/\${DESIRED_PODS}" if [ "\${READY_PODS}" != "\${DESIRED_PODS}" ]; then echo "❌ Not all pods are ready!" exit 1 fi # Test health endpoint POD_NAME=\$(kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o jsonpath='{.items[0].metadata.name}') kubectl exec \${POD_NAME} -n ${NAMESPACE} -- wget -q -O- http://localhost/health """ echo "✅ Health checks passed" } } } stage('Show New State') { when { expression { !params.DRY_RUN } } steps { script { echo "📊 New Deployment State:" sh """ echo "=== New Deployment ===" kubectl get deployment ${APP_NAME} -n ${NAMESPACE} echo "" echo "=== New Image ===" kubectl get deployment ${APP_NAME} -n ${NAMESPACE} \ -o jsonpath='{.spec.template.spec.containers[0].image}' echo "" echo "" echo "=== New Pods ===" kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} """ } } } stage('Dry Run Summary') { when { expression { params.DRY_RUN } } steps { script { echo """ 🔍 DRY RUN SUMMARY This is what would happen: Method: ${params.ROLLBACK_METHOD} Target: ${params.TARGET_VERSION} Steps that would be executed: 1. Update deployment to target version 2. Update Git manifests 3. Wait for rollout (timeout: ${HEALTH_CHECK_TIMEOUT}) ${params.SKIP_HEALTH_CHECK ? '4. (Health check skipped)' : '4. Run health checks'} No actual changes were made. """ } } } } post { success { script { if (params.DRY_RUN) { echo "✅ DRY RUN COMPLETED - No changes made" } else { echo """ ✅ ROLLBACK SUCCESSFUL! Application: ${APP_NAME} Method: ${params.ROLLBACK_METHOD} Target: ${params.TARGET_VERSION} Namespace: ${NAMESPACE} The application has been rolled back successfully! ✨ """ } } } failure { echo """ ❌ ROLLBACK FAILED! Please check the logs and try again. Manual rollback command: kubectl rollout undo deployment/${APP_NAME} -n ${NAMESPACE} """ } } }