pipeline { agent any options { timeout(time: 20, unit: 'MINUTES') timestamps() } stages { stage('Checkout') { steps { script { cleanWs() checkout scm echo "โœ… Code checked out successfully" echo "๐Ÿ“‚ Working directory: ${pwd()}" echo "๐ŸŒฟ Git branch: ${env.BRANCH_NAME}" echo "๐Ÿท๏ธ Git commit: ${env.GIT_COMMIT?.take(8)}" } } } stage('Validate Configuration') { steps { script { echo "๐Ÿ” Validating configuration files..." // Check required files exist if (!fileExists('docker-compose.yml')) { error("โŒ docker-compose.yml not found") } echo "โœ… Found: docker-compose.yml" if (!fileExists('.env.example')) { error("โŒ .env.example not found") } echo "โœ… Found: .env.example" // Validate Docker Compose syntax sh ''' echo "๐Ÿณ Validating Docker Compose files..." # Set dummy values for required environment variables during validation export PG_PASS="validation_dummy_password" export AUTHENTIK_SECRET_KEY="validation_dummy_secret_key_min_60_chars_aaaaaaaaaaaaaaaaaaaaaa" docker compose -f docker-compose.yml config --quiet || { echo "โŒ Docker Compose validation failed" exit 1 } echo "โœ… Docker Compose syntax is valid" ''' // Check Jenkins credentials configuration sh ''' echo "๐Ÿ” Checking Jenkins credentials configuration..." echo "โ„น๏ธ Note: Make sure you have configured these Jenkins credentials:" echo " - authentik-pg-pass (Secret Text): PostgreSQL password" echo " - authentik-secret-key (Secret Text): Authentik secret key (60+ chars)" echo " - authentik-pg-db (String, optional): Database name (default: authentik)" echo " - authentik-pg-user (String, optional): Database user (default: authentik)" echo " - authentik-postgres-host (String, optional): PostgreSQL host (default: postgres-postgres)" echo " - authentik-postgres-port (String, optional): PostgreSQL port (default: 5432)" echo "๐Ÿ“ Configure in: Jenkins โ†’ Manage Jenkins โ†’ Manage Credentials โ†’ (global)" ''' } } } stage('System Health Check') { steps { script { echo "๐Ÿ” Checking system resources..." sh ''' # Check Docker is running docker info > /dev/null && echo "โœ… Docker is running" # Check available resources echo "๐Ÿ’พ Memory information:" free -h || sysctl hw.memsize || echo "Memory check not available" echo "๐Ÿ’ฟ Disk space:" df -h . || echo "Disk check not available" echo "๐Ÿ–ฅ๏ธ CPU information:" nproc || sysctl -n hw.ncpu || echo "CPU check not available" ''' } } } stage('Verify Database') { when { anyOf { branch 'main' branch 'master' buildingTag() changeset "**/*.yml" changeset "**/*.yaml" expression { return env.BRANCH_NAME == null || env.BRANCH_NAME == 'main' || env.BRANCH_NAME == 'master' } } } steps { script { echo "๐Ÿ—„๏ธ Verifying Authentik database exists in PostgreSQL..." withCredentials([ string(credentialsId: 'authentik-pg-pass', variable: 'PG_PASS'), string(credentialsId: 'authentik-pg-db', variable: 'PG_DB'), string(credentialsId: 'authentik-pg-user', variable: 'PG_USER'), string(credentialsId: 'authentik-postgres-host', variable: 'POSTGRES_HOST') ]) { sh ''' export PG_DB="${PG_DB:-authentik}" export PG_USER="${PG_USER:-authentik}" export POSTGRES_HOST="${POSTGRES_HOST:-postgres-postgres}" export PGPASSWORD="${PG_PASS}" echo "๐Ÿ” Checking if PostgreSQL server is running..." if ! docker exec ${POSTGRES_HOST} pg_isready > /dev/null 2>&1; then echo "โŒ PostgreSQL server '${POSTGRES_HOST}' is not running or not accessible" echo "Please ensure the PostgreSQL deployment is running first" exit 1 fi echo "โœ… PostgreSQL server is running" echo "โ„น๏ธ Verifying database connection..." # Test connection to the database # If this succeeds, both the database and user exist with correct permissions if docker exec -e PGPASSWORD="${PG_PASS}" ${POSTGRES_HOST} psql -h localhost -U ${PG_USER} -d ${PG_DB} -c "SELECT 1 as connection_test;" > /dev/null 2>&1; then echo "โœ… Successfully connected to database '${PG_DB}' as user '${PG_USER}'" echo "โœ… Database and user verification completed" else echo "โŒ Cannot connect to database '${PG_DB}' as user '${PG_USER}'" echo "" echo "Possible causes:" echo " 1. Database '${PG_DB}' does not exist" echo " 2. User '${PG_USER}' does not exist" echo " 3. User '${PG_USER}' does not have permissions on database '${PG_DB}'" echo " 4. Password in Jenkins credentials does not match" echo "" echo "โ„น๏ธ To create the database and user, run on the PostgreSQL server:" echo " cd /path/to/postgresql/scripts" echo " python3 create_db_user.py --username ${PG_USER} --password --databases ${PG_DB}" echo "" exit 1 fi ''' } } } } stage('Deploy Authentik') { when { anyOf { branch 'main' branch 'master' buildingTag() changeset "**/*.yml" changeset "**/*.yaml" expression { return env.BRANCH_NAME == null || env.BRANCH_NAME == 'main' || env.BRANCH_NAME == 'master' } } } steps { script { echo "๐Ÿš€ Deploying Authentik..." try { // Deploy with Jenkins credentials withCredentials([ string(credentialsId: 'authentik-pg-pass', variable: 'PG_PASS'), string(credentialsId: 'authentik-secret-key', variable: 'AUTHENTIK_SECRET_KEY'), string(credentialsId: 'authentik-pg-db', variable: 'PG_DB'), string(credentialsId: 'authentik-pg-user', variable: 'PG_USER'), string(credentialsId: 'authentik-postgres-host', variable: 'POSTGRES_HOST'), string(credentialsId: 'authentik-postgres-port', variable: 'POSTGRES_PORT') ]) { sh ''' echo "๐Ÿ”ง Using Jenkins credentials for deployment..." export PG_DB="${PG_DB:-authentik}" export PG_USER="${PG_USER:-authentik}" export PG_PASS="${PG_PASS}" export AUTHENTIK_SECRET_KEY="${AUTHENTIK_SECRET_KEY}" export POSTGRES_HOST="${POSTGRES_HOST:-postgres-postgres}" export POSTGRES_PORT="${POSTGRES_PORT:-5432}" echo "๐Ÿ“‹ Deployment configuration:" echo " PostgreSQL Host: ${POSTGRES_HOST}" echo " PostgreSQL Port: ${POSTGRES_PORT}" echo " Database: ${PG_DB}" echo " User: ${PG_USER}" echo " Password: [REDACTED]" echo " Secret Key: [REDACTED]" # Stop existing services docker compose down || true # Pull latest images echo "๐Ÿ“ฅ Pulling latest images..." docker compose pull # Start Authentik server and worker echo "๐Ÿš€ Starting Authentik server and worker..." docker compose up -d server worker # Wait for services to be ready echo "โณ Waiting for Authentik services to start..." sleep 10 # Check if services are running echo "๐Ÿ” Checking service status..." docker compose ps ''' } echo "โœ… Authentik deployed successfully" } catch (Exception e) { echo "โŒ Deployment failed: ${e.getMessage()}" // Show logs for debugging sh ''' echo "๐Ÿณ Container logs:" docker compose logs --tail=50 || echo "Could not get container logs" echo "๐Ÿ“‹ System status:" docker compose ps || echo "Could not get container status" ''' throw e } } } } stage('Verify Deployment') { steps { script { echo "๐Ÿ” Verifying deployment..." withCredentials([ string(credentialsId: 'authentik-pg-pass', variable: 'PG_PASS'), string(credentialsId: 'authentik-pg-db', variable: 'PG_DB'), string(credentialsId: 'authentik-pg-user', variable: 'PG_USER'), string(credentialsId: 'authentik-postgres-host', variable: 'POSTGRES_HOST') ]) { sh ''' export PG_DB="${PG_DB:-authentik}" export PG_USER="${PG_USER:-authentik}" export POSTGRES_HOST="${POSTGRES_HOST:-postgres-postgres}" export PGPASSWORD="${PG_PASS}" echo "๐Ÿณ Container status:" docker compose ps || echo "โŒ Could not get container status" echo "๐Ÿ”— Testing database connection to external PostgreSQL..." docker exec -e PGPASSWORD="${PG_PASS}" ${POSTGRES_HOST} psql -h localhost -U ${PG_USER} -d ${PG_DB} -c "SELECT 1 as connection_test;" || echo "โš ๏ธ Database connection test failed" echo "๐Ÿ“Š Database statistics:" docker exec -e PGPASSWORD="${PG_PASS}" ${POSTGRES_HOST} psql -h localhost -U ${PG_USER} -d ${PG_DB} -c " SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'public' LIMIT 5; " || echo "โš ๏ธ Could not query database" echo "๐ŸŒ Checking Authentik server..." # Wait a bit for Authentik to fully start sleep 5 # Check if server container is healthy if docker compose ps server | grep -q "Up"; then echo "โœ… Authentik server is running" else echo "โš ๏ธ Authentik server status unclear" fi # Check if worker container is healthy if docker compose ps worker | grep -q "Up"; then echo "โœ… Authentik worker is running" else echo "โš ๏ธ Authentik worker status unclear" fi ''' } echo "โœ… Deployment verification completed" } } } stage('Create Backup') { steps { script { def timestamp = new Date().format('yyyyMMdd_HHmmss') def backupFile = "authentik_backup_${timestamp}.sql" echo "๐Ÿ’พ Creating deployment backup: ${backupFile}" withCredentials([ string(credentialsId: 'authentik-pg-pass', variable: 'PG_PASS'), string(credentialsId: 'authentik-pg-db', variable: 'PG_DB'), string(credentialsId: 'authentik-pg-user', variable: 'PG_USER'), string(credentialsId: 'authentik-postgres-host', variable: 'POSTGRES_HOST') ]) { sh """ export PG_DB="\${PG_DB:-authentik}" export PG_USER="\${PG_USER:-authentik}" export POSTGRES_HOST="\${POSTGRES_HOST:-postgres-postgres}" export PGPASSWORD="\${PG_PASS}" # Create backup directory mkdir -p jenkins_backups # Create backup from external PostgreSQL docker exec -e PGPASSWORD="\${PG_PASS}" \${POSTGRES_HOST} pg_dump \\ -h localhost \\ -U \${PG_USER} \\ -d \${PG_DB} \\ --format=custom \\ --compress=9 \\ --file=/tmp/${backupFile} # Copy backup from container docker cp \${POSTGRES_HOST}:/tmp/${backupFile} jenkins_backups/ # Create checksum cd jenkins_backups sha256sum ${backupFile} > ${backupFile}.sha256 echo "โœ… Backup created: ${backupFile}" ls -lh ${backupFile} """ } // Archive backup artifacts archiveArtifacts artifacts: 'jenkins_backups/*.sql,jenkins_backups/*.sha256', fingerprint: true, allowEmptyArchive: true } } } } post { always { script { echo "๐Ÿ“‹ Pipeline completed with status: ${currentBuild.currentResult}" // Generate pipeline summary def summary = """ ๐ŸŽฏ Authentik Deployment Pipeline ๐Ÿ“… Build: ${env.BUILD_NUMBER} ๐ŸŒฟ Branch: ${env.BRANCH_NAME} ๐Ÿท๏ธ Commit: ${env.GIT_COMMIT?.take(8)} โฑ๏ธ Duration: ${currentBuild.durationString} โœ… Status: ${currentBuild.currentResult} """ echo summary // Clean up old artifacts (keep last 5) sh 'find jenkins_backups -name "*.sql" -type f | sort -r | tail -n +6 | xargs rm -f || true' } } success { script { echo "๐ŸŽ‰ Authentik deployment successful!" echo "" echo "๐Ÿ“ Next steps:" echo " 1. Access Authentik: http://:9000/" echo " 2. Initial setup: http://:9000/if/flow/initial-setup/" echo " 3. Admin interface: http://:9000/if/admin/" echo "" echo "โš ๏ธ Remember to configure:" echo " - Admin account (initial setup)" echo " - Email settings (for notifications)" echo " - SSL/TLS (reverse proxy recommended)" echo " - Applications and providers" } } failure { script { echo "โŒ Authentik deployment failed!" // Show container logs for debugging sh ''' echo "๐Ÿณ Container logs:" docker compose logs --tail=100 || echo "Could not get container logs" echo "๐Ÿ“‹ System status:" docker compose ps || echo "Could not get container status" ''' } } unstable { script { echo "โš ๏ธ Authentik deployment completed with warnings!" } } cleanup { script { echo "๐Ÿงน Cleaning up workspace..." // Remove temporary files sh 'rm -f *.tmp || true' // Only tear down containers for non-main branches (explicit check) // Keep containers running on main/master for production if (env.BRANCH_NAME && env.BRANCH_NAME != 'main' && env.BRANCH_NAME != 'master') { echo "๐Ÿ—‘๏ธ Tearing down development containers for branch: ${env.BRANCH_NAME}" sh 'docker compose down || true' } else { echo "โœ… Keeping containers running for production (main branch)" } } } } }