#!groovy

@Library('infraLib') _

def getAWSProfile(branch) {
  return branch == "production" ? "gdb-mct-prod" : "gdb-mct-dev"
}

def dynamicEnvironments = ['test1', 'test2', 'test3', 'test4', 'test5']
def envChoices = ['default'].plus(dynamicEnvironments)

def getEnvironmentNameFromLabel(dynamicEnvironments) {
  label = "None"
  if (env.CHANGE_ID) {
    pullRequest.labels.find{
      if (dynamicEnvironments.contains(it)) {
        label = it
      }
    }
  }
  return label
}

def getOverriddenDockerImageTagFromLabels(override, dynamicEnvironments) {
  label = getEnvironmentNameFromLabel(dynamicEnvironments) != "None" ? getEnvironmentNameFromLabel(dynamicEnvironments) : override
  return label
}

def getVersion(override, dynamicEnvironments) {
  version = getOverriddenDockerImageTagFromLabels(override, dynamicEnvironments)
  return !version.equals('default') ? version : octopus.getVersion(env.BRANCH_NAME,env.WORKSPACE)
}

def setOctopusChannel(override, environment) {
  if (!override.equals('default') || !environment.equals('None')) {
    return 'dynamic'
  } else {
    return octopus.setOctoChannel(env.BRANCH_NAME)
  }
}

def setDeploymentEnv(override, dynamicEnvironments) {
  environment = getOverriddenDockerImageTagFromLabels(override, dynamicEnvironments)
  return !environment.equals('default') ? "${environment} decibel" : octopus.setDeploymentEnv(params.DEPLOY_ENV_CHOICE, env.PROJECT_GROUP, env.BRANCH_NAME)
}

def getParamValueByBranch(branch) {
  if (env.CHANGE_ID) return false
  switch(branch) {
    case "develop":
    case "media_plan_master":
      return false
    default:
      return true
  }
}

def getPullRequestLabels() {
  if (!env.CHANGE_ID) return []

  return pullRequest.labels
}

pipeline {
  agent {
    label 'linux-agents-xlarge'
  }

  options {
    ansiColor('xterm')
    disableConcurrentBuilds()
    disableResume()
    timeout(60)
    timestamps()
    buildDiscarder logRotator(artifactDaysToKeepStr: '',
                              artifactNumToKeepStr: '',
                              daysToKeepStr: '',
                              numToKeepStr: '15')
  }

  parameters {
    separator(name: 'separator-decibel-api-deployment', sectionHeader: 'Deployment parameters')
    choice choices: [
             'default',
             'dev',
             'dev2',
             'qa',
             'prod',
             'skip_deployment',
             'test1',
             'test2',
             'test3',
             'test4',
             'test5'
           ],
           description: 'Set environment for deployment. Development by default',
           name: 'DEPLOY_ENV_CHOICE'
    choice choices: [
             'default',
             'yes',
             'no'
           ],
           description: 'By default deploy is only triggered for develop/release/master branch',
           name: 'TRIGGER_DEPLOY'
    choice choices: [
             'error',
             'overwrite',
             'skip_build'
           ],
           description: "Docker image upload mode - ovewrite if the image with such tag already exists, raise error if exists, or skip build part if exists.",
           name: 'IMAGE_UPLOAD_MODE'
    choice choices: [
             'no',
             'yes'
           ],
           description: "Don't raise errors in case release already exists",
           name: 'SKIP_RELEASE_CREATION'
    separator(name: 'separator-decibel-api-checks', sectionHeader: 'Skip static checks')
    booleanParam defaultValue: getParamValueByBranch(env.BRANCH_NAME),
                 description: 'Skip checkstyle',
                 name: 'SKIP_LINT'
    booleanParam defaultValue: getParamValueByBranch(env.BRANCH_NAME),
                 description: 'Skip unit tests',
                 name: 'SKIP_TESTS'
    separator(name: 'separator-decibel-api-checks', sectionHeader: 'Dynamic environments')
    choice choices: envChoices,
           description: 'Override docker image tag',
           name: 'OVERRIDDEN_DOCKER_IMAGE_TAG'
    booleanParam defaultValue: false,
                 description: 'Skip unit tests',
                 name: 'REMOVE_LABEL'
  }

  environment {
    PROJECT_GROUP = 'marketing'
    PROJECT       = 'api'

    AWS_PROFILE    = 'gdb-mct-dev'
    AWS_ACCOUNT_ID = '925412343888'
    AWS_REGION     = 'us-east-1'

    ECR_REGISTRY        = "${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com"
    ECR_REPOSITORY_NAME = "${env.PROJECT_GROUP}/${env.PROJECT}"
    ECR_REGISTRY_URI    = "${env.ECR_REGISTRY}/${env.ECR_REPOSITORY_NAME}"

    OCTOPUS_CLI_SERVER  = 'https://octopus.delphi.zone'
    OCTOPUS_CLI_API_KEY = utility.getSecretValueById('infra/jenkins/octopus_api_key', 'gdb-delphi-dev')
    DEPLOY_ENV          = "${!env.CHANGE_ID ? octopus.setDeploymentEnv(params.DEPLOY_ENV_CHOICE, 'decibel', env.BRANCH_NAME) : setDeploymentEnv(params.OVERRIDDEN_DOCKER_IMAGE_TAG, dynamicEnvironments)}"
    OCTOPUS_CHANNEL     = "${env.DEPLOY_ENV.startsWith('test') ? setOctopusChannel(params.OVERRIDDEN_DOCKER_IMAGE_TAG, env.ENV_LABEL) : octopus.setOctoChannel(env.BRANCH_NAME)}"
    OCTOPUS_PROJECT     = "decibel.decibel-api"
    VERSION             = octopus.getVersion(env.BRANCH_NAME,env.WORKSPACE)
    ENV_LABEL           = getEnvironmentNameFromLabel(dynamicEnvironments)

    NOTIFICATIONS_CHANNEL = '#mct-deployments'

    PYPI_INDEX_URL = 'https://artifacts.apollo.stream/repository/pypi-all/simple'

    REDIS_IMAGE         = '483193324480.dkr.ecr.us-east-1.amazonaws.com/redis:3.2.10'
    POSTGRES_IMAGE      = '483193324480.dkr.ecr.us-east-1.amazonaws.com/postgres:12.10-alpine3.15'
    ELASTICSEARCH_IMAGE = '483193324480.dkr.ecr.us-east-1.amazonaws.com/elasticsearch:7.10.2'
  }

  triggers {
    issueCommentTrigger('.*build me.*')
  }

  stages {
    stage('info') {
      steps {
        script {
          currentBuild.description = """
            <b>deployment</b>
            <ul>
              <li>OCTOPUS_CHANNEL: ${env.OCTOPUS_CHANNEL}</li>
              <li>DEPLOY_ENV: ${env.DEPLOY_ENV}</li>
              <li>ENV_LABEL: ${ENV_LABEL}</li>
            </ul>
          """
        }
      }
    }
    stage('Add Label') {
      when {
        allOf {
          changeRequest()
          not { expression { return params.OVERRIDDEN_DOCKER_IMAGE_TAG.equals('default') } }
          not { expression { return params.REMOVE_LABEL } }
        } /** end: allOf */
      } /** end: when */
      steps {
        script {
          pullRequest.addLabel("${params.OVERRIDDEN_DOCKER_IMAGE_TAG}")
          pullRequest.addLabel(env.BRANCH_NAME)
        } /** end: script */
      } /** end: steps */
    } /** end: stage */
    stage('Remove Label') {
      when {
        allOf {
          changeRequest()
          not { expression { return params.OVERRIDDEN_DOCKER_IMAGE_TAG.equals('default') } }
          expression { return params.REMOVE_LABEL }
        } /** end: allOf */
      } /** end: when */
      steps {
        script {
          pullRequest.removeLabel("${params.OVERRIDDEN_DOCKER_IMAGE_TAG}")
          pullRequest.removeLabel(env.BRANCH_NAME)
        } /** end: script */
      } /** end: steps */
    } /** end: stage */
    stage('Build') {
      when {
        not { expression { params.IMAGE_UPLOAD_MODE.equals('skip_build')  } }
      }
      options {
        retry(2)
      }
      steps {
        script { currentBuild.displayName = "${env.VERSION}-${env.OCTOPUS_CHANNEL}" }
        sh "touch .env; mkdir -p test-reports/"
        sh label: 'build', script: '''
            make docker/login/base
            make docker/image/build
        '''
        script {
          imageSHA = sh returnStdout: true, script: "docker image inspect ${env.ECR_REGISTRY_URI}:${env.VERSION} | jq -r .[].Id"
          currentBuild.description = currentBuild.description + "<p><b>image sha:</b> ${imageSHA}</p>"
        }
      } /** end: steps */
    } /** end: stage */

    stage('Test') {
      options { retry(2) }
      when {
        allOf {
          not { expression { params.SKIP_TESTS } }
          not { expression { env.VERSION.startsWith('test') } }
          not { expression { params.IMAGE_UPLOAD_MODE.equals('skip_build')  } }
        }
      }
      steps {
        sh label: 'test', script: '''
          make docker/test
        '''
      } /** end: steps */
      post {
        always {
          sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
          junit allowEmptyResults: false, testResults: "**/test-reports/*.xml"
          cobertura autoUpdateHealth: false,
            autoUpdateStability: false,
            coberturaReportFile: 'coverage.xml',
            failUnhealthy: false,
            failUnstable: false,
            maxNumberOfBuilds: 0,
            onlyStable: false,
            zoomCoverageChart: false
          sh 'make docker/clean'
        }
      }
    } /** end: stage */

    stage('Static') {
      when {
        allOf {
          not { expression { params.SKIP_LINT } }
          not { expression { env.VERSION.startsWith('test') } }
          not { expression { params.IMAGE_UPLOAD_MODE.equals('skip_build')  } }
        }
      }
      parallel {
        stage('Checkstyle') {
          steps {
            sh label: 'checkstyle', script: '''
              make docker/checkstyle
            '''
          } /** end: steps */
          post {
            always {
              sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
            }
          }
        } /** end: stage */
        stage('Security') {
          steps {
            sh label: 'check dependencies', script: '''
              make docker/security
            '''
          } /** end: steps */
          post {
            always {
              sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
            }
          }
        } /** end: stage */
      } /** end: parallel */
    } /** end: stage */
    stage('SonarQube Analysis') {
      when {
        allOf {
          branch 'develop' // there is a limit to a single branch in the sonarqube community edition
        }
      }
      environment {
        SONAR_HOST_URL         = "https://sonar.delphiplatform.io"
        SONAR_PROJECT_BASE_DIR = "${env.WORKSPACE}"
        SONAR_TOKEN            = utility.getSecretValueById('infra/jenkins/common/sonar_login_token', 'gdb-infra-dev')
      }
      steps {
        script {
          docker.image("sonarsource/sonar-scanner-cli").inside {
            sh script: """
set +x
sonar-scanner \
  -Dsonar.projectKey=decibel.api \
  -Dsonar.projectBaseDir=${env.SONAR_PROJECT_BASE_DIR} \
  -Dsonar.host.url=${env.SONAR_HOST_URL} \
  -Dsonar.login=${env.SONAR_TOKEN} \
  -Dsonar.projectVersion=${env.VERSION}
"""
            }
          }
        } /** end: steps */
    } /** end: stage */

    stage('Deploy') {
      when {
        anyOf {
          branch 'master'
          branch 'develop'
          branch 'qa'
          branch 'release'
          branch 'production'
          branch 'media_plan_master'
          expression { return params.TRIGGER_DEPLOY.equals('yes') }
          allOf {
            expression { return env.OCTOPUS_CHANNEL.equals('dynamic') }
            expression { return getPullRequestLabels().contains(env.BRANCH_NAME) }
          }
        }
      }
      stages {
        stage('Check version') {
          when { expression { return params.IMAGE_UPLOAD_MODE == 'error' && octopus.ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY_NAME,env.AWS_PROFILE) }}
          steps { error "Image with ${env.VERSION} version tag already exists. To ignore this error set IMAGE_UPLOAD_MODE parameter to overwrite or skip_build"}
        } /** end: stage */
        stage('Push to ECR') {
          when {
            expression { return params.IMAGE_UPLOAD_MODE == 'overwrite' || !octopus.ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY_NAME,env.AWS_PROFILE) }
          }
          steps {
            sh label: 'push', script: '''
              make docker/login
              make docker/image/push
            '''
          } /** end: steps */
        } /** end: stage */
        stage('Upload documentation') {
          steps {
            script {
              documentation.upload(env.BRANCH_NAME, env.WORKSPACE)
            } /** end: script */
          } /** end: steps */
        } /** end: stage */
        stage('Create a release in octopus') {
          //Check if release already exists. If it does, raise error or skip it, based on given param
          when {
            allOf {
              expression { return params.SKIP_RELEASE_CREATION.equals('no') || !octopus.releaseExists(env.OCTOPUS_PROJECT,env.VERSION) }
            }
          }
          steps {
            echo octopus.createRelease(env.OCTOPUS_PROJECT,env.VERSION,env.OCTOPUS_CHANNEL)
          } /** end: steps */
        } /** end: stage */
        stage('Deploy release in octopus') {
          when {
            allOf {
              not { expression { return env.DEPLOY_ENV.equals('skip_deployment') } }
            }
          }
          steps {
            echo octopus.deployRelease(env.OCTOPUS_PROJECT,env.DEPLOY_ENV,env.VERSION,env.OCTOPUS_CHANNEL)
          } /** end: steps */
        } /** end: stage */
      } /** end: stages */
    } /** end: stage */
  } /** end: stages */
  post {
    always {
      sh "sudo chown \$(whoami):\$(whoami) -R ${env.WORKSPACE}"
      sh label: 'cleanup', script: 'make docker/clean'
      script {
        utility.slackNotify(currentBuild.currentResult, env.NOTIFICATIONS_CHANNEL)
      }
    }
    cleanup {
      deleteDir()
    }
  } /** end: post */
}
