Irregular behavior of sendSlack with post on Jenkins - Is this a bug?

Viewed 23

I am trying to create a Jenkins pipeline wherein a message is sent in Slack thread on every pass/fail after every stage (I have used nested stages and with parallel) but the pipeline shows irregular behavior.

I have deliberately put wrong command in Stage11

Snippet 1 - This code snippet has sleep in both stages. This code behaves as expected.

pipeline {
    agent any

    stages {
        stage('Start') {
            steps {
                script {
                    slackResponse = slackSendMessage('STARTED')
                }
            }
        }
        
        stage("parallel1") {
            parallel {
                stage('Stage11') {
                    steps {
                        sh 'sleep 5'
                        sh 'data'
                        
                    }
                    
                    post {
                        success {
                            slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                        }
                        failure {
                            slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                        }
                    }
                }
                
                stage('Stage12') {
                    steps {
                        sh 'sleep 10'
                        sh 'date'
                    }
                    
                    post {
                        success {
                            slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                        }
                        failure {
                            slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                        }
                    }
                }
            }
            
            post {
                success {
                    slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                }
                failure {
                    slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                }   
            }
        }
    }
    
    post {
        always {
            slackSendMessage(currentBuild.result, slackResponse, '', true)
        }
    }
}

def slackSendMessage(buildStatus = 'STARTED', slackResponse = null, currentStage = '', threadMessage = false) {
    // build status of null means successful
    buildStatus =  buildStatus ?: 'SUCCESSFUL'
    
    // used to tag on Slack
    def userEmailId = ''
    wrap([$class: 'BuildUser']) {
        userEmailId = env.BUILD_USER_EMAIL
    }
    def userId = slackUserIdFromEmail(userEmailId)
    buildTriggerBy = "<@$userId>"
    
    def message = ''
    if (currentStage.length() > 0) {
        if (buildStatus == "FAILED") {
            message = "${buildStatus}: Stage '${currentStage}' (${env.BUILD_URL}console)"
        }
        else {
            message = "${buildStatus}: Stage '${env.STAGE_NAME}'"
        }
    }
    else {
        message = "${buildStatus}: Job '${env.JOB_NAME}' [${env.BUILD_NUMBER}] By ${buildTriggerBy}'\n(${env.BUILD_URL})"
    }
    
    // Send notifications
    if (threadMessage == true) {
        slackSend(channel: slackResponse.threadId, message: message)
    }
    else {
        // slackResponse will be used to send message to same thread.
        def getSlackResponse = slackSend(message: message)
        return(getSlackResponse)
    }
}

Output 1 - Image This works as expected behavior, Stage11 fails, Stage 12 passed and overall failure.

Snippet 1 - This code snippet does not have sleep. This code behaves differently.

pipeline {
    agent any

    stages {
        stage('Start') {
            steps {
                script {
                    slackResponse = slackSendMessage('STARTED')
                }
            }
        }
        
        stage("parallel1") {
            parallel {
                stage('Stage11') {
                    steps {
                        // sh 'sleep 5'
                        sh 'data'
                        
                    }
                    
                    post {
                        success {
                            slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                        }
                        failure {
                            slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                        }
                    }
                }
                
                stage('Stage12') {
                    steps {
                        // sh 'sleep 10'
                        sh 'date'
                    }
                    
                    post {
                        success {
                            slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                        }
                        failure {
                            slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                        }
                    }
                }
            }
            
            post {
                success {
                    slackSendMessage('SUCCESSFUL', slackResponse, env.STAGE_NAME, true)
                }
                failure {
                    slackSendMessage('FAILED', slackResponse, env.STAGE_NAME, true)
                }   
            }
        }
    }
    
    post {
        always {
            slackSendMessage(currentBuild.result, slackResponse, '', true)
        }
    }
}

def slackSendMessage(buildStatus = 'STARTED', slackResponse = null, currentStage = '', threadMessage = false) {
    // build status of null means successful
    buildStatus =  buildStatus ?: 'SUCCESSFUL'
    
    // used to tag on Slack
    def userEmailId = ''
    wrap([$class: 'BuildUser']) {
        userEmailId = env.BUILD_USER_EMAIL
    }
    def userId = slackUserIdFromEmail(userEmailId)
    buildTriggerBy = "<@$userId>"
    
    def message = ''
    if (currentStage.length() > 0) {
        if (buildStatus == "FAILED") {
            message = "${buildStatus}: Stage '${currentStage}' (${env.BUILD_URL}console)"
        }
        else {
            message = "${buildStatus}: Stage '${env.STAGE_NAME}'"
        }
    }
    else {
        message = "${buildStatus}: Job '${env.JOB_NAME}' [${env.BUILD_NUMBER}] By ${buildTriggerBy}'\n(${env.BUILD_URL})"
    }
    
    // Send notifications
    if (threadMessage == true) {
        slackSend(channel: slackResponse.threadId, message: message)
    }
    else {
        // slackResponse will be used to send message to same thread.
        def getSlackResponse = slackSend(message: message)
        return(getSlackResponse)
    }
}

Output 2 - Image This does not work as expected. If you see the output Stage12 passed but since Stage11 failed so a repeated slack message was sent saying Stage12 failed which should not have come.

Operating System - Amazon AMI (ami-090fa75af13c156b4)
Jenkins - 2.346.2
Slack Plugin - 616.v03b_1e98d13dd

0 Answers
Related