@Library('nf-jenkins-shared-libraries')_  
import jenkins.model.Jenkins
import org.apache.commons.lang3.StringUtils;
import hudson.model.*

repo = "admin-gui"
deployableCommitHash = ""
error_msg = null
startedUserId = ""
channel = "cd-console-staging"
failure = false
def cfinvalidation
def CF_DISTRIBUTION =  [ 'sandbox': 'E2XXWR5XCP9VOX', 'staging': 'ESJT33GN4NMB1', 'production': 'E2PDVWYFH4TD0H' ]
authorized_users = ["nick.pieros@netfoundry.io", "loren.fouts@netfoundry.io", "vinay.lakshmaiah@netfoundry.io", "albert.mugisha@netfoundry.io", "mike.guthrie@netfoundry.io"]
SlackEmail = ['mike.guthrie@netfoundry.io': 'U6U329WQZ', 'nick.pieros@netfoundry.io': 'U9P6CEVT7', 'loren.fouts@netfoundry.io': 'UPAND2S9W', 'albert.mugisha@netfoundry.io': 'UCG7GLZ5W', 'vinay.lakshmaiah@netfoundry.io': 'U6ZG28L69']
SlackNames = ['Mike Guthrie': 'U6U329WQZ', 'Nick Pieros': 'U9P6CEVT7', 'Loren Fouts': 'UPAND2S9W', 'Albert Mugisha': 'UCG7GLZ5W', 'Vinay Lakshmaiah': 'U6ZG28L69']


pipeline {
    agent {
        dockerfile { 
            filename 'Dockerfile'
            args '-u 0 --privileged'
        }
    }
    
    environment {
        DOCKER_HUB = credentials('dockerhub')
        BITBUCKET_AUTH = credentials('bitbucket_auth')
        BITBUCKET_ADMIN = credentials('bitbucket_admin')
    }

    parameters {
        choice(name:'environment', choices: "staging\nproduction", description: 'Environment to Rollback')
    }

    stages {
        stage('Authorization') {
            steps {
                script {
                    //check authorized user
                    try {
                        startedUserId = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
                        if(! authorized_users.contains(startedUserId)){
                            error_msg = "User ${startedUserId} is not authorized to run this pipeline"
                            error "${error_msg}"
                        }
                    } catch(e) {
                        failure = true
                        error_msg =  "User ${startedUserId} is not authorized to run this pipeline"
                        error "${error_msg}"
                    }
                }
            }
        }
        
        stage('Confirmation') {
            when { expression { failure == false }}
            steps {
                script {
                    //Read Rollbacks and ask for confirmation
                    sh "mkdir -p ${repo}-${BUILD_ID}"
                    sh "chown -R 1000:1000 ${repo}-${BUILD_ID}"
                    dir("${repo}-${BUILD_ID}"){
                        git branch: "develop", credentialsId: 'afd18d9b-3d41-4b59-b5cc-05cd7df2ce63', url: "git@bitbucket.org:netfoundry/${repo}.git"
                        def git_tags_raw = sh ( script: " git log --tags --no-walk --pretty='format:%d' | sed 10q | sed 's/[()]//g' | sed 's/,[^,]*\$//' | sed  's ......  ' | sed 's/[, ]/\\n/g' | sed 's/tag://g' | grep -i 'release-*' | jq -R -c -s 'split(\"\\n\")' | jq 'map(select(length > 0))'", returnStdout: true ).trim()
                        git_tags = new groovy.json.JsonSlurperClassic().parseText(git_tags_raw)
                        if(git_tags[-1] == "") {
                            git_tags.minus(git_tags[-1])
                        }
                        def INPUT_PARAMS = input message: "Rollback ${repo} for ${params.environment}. Please select rollback tag", ok: 'Next',
                            parameters: [
                                choice(name: 'rollbackTag', choices: git_tags, description: "Select RollBack Tag. Date is based on when last deployment was done"),
                                booleanParam(name: 'proceed', defaultValue: false, description: 'Proceed With Rollback')
                            ], submitterParameter: 'approvedUser'
                        if( INPUT_PARAMS['proceed'] == false) {
                            error "User selected not to proceed, pipeline closing"
                            failure = true
                        } else {
                            //Proceeding, Get commit hash
                            deployableCommitHash = INPUT_PARAMS['rollbackTag'].split("-")[1]
					        echo "${params.environment} will be rolled back to Commit: ${deployableCommitHash}"
                        }
                    }
                    
                    sh "rm -rf ${repo}-${BUILD_ID}"
                    currentBuild.description = "Action: Rollback, Repo:- ${repo}, Environment:- ${params.environment}. Commit Hash: ${deployableCommitHash}"
                }
            }
        }

        stage('Git Checkout'){
            when { expression { failure == false }}
            steps {
                script {
                    try {
                        gitClone( deployableCommitHash, repo )
                    } catch(e){
                        failure = true
                        error_msg = "Git Clone failed" 
                        error "${error_msg}"
                    }
                }
            }
        }

        stage('Build') {
            when { expression { failure == false }}
            steps {
                script {
                    dir("${repo}"){
                        try {
                            sh "npm install"
                            sh "node version.js"
                            sh "npm run lint"
                            sh "npm run test"
                            sh "npm run build-aot"

                            archiveArtifacts "node_modules/sonar-scanner/**"
                        } catch(e){
                            failure = true
                            error_msg = "Build Failed for branch ${deployableCommitHash}"
                            error "${error_msg}"
                        }
                    }
                }
            }
        }

        stage('Rollback Staging') {
            when { expression { failure == false && params.environment == "staging" }}
            steps {
                script {
                    dir("${repo}"){
                        try{
                            currentBuild.description = "Rolling back - Environment: ${params.environment} REPO: ${repo} to Hash: ${deployableCommitHash}"

                            sh "npm install"
                            sh "node version.js"
                            sh "node --max_old_space_size=8000 ./node_modules/.bin/ng build --configuration=staging --prod --aot"
                    
                            archiveArtifacts "dist/**"
                        } catch(e){
                            failure = true
                            error_msg = "Build Failed for branch ${deployableCommitHash} during rollback to Staging"
                            error "${error_msg}"
                        }
                        try{
                            def account_id = nfAWSAccounts account_name: "${params.environment}"
                            withAWS(credentials: 'jenkins-central-mgt', region: 'us-east-1', role:'jenkins-central-mgt',  roleAccount: "${account_id}") {
                                sh "aws s3 sync dist s3://nf-admin-gui-staging --only-show-errors"

                                //Invalidate CloudFront Cache
                                cfinvalidation = sh (script: "aws cloudfront create-invalidation --distribution-id ${CF_DISTRIBUTION[params.environment]} --paths '/*' --query 'Invalidation.Id' --output text", returnStdout: true )
                                sh "aws cloudfront wait invalidation-completed --distribution-id ${CF_DISTRIBUTION[params.environment]} --id ${cfinvalidation}"

                                // Record SSM
                                sh "aws ssm put-parameter --name /${params.environment}/container-image/${repo} --type String --value ${deployableCommitHash} --region us-east-1 --overwrite"
                            }
                        } catch(e){
                            failure = true
                            error_msg = "Deployment/SSM Record to Staging failed during rollback"
                            error "${error_msg}"
                        }
                    }
                }
            }
        }

        stage('Rollback Production') {
            when { expression { failure == false && params.environment == "production" }}
            steps {
                script {
                    dir("${repo}"){
                        try {
                            currentBuild.description = "Rolling back - Environment: ${params.environment} REPO: ${repo} to Hash: ${deployableCommitHash}"
                            
                            sh "npm install"
                            sh "node version.js"
                            sh "npm run build-prod"

                            archiveArtifacts "dist/**"
                        } catch(e){
                            failure = true
                            error_msg = "Build Failed for branch ${deployableCommitHash} during rollback to Production"
                            error "${error_msg}"
                        }

                        try{
                            def account_id = nfAWSAccounts account_name: "${params.environment}"
                            withAWS(credentials: 'jenkins-central-mgt', region: 'us-east-1', role:'jenkins-central-mgt',  roleAccount: "${account_id}") {
                                sh "aws s3 sync dist s3://nf-admin-gui-production --only-show-errors"

                                //Invalidate CloudFront Cache
                                cfinvalidation = sh (script: "aws cloudfront create-invalidation --distribution-id ${CF_DISTRIBUTION[params.environment]} --paths '/*' --query 'Invalidation.Id' --output text", returnStdout: true )
                                sh "aws cloudfront wait invalidation-completed --distribution-id ${CF_DISTRIBUTION[params.environment]} --id ${cfinvalidation}"

                                // Record SSM
                                sh "aws ssm put-parameter --name /${params.environment}/container-image/${repo} --type String --value ${deployableCommitHash} --region us-east-1 --overwrite"
                            }
                        } catch(e){
                            failure = true
                            error_msg = "Deployment/SSM Record to Production failed during rollback"
                            error "${error_msg}"
                        }
                    }
                }
            }
        }
    }

    post {
        always{
            script{
                sh "rm -rf ${repo}-${BUILD_ID}"
                sh "rm -rf ${repo}"
            } 
        }
        success {
            script {
                slack_notification(startedUserId, deployableCommitHash, params.environment)
            }
        }
	    failure {
            script {
                slack_notification(startedUserId, deployableCommitHash, params.environment)
            }
        }
        aborted {
            script{
                echo "Pipeline Aborted"
            }
        }
    }
}

def gitClone(commit_hash, repo ){
    sh "mkdir -p ${repo}"
    sh "chown -R 1000:1000 ${repo}"
    dir("${repo}"){
        checkout ( [$class: 'GitSCM',
            branches: [[name: "${commit_hash}" ]],
            userRemoteConfigs: [[
                credentialsId: 'afd18d9b-3d41-4b59-b5cc-05cd7df2ce63', 
                url: "git@bitbucket.org:netfoundry/${repo}.git"
        ]]])
    }
}

def slack_notification(userid, commit_hash, environment) {
    def color = "danger"
    def username = "${SlackEmail[userid]}"

    if( environment == "production" ){
        channel = "cd-console-production"
    }

    if( username == "null" || username == null ) { username = "" } else { username = "<@${username}>"}

    if( currentBuild.currentResult == "SUCCESS" ) {
        def fn_msg = "Rollback ${environment} was successful."
        slack_msg = "Attn: ${username}\n"+
                    "*Repo:* ${repo}\n" +
                    "*Commit Hash:* ${commit_hash}\n" +
                    "*Result:* ${fn_msg}\n" +
                    "*BuildURL:* ${RUN_DISPLAY_URL}\n"
        color = "good"
    } else {
	    slack_msg = "Attn: ${username}\n"+
                    "*Repo:* ${repo}\n" +
                    "*Commit Hash:* ${commit_hash}\n" +
                    "*Result:* Rollback ${environment} failed. Please check link for details\n"+
                    "*BuildURL:* ${RUN_DISPLAY_URL}\n"+
                    "*Failure Reason:* ${error_msg}\n"
    }
	slackSend channel: "${channel}",
              color: "${color}",
              message: slack_msg

}