#!/usr/bin/env groovy

@Library('apm@current') _

pipeline {
  agent any
  environment {
    REPO = 'metricbeat-tests-poc'
    BASE_DIR = "src/github.com/elastic/${env.REPO}"
    NOTIFY_TO = credentials('notify-to')
    JOB_GCS_BUCKET = credentials('gcs-bucket')
  }
  options {
    timeout(time: 1, unit: 'HOURS')
    buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30'))
    timestamps()
    ansiColor('xterm')
    disableResume()
    durabilityHint('PERFORMANCE_OPTIMIZED')
    rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true])
    quietPeriod(10)
  }
  triggers {
    issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*')
  }
  parameters {
    choice(name: 'LOG_LEVEL', choices: ['INFO', 'DEBUG'], description: 'Log level to be used')
    choice(name: 'QUERY_MAX_ATTEMPTS', choices: ['5', '10', '20'], description: 'Number of attempts to create the connection to Elasticsearch')
    choice(name: 'RETRY_TIMEOUT', choices: ['3', '5', '7', '11'], description: 'Number of seconds between retry')
    string(name: 'STACK_VERSION', defaultValue: '7.6.0', description: 'SemVer version of the stack to be used.')
    string(name: 'METRICBEAT_VERSION', defaultValue: '7.6.0', description: 'SemVer version of the metricbeat to be used.')
    string(name: 'HELM_CHART_VERSION', defaultValue: '7.6.1', description: 'SemVer version of Helm chart to be used.')
    string(name: 'HELM_VERSION', defaultValue: '2.16.3', description: 'SemVer version of Helm to be used.')
    string(name: 'KIND_VERSION', defaultValue: '0.7.0', description: 'SemVer version of Kind to be used.')
    string(name: 'KUBERNETES_VERSION', defaultValue: '1.15.3', description: 'SemVer version of Kubernetes to be used.')
    string(name: 'GO_VERSION', defaultValue: '1.13.4', description: "Go version to use.")
  }
  stages {
    stage('Initializing'){
      agent { label 'linux && immutable' }
      options { skipDefaultCheckout() }
      environment {
        HOME = "${env.WORKSPACE}"
        GOPATH = "${env.WORKSPACE}"
        GO_VERSION = "${params.GO_VERSION.trim()}"
        PATH = "${env.PATH}:${env.WORKSPACE}/bin:${env.WORKSPACE}/${env.BASE_DIR}/.ci/scripts"
        GO111MODULE = 'on'
        GOPROXY = 'https://proxy.golang.org'
        METRICBEAT_VERSION = "${params.METRICBEAT_VERSION.trim()}"
        STACK_VERSION = "${params.STACK_VERSION.trim()}"
        HELM_CHART_VERSION = "${params.HELM_CHART_VERSION.trim()}"
        HELM_VERSION = "${params.HELM_VERSION.trim()}"
        KIND_VERSION = "${params.KIND_VERSION.trim()}"
        KUBERNETES_VERSION = "${params.KUBERNETES_VERSION.trim()}"
        LOG_LEVEL = "${params.LOG_LEVEL.trim()}"
        QUERY_MAX_ATTEMPTS = "${params.QUERY_MAX_ATTEMPTS.trim()}"
        RETRY_TIMEOUT = "${params.RETRY_TIMEOUT.trim()}"
      }
      stages {
        stage('Checkout') {
          steps {
            gitCheckout(basedir: BASE_DIR, githubNotifyFirstTimeContributor: true)
            stash allowEmpty: true, name: 'source', useDefaultExcludes: false
          }
        }
        stage('Tests') {
          parallel {
            stage('Sanity checks') {
              agent { label 'linux && immutable' }
              environment {
                GOROOT = "${env.WORKSPACE}/.gimme/versions/go${env.GO_VERSION}.linux.amd64"
                PATH = "${env.WORKSPACE}/${env.BASE_DIR}/bin:${env.PATH}:${env.GOROOT}/bin"
              }
              options { skipDefaultCheckout() }
              steps {
                withGithubNotify(context: 'Sanity checks', tab: 'tests') {
                  deleteDir()
                  unstash 'source'
                  dir(BASE_DIR){
                    sh script: """.ci/scripts/install-dependencies.sh "${GO_VERSION}" """, label: 'Install dependencies'
                    preCommit(commit: "${GIT_BASE_COMMIT}", junit: true)
                  }
                }
              }
            }
            stage('Unit Tests') {
              agent { label 'linux && immutable' }
              options { skipDefaultCheckout() }
              steps {
                withGithubNotify(context: 'Tests', tab: 'tests') {
                  deleteDir()
                  unstash 'source'
                  dir(BASE_DIR){
                    sh script: """.ci/scripts/build-test.sh "${GO_VERSION}" """, label: 'Build and test'
                  }
                  stash allowEmpty: false, name: 'build', useDefaultExcludes: false, excludes: '.gimme/**'
                }
              }
              post {
                always {
                  junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/outputs/TEST-unit.xml")
                }
              }
            }
          }
        }
        stage('End-To-End Tests') {
          options { skipDefaultCheckout() }
          environment {
            GO111MODULE = 'on'
            PATH = "${env.HOME}/bin:${env.WORKSPACE}/${env.BASE_DIR}/bin:${env.GOPATH}/bin:${HOME}/go/bin:${env.PATH}"
            GOPROXY = 'https://proxy.golang.org'
          }
          steps {
            withGithubNotify(context: 'E2E Tests', tab: 'tests') {
              deleteDir()
              unstash 'source'
              dir("${BASE_DIR}") {
                script {
                  def features = readYaml(file: '.ci/.e2e-tests.yaml')
                  def parallelTasks = [:]
                  features['FEATURES'].each { feature ->
                      parallelTasks["${feature}"] = generateFunctionalTestStep(feature: "${feature}")
                  }
                  parallel(parallelTasks)
                }
              }
            }
          }
        }
        stage('Release') {
          options { skipDefaultCheckout() }
          when { tag "v*" }
          steps {
            deleteDir()
            unstash 'source'
            dir("${BASE_DIR}") {
              script {
                def matrix = readYaml(file: '.ci/.package.yaml')
                def parallelTasks = [:]
                matrix['OSS'].each { oss ->
                  matrix['PLATFORM'].each { platform ->
                    parallelTasks["${oss}-${platform}"] = generateStep(oss: "${oss}", platform: "${platform}")
                  }
                }
                parallel(parallelTasks)
              }
            }
          }
        }
      }
    }
  }
  post {
    cleanup {
      notifyBuildResult(to: ['victor.martinez@elastic.co', 'manuel.delapena@elastic.co'])
    }
  }
}

def generateStep(Map params = [:]){
  def oss = params.get('oss')
  def platform = params.get('platform')
  return {
    node('linux && immutable') {
      try {
        deleteDir()
        unstash 'build'
        dir("${BASE_DIR}/cli") {
          withEnv(["GOOS=${oss}", "GOARCH=${platform}"]) {
            sh script: 'make build', label: 'Create releases'
          }
        }
      } catch(e) {
        error(e.toString())
      } finally {
        archiveArtifacts allowEmptyArchive: true, artifacts: "${BASE_DIR}/cli/.github/releases/download/**"
      }
    }
  }
}

def generateFunctionalTestStep(Map params = [:]){
  def feature = params.get('feature')
  return {
    node('linux && immutable') {
      try {
        deleteDir()
        unstash 'build'
        retry(3){
          dir("${BASE_DIR}"){
            sh script: """.ci/scripts/install-test-dependencies.sh "${GO_VERSION}" """, label: "Install test dependencies for ${feature}"
          }
        }
        dir("${BASE_DIR}"){
          sh script: """.ci/scripts/functional-test.sh "${GO_VERSION}" "${feature}" "${STACK_VERSION}" "${METRICBEAT_VERSION}" """, label: "Run functional tests for ${feature}"
        }
      } catch(e) {
        error(e.toString())
      } finally {
        junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/outputs/TEST-${feature}.xml")
          archiveArtifacts allowEmptyArchive: true, artifacts: "${BASE_DIR}/outputs/TEST-${feature}.xml"
      }
    }
  }
}
