github.com/verrazzano/verrazzano@v1.7.0/ci/JenkinsfileBackendTests (about)

     1  // Copyright (c) 2021, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  import groovy.transform.Field
     5  
     6  @Field
     7  def GIT_COMMIT_TO_USE = ""
     8  @Field
     9  def LAST_CLEAN_BACKEND_COMMIT = ""
    10  @Field
    11  def TESTS_FAILED = false
    12  @Field
    13  def storeLocation=""
    14  @Field
    15  def verrazzanoPrefix="verrazzano-"
    16  @Field
    17  def fullBundle=""
    18  @Field
    19  def liteBundle=""
    20  @Field
    21  def SUSPECT_LIST = ""
    22  @Field
    23  def COMPARISON_URL_ON_FAILURE = ""
    24  
    25  @Field
    26  def backendTestsUpToDate              = false // If true, indicates that the backend tests already passed at the latest commit
    27  @Field
    28  def backendTestsUpToDateFailed        = false // If true, indicates that the backend tests already ran and failed at the latest commit
    29  
    30  // Non Fields
    31  // def branchSpecificSchedule = getCronSchedule()
    32  
    33  // File containing the links to download the Verrazzano distributions
    34  @Field
    35  def verrazzanoDistributionsFile = "verrazzano_distributions.html"
    36  
    37  def ociOsRegion = "us-phoenix-1"
    38  def ociOsBucket = "verrazzano-builds"
    39  def urlTriggerBranchName = env.BRANCH_NAME.replace('/', '%2F')
    40  def lastStableCommitFile = "last-stable-commit.txt"
    41  
    42  pipeline {
    43      options {
    44          timeout(time: 12, unit: 'HOURS')
    45          skipDefaultCheckout true
    46          disableConcurrentBuilds()
    47          timestamps ()
    48      }
    49  
    50      agent {
    51         docker {
    52              image "${RUNNER_DOCKER_IMAGE}"
    53              args "${RUNNER_DOCKER_ARGS}"
    54              registryUrl "${RUNNER_DOCKER_REGISTRY_URL}"
    55              registryCredentialsId 'ocir-pull-and-push-account'
    56              label "pipeline-job-large"
    57          }
    58      }
    59  
    60      //  triggers {
    61      //      cron(branchSpecificSchedule)
    62  
    63      // There is an issue with URLTrigger for backend tests where the polling URL maintains a global state that does not work with specific jobs.
    64      // Therefore, the below code is disabled until the global state issue is fixed.
    65      // Ex: - Each job has an URL Trigger poll cron schedule
    66      //      - When a job’s cron triggers, it checks the state of whether the specific URL X has changed or not.
    67      //      - If it has changed, then it triggers only the job whose cron schedule fired off (no other jobs that look for that URL X will trigger)
    68      //      - Any other jobs that wake up and poll on URL X will not see it as changing unless it actually changed again since this time
    69  
    70      //        URLTrigger(
    71      //            cronTabSpec: branchSpecificSchedule,
    72      //            entries: [
    73      //                URLTriggerEntry(
    74      //                    url: "https://objectstorage.${ociOsRegion}.oraclecloud.com/n/${OS_NAMESPACE_URL_TRIGGER}/b/${ociOsBucket}/o/${urlTriggerBranchName}/${lastStableCommitFile}",
    75      //                    checkETag: false,
    76      //                    checkStatus: true,
    77      //                    statusCode: 403,
    78      //                    checkLastModificationDate: true,
    79      //                    timeout: 200,
    80      //                    requestHeaders: [
    81      //                        RequestHeader( headerName: "Accept" , headerValue: "application/json" )
    82      //                    ],
    83      //                   contentTypes: [
    84      //                        MD5Sum()
    85      //                    ]
    86      //                )
    87      //            ]
    88      //        )
    89      //    }
    90  
    91      parameters {
    92          string (name: 'TAGGED_TESTS',
    93                  defaultValue: '',
    94                  description: 'A comma separated list of build tags for tests that should be executed (e.g. unstable_test). Default:',
    95                  trim: true)
    96          string (name: 'INCLUDED_TESTS',
    97                  defaultValue: '.*',
    98                  description: 'A regex matching any fully qualified test file that should be executed (e.g. examples/helidon/). Default: .*',
    99                  trim: true)
   100          string (name: 'EXCLUDED_TESTS',
   101                  defaultValue: '_excluded_test',
   102                  description: 'A regex matching any fully qualified test file that should not be executed (e.g. multicluster/|_excluded_test). Default: _excluded_test',
   103                  trim: true)
   104          booleanParam (description: 'Force execution of the tests even if up-to-date', name: 'FORCE', defaultValue: false)
   105          booleanParam (description: 'Skip test execution (for debugging)', name: 'DRY_RUN', defaultValue: false)
   106      }
   107  
   108      environment {
   109          IS_BACKEND_PIPELINE = "true"
   110          OCIR_SCAN_COMPARTMENT = credentials('ocir-scan-compartment')
   111          OCIR_SCAN_TARGET = credentials('ocir-scan-target')
   112          OCIR_SCAN_REGISTRY = credentials('ocir-scan-registry')
   113          OCIR_SCAN_REPOSITORY_PATH = credentials('ocir-scan-repository-path')
   114          DOCKER_SCAN_CREDS = credentials('v8odev-ocir')
   115          DOCKER_CREDS = credentials('github-packages-credentials-rw')
   116          DOCKER_REPO = 'ghcr.io'
   117  
   118          OCI_CLI_AUTH="instance_principal"
   119          OCI_OS_NAMESPACE = credentials('oci-os-namespace')
   120          OCI_OS_BUCKET="verrazzano-builds"
   121          OCI_OS_COMMIT_BUCKET="verrazzano-builds-by-commit"
   122          CLEAN_BRANCH_NAME = "${env.BRANCH_NAME.replace("/", "%2F")}"
   123          SERVICE_KEY = credentials('PAGERDUTY_SERVICE_KEY')
   124  
   125          STABLE_COMMIT_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-stable-commit.txt"
   126          LAST_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-backend-run-commit.txt"
   127          CLEAN_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt"
   128  
   129          STABLE_COMMIT_LOCATION = "${WORKSPACE}/last-stable-commit.txt"
   130          LAST_BACKEND_LOCATION = "${WORKSPACE}/last-backend-run-commit.txt"
   131          CLEAN_BACKEND_LOCATION = "${WORKSPACE}/last-clean-backend-commit.txt"
   132  
   133          OCI_OS_REGION="us-phoenix-1"
   134  
   135          PIPELINE_OWNERS = credentials('backendtests-owners')
   136      }
   137  
   138      // This job runs against the latest stable master commit. That is defined as the last clean master build and test run whose
   139      // commit has been stored in object storage. This job will fetch that commit from master and run extended tests using that.
   140      // This job is NOT currently setup to run extended tests from other branches, if you need to run those extended jobs you will
   141      // need to run those against your branch individually.
   142  
   143      stages {
   144          stage('Check last clean backend') {
   145              steps {
   146                  sh """
   147                      oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${STABLE_COMMIT_OS_LOCATION} --file ${STABLE_COMMIT_LOCATION}
   148                  """
   149  
   150                  script {
   151                      // Check if there is already a clean backend run at this commit already, and set the display name if
   152                      // it already is tested, or if doing a special run type (dry run, etc...)
   153                      preliminaryChecks()
   154                  }
   155              }
   156          }
   157          stage('Clean workspace and checkout') {
   158              when {
   159                  allOf {
   160                      expression { return runTests() }
   161                  }
   162              }
   163              steps {
   164                  script {
   165                      cleanWorkspaceAndCheckout()
   166                  }
   167              }
   168          }
   169  
   170          stage ('Backend Test Suites') {
   171              when {
   172                  allOf {
   173                      expression { return runTests() }
   174                  }
   175              }
   176              parallel {
   177                  stage('Quick Create Tests') {
   178                      steps {
   179                          script {
   180                              build job: "/verrazzano-clusterAPI-qc-kind-tests/${CLEAN_BRANCH_NAME}",
   181                                      parameters: [
   182                                              string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   183                                      ], wait: false, propagate: false
   184                          }
   185                      }
   186                  }
   187  
   188                  stage('Upgrade Path Minor Release Tests') {
   189                      steps {
   190                          script {
   191                              build job: "/verrazzano-push-triggered-upgrade-minor-release-tests/${CLEAN_BRANCH_NAME}",
   192                                  parameters: [
   193                                      string(name: 'N_JOBS_FOR_EACH_BATCH', value: '6'),
   194                                      string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   195                                      string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS),
   196                                      string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS),
   197                                      string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS),
   198                                  ], wait: true, propagate: true
   199                          }
   200                      }
   201                  }
   202  
   203                  stage('vz analyze tool') {
   204                      environment {
   205                          SEARCH_HTTP_ENDPOINT = credentials('search-gw-url')
   206                          SEARCH_PASSWORD = "${PROMETHEUS_CREDENTIALS_PSW}"
   207                          SEARCH_USERNAME = "${PROMETHEUS_CREDENTIALS_USR}"
   208                      }
   209                      steps {
   210                          retry(count: JOB_PROMOTION_RETRIES) {
   211                              script {
   212                                  build job: "/verrazzano-analyze-tool-test/${CLEAN_BRANCH_NAME}",
   213                                      parameters: [
   214                                          string(name: 'KUBERNETES_CLUSTER_VERSION', value: '1.27'),
   215                                          string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   216                                      ], wait: true
   217                              }
   218                          }
   219                      }
   220                  }
   221  
   222                  stage('A la carte tests') {
   223                      steps {
   224                          script {
   225                              build job: "/verrazzano-a-la-carte-triggered/${CLEAN_BRANCH_NAME}",
   226                                  parameters: [
   227                                      string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   228                                  ], wait: true, propagate: true
   229                          }
   230                      }
   231                  }
   232  
   233                  stage('Examples on OKE OCI DNS Tests') {
   234                      steps {
   235                          retry(count: JOB_PROMOTION_RETRIES) {
   236                              script {
   237                                  build job: "verrazzano-new-oci-dns-examples-tests/${CLEAN_BRANCH_NAME}",
   238                                      parameters: [
   239                                          string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   240                                          string(name: 'TEST_ENV', value: "ocidns_oke"),
   241                                          string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS),
   242                                          string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS),
   243                                          string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS)
   244                                      ], wait: true
   245                              }
   246                          }
   247                      }
   248                      post {
   249                          failure {
   250                              script {
   251                                  TESTS_FAILED = true
   252                              }
   253                          }
   254                      }
   255                  }
   256              }
   257          }
   258          stage('Update Last Clean Backend Test') {
   259              when {
   260                  allOf {
   261                      expression { return runTests() }
   262                      expression { TESTS_FAILED == false }
   263                  }
   264              }
   265              environment {
   266                  GIT_COMMIT_USED = "${env.GIT_COMMIT}"
   267              }
   268              steps {
   269                  script {
   270                      sh """
   271                          # Update the clean backend commit
   272                          echo "git-commit=${GIT_COMMIT_USED}" > commit-that-passed.txt
   273                          oci --region ${OCI_OS_REGION} os object put --force --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt --file commit-that-passed.txt
   274                      """
   275                  }
   276              }
   277          }
   278      }
   279      post {
   280          always {
   281              script {
   282                  sh """
   283                      # Update the last backend commit
   284                      echo "git-commit=${env.GIT_COMMIT}" > commit-used.txt
   285                      oci --region ${OCI_OS_REGION} os object put --force --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${LAST_BACKEND_OS_LOCATION} --file commit-used.txt
   286                  """
   287              }
   288          }
   289          failure {
   290              script {
   291                  failedOrAborted()
   292              }
   293          }
   294          aborted {
   295              script {
   296                  failedOrAborted()
   297              }
   298          }
   299          cleanup {
   300              deleteDir()
   301          }
   302      }
   303  }
   304  
   305  def failedOrAborted() {
   306      if (isAlertingEnabled()) {
   307          if (isPagerDutyEnabled()) {
   308              pagerduty(resolve: false, serviceKey: "$SERVICE_KEY",
   309              incDescription: "Verrazzano Backend Tests: ${env.JOB_NAME} - Failed",
   310              incDetails: "Job Failed - \"${env.JOB_NAME}\" build: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nBlue Ocean:\n${env.RUN_DISPLAY_URL}")
   311          }
   312          slackSend ( channel: "$SLACK_ALERT_CHANNEL", message: "Job Failed - \"${env.JOB_NAME}\" build: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nBlue Ocean:\n${env.RUN_DISPLAY_URL}\n\nSuspects:\n${SUSPECT_LIST} ${PIPELINE_OWNERS}\n\nChange comparison: ${COMPARISON_URL_ON_FAILURE}" )
   313          echo "done alerts"
   314      }
   315  }
   316  
   317  // Preliminary job checks and display updates
   318  def preliminaryChecks() {
   319      // Get the last stable commit ID to pass the triggered tests
   320      def stableCommitProps = readProperties file: "${STABLE_COMMIT_LOCATION}"
   321      GIT_COMMIT_TO_USE = stableCommitProps['git-commit']
   322      echo "Last stable commit: ${GIT_COMMIT_TO_USE}"
   323  
   324      LAST_CLEAN_BACKEND_COMMIT=getLastCleanBackendCommit()
   325      echo "Last clean backend commit: ${LAST_CLEAN_BACKEND_COMMIT}"
   326  
   327      if (LAST_CLEAN_BACKEND_COMMIT == GIT_COMMIT_TO_USE) {
   328          backendTestsUpToDate = true
   329      } else {
   330          // Check if we are still at the same commit previously run (if so we know it wasn't clean and it failed in some way)
   331          LAST_BACKEND_RUN_COMMIT=getLastBackendRunCommit()
   332          if (LAST_BACKEND_RUN_COMMIT != null && LAST_BACKEND_RUN_COMMIT == GIT_COMMIT_TO_USE) {
   333              backendTestsUpToDateFailed = true
   334          }
   335      }
   336  
   337      echo "Up to date: ${backendTestsUpToDate}"
   338      echo "Dry run: ${params.DRY_RUN}"
   339      echo "Force run: ${params.FORCE}"
   340      echo "Execute tests: " + runTests()
   341  
   342      // Indicate in title if run is up-to-date or dry-run
   343      if (params.DRY_RUN) {
   344          currentBuild.displayName = "${currentBuild.displayName} : DRY-RUN"
   345      }
   346      if (backendTestsUpToDate) {
   347          currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE"
   348      }
   349      if (params.FORCE) {
   350          currentBuild.displayName = "${currentBuild.displayName} : FORCE"
   351      } else if (backendTestsUpToDateFailed) {
   352         currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE-FAILED"
   353         currentBuild.result = 'FAILURE'
   354         error('Failing the build since the current commit matches the commit of previously failing backend build')
   355      }
   356  
   357      if (runTests()) {
   358          echo "Executing backend tests for commit ${GIT_COMMIT_TO_USE}"
   359      }
   360  }
   361  
   362  def dockerLogins() {
   363      try {
   364          sh """
   365              echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin
   366          """
   367      } catch(error) {
   368          echo "docker login failed, retrying after sleep"
   369          retry(4) {
   370              sleep(30)
   371              sh """
   372              echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin
   373              """
   374          }
   375      }
   376      if (!(env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) {
   377          try {
   378              sh """
   379                  echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin
   380              """
   381          } catch(error) {
   382              echo "docker login failed, retrying after sleep"
   383              retry(4) {
   384                  sleep(30)
   385                  sh """
   386                      echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin
   387                  """
   388              }
   389          }
   390      }
   391  }
   392  
   393  def scmCheckout() {
   394      echo "${NODE_LABELS}"
   395      echo "SCM checkout of ${GIT_COMMIT_TO_USE}"
   396      def scmInfo = checkout([
   397          $class: 'GitSCM',
   398          branches: [[name: GIT_COMMIT_TO_USE]],
   399          doGenerateSubmoduleConfigurations: false,
   400          extensions: [],
   401          submoduleCfg: [],
   402          userRemoteConfigs: [[url: env.SCM_VERRAZZANO_GIT_URL]]])
   403      env.GIT_COMMIT = scmInfo.GIT_COMMIT
   404      env.GIT_BRANCH = scmInfo.GIT_BRANCH
   405      echo "SCM checkout of ${env.GIT_BRANCH} at ${env.GIT_COMMIT}"
   406      // If the commit we were handed is not what the SCM says we are using, fail
   407      if (!env.GIT_COMMIT.equals(GIT_COMMIT_TO_USE)) {
   408          error( "SCM didn't checkout the commit we expected. Expected: ${GIT_COMMIT_TO_USE}, Found: ${scmInfo.GIT_COMMIT}")
   409      }
   410  
   411      if (LAST_CLEAN_BACKEND_COMMIT != null) {
   412          COMPARISON_URL_ON_FAILURE = "https://github.com/verrazzano/verrazzano/compare/${LAST_CLEAN_BACKEND_COMMIT}...${GIT_COMMIT_TO_USE}"
   413          def lastClean = "${LAST_CLEAN_BACKEND_COMMIT}"
   414          def currentStable = "${GIT_COMMIT_TO_USE}"
   415          def commitList = getCommitListFromGitLog(lastClean, currentStable)
   416          withCredentials([file(credentialsId: 'jenkins-to-slack-users', variable: 'JENKINS_TO_SLACK_JSON')]) {
   417              def userMappings = readJSON file: JENKINS_TO_SLACK_JSON
   418              SUSPECT_LIST = getSuspectList(commitList, userMappings)
   419              echo "Suspect list: ${SUSPECT_LIST}"
   420          }
   421      }
   422      echo "URL if fails: ${COMPARISON_URL_ON_FAILURE}"
   423  }
   424  
   425  def cleanWorkspaceAndCheckout() {
   426      scmCheckout()
   427      dockerLogins()
   428      TIMESTAMP = sh(returnStdout: true, script: "date +%Y%m%d%H%M%S").trim()
   429      SHORT_COMMIT_HASH = sh(returnStdout: true, script: "git rev-parse --short=8 HEAD").trim()
   430      // update the description with some meaningful info
   431      currentBuild.description = SHORT_COMMIT_HASH + " : " + env.GIT_COMMIT + " : " + GIT_COMMIT_TO_USE
   432      storeLocation="ephemeral/${env.BRANCH_NAME}/${SHORT_COMMIT_HASH}"
   433  }
   434  
   435  // Returns the last clean commit for the backends, or null if the commit file does not exist yet.
   436  // - fails the pipeline if any error other than 404 is returned by the OCI CLI
   437  def getLastCleanBackendCommit() {
   438      lastBackendCommitCommandOutput = sh (
   439          label: "Get last clean backend commit ID",
   440          script: "oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${CLEAN_BACKEND_OS_LOCATION} --file ${CLEAN_BACKEND_LOCATION} 2>&1 || true",
   441          returnStdout: true
   442          ).trim()
   443      echo "command out: ${lastBackendCommitCommandOutput}"
   444      if (lastBackendCommitCommandOutput.length() > 0) {
   445          // We can get warning messages here as well even when the command succeeded, so be more precise on the checking
   446          if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) {
   447              // If we think we had a status: NNN, we ignore 404 and fail for others
   448              assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   449          } else {
   450              // If we got here, we have some message that may or may not be an error. If we don't see the file, we assume it was an error
   451              sh """
   452                  if [ ! -f ${CLEAN_BACKEND_LOCATION} ]; then
   453                      echo "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   454                      exit 1
   455                  fi
   456              """
   457          }
   458      }
   459      // Get the commit ID for the last known clean pass of the Backend tests
   460      def cleanBackendsCommitProps = readProperties file: "${CLEAN_BACKEND_LOCATION}"
   461      return cleanBackendsCommitProps['git-commit']
   462  }
   463  
   464  // Returns the last run commit for the Backend, or null if the commit file does not exist yet.
   465  // - fails the pipeline if any error other than 404 is returned by the OCI CLI
   466  def getLastBackendRunCommit() {
   467      lastBackendCommitCommandOutput = sh (
   468          label: "Get last clean backend commit ID",
   469          script: "oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${LAST_BACKEND_OS_LOCATION} --file ${LAST_BACKEND_LOCATION} 2>&1 || true",
   470          returnStdout: true
   471          ).trim()
   472      echo "command out: ${lastBackendCommitCommandOutput}"
   473      if (lastBackendCommitCommandOutput.length() > 0) {
   474          // We can get warning messages here as well even when the command succeeded, so be more precise on the checking
   475          if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) {
   476              // If we think we had a status: NNN, we ignore 404 and fail for others
   477              assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   478          } else {
   479              // If we got here, we have some message that may or may not be an error. If we don't see the file, we assume it was an error
   480              sh """
   481                  if [ ! -f ${LAST_BACKEND_LOCATION} ]; then
   482                      echo "An unexpected error occurred getting last backend run commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   483                      exit 1
   484                  fi
   485              """
   486          }
   487      }
   488      // Get the commit ID for the last known clean pass of the Backend tests
   489      def lastBackendCommitProps = readProperties file: "${LAST_BACKEND_LOCATION}"
   490      return lastBackendCommitProps['git-commit']
   491  }
   492  
   493  // Checks all the conditions gating test execution and collates the result
   494  def runTests() {
   495    return params.FORCE || ( ! backendTestsUpToDate && ! backendTestsUpToDateFailed && ! params.DRY_RUN )
   496  }
   497  
   498  def isAlertingEnabled() {
   499      // this controls whether any alerting happens for these tests
   500      if (NOTIFY_BACKEND_FAILURES.equals("true") && (env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) {
   501          echo "Alert notifications enabled for ${env.BRANCH_NAME}"
   502          return true
   503      }
   504      return false
   505  }
   506  
   507  def isPagerDutyEnabled() {
   508      // this additionally controls whether PD alerts are enabled (note that you must also enable alerting in general as well if you want these)
   509      if (NOTIFY_PAGERDUTY_BACKEND_FAILURES.equals("true")) {
   510          echo "Pager-Duty notifications enabled via global override setting"
   511          return true
   512      }
   513      return false
   514  }
   515  
   516  //  def getCronSchedule() {
   517  //      if (env.BRANCH_NAME.equals("master")) {
   518  //          return "@weekly"
   519  //      } else if (env.BRANCH_NAME.startsWith("release-")) {
   520  //          return "@weekly"
   521  //      }
   522  //      return ""
   523  //  }
   524  
   525  // Called in Stage Clean workspace and checkout steps
   526  def getCommitListFromGitLog(lastClean, currentStable) {
   527      echo "Checking for change sets"
   528      def commitList = sh(returnStdout: true, script: "git log ${lastClean}...${currentStable} --oneline | cut -d \" \" -f 1").trim().split('\n')
   529      for (int i = 0; i < commitList.size(); i++) {
   530          echo "Found commit id: ${commitList[i]}"
   531      }
   532      return commitList
   533  }
   534  
   535  def trimIfGithubNoreplyUser(userIn) {
   536      if (userIn == null) {
   537          echo "Not a github noreply user, not trimming: ${userIn}"
   538          return userIn
   539      }
   540      if (userIn.matches(".*\\+.*@users.noreply.github.com.*")) {
   541          def userOut = userIn.substring(userIn.indexOf("+") + 1, userIn.indexOf("@"))
   542          return userOut;
   543      }
   544      if (userIn.matches(".*<.*@users.noreply.github.com.*")) {
   545          def userOut = userIn.substring(userIn.indexOf("<") + 1, userIn.indexOf("@"))
   546          return userOut;
   547      }
   548      if (userIn.matches(".*@users.noreply.github.com")) {
   549          def userOut = userIn.substring(0, userIn.indexOf("@"))
   550          return userOut;
   551      }
   552      echo "Not a github noreply user, not trimming: ${userIn}"
   553      return userIn
   554  }
   555  
   556  def getSuspectList(commitList, userMappings) {
   557      def retValue = ""
   558      def suspectList = []
   559      if (commitList == null || commitList.size() == 0) {
   560          echo "No commits to form suspect list"
   561      } else {
   562          for (int i = 0; i < commitList.size(); i++) {
   563              def id = commitList[i]
   564              try {
   565                  def gitAuthor = sh(
   566                      script: "git log --format='%ae' '$id^!'",
   567                      returnStdout: true
   568                  ).trim()
   569                  if (gitAuthor != null) {
   570                      def author = trimIfGithubNoreplyUser(gitAuthor)
   571                      echo "DEBUG: author: ${gitAuthor}, ${author}, id: ${id}"
   572                      if (userMappings.containsKey(author)) {
   573                          def slackUser = userMappings.get(author)
   574                          if (!suspectList.contains(slackUser)) {
   575                              echo "Added ${slackUser} as suspect"
   576                              retValue += " ${slackUser}"
   577                              suspectList.add(slackUser)
   578                          }
   579                      } else {
   580                          // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated
   581                          if (!suspectList.contains(author)) {
   582                              echo "Added ${author} as suspect"
   583                              retValue += " ${author}"
   584                              suspectList.add(author)
   585                          }
   586                      }
   587                  } else {
   588                      echo "No author returned from git"
   589                  }
   590              } catch (Exception e) {
   591                  echo "INFO: Problem processing commit ${id}, skipping commit: " + e.toString()
   592              }
   593          }
   594      }
   595      def startedByUser = "";
   596      def causes = currentBuild.getBuildCauses()
   597      echo "causes: " + causes.toString()
   598      for (cause in causes) {
   599          def causeString = cause.toString()
   600          echo "current cause: " + causeString
   601          def causeInfo = readJSON text: causeString
   602          if (causeInfo.userId != null) {
   603              startedByUser = causeInfo.userId
   604          }
   605      }
   606  
   607      if (startedByUser.length() > 0) {
   608          echo "Build was started by a user, adding them to the suspect notification list: ${startedByUser}"
   609          def author = trimIfGithubNoreplyUser(startedByUser)
   610          echo "DEBUG: author: ${startedByUser}, ${author}"
   611          if (userMappings.containsKey(author)) {
   612              def slackUser = userMappings.get(author)
   613              if (!suspectList.contains(slackUser)) {
   614                  echo "Added ${slackUser} as suspect"
   615                  retValue += " ${slackUser}"
   616                  suspectList.add(slackUser)
   617              }
   618          } else {
   619              // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated
   620              if (!suspectList.contains(author)) {
   621                 echo "Added ${author} as suspect"
   622                 retValue += " ${author}"
   623                 suspectList.add(author)
   624              }
   625          }
   626      } else {
   627          echo "Build not started by a user, not adding to notification list"
   628      }
   629      echo "returning suspect list: ${retValue}"
   630      return retValue
   631  }
   632  
   633  @NonCPS
   634  List extractReleaseTags(final String fileContent) {
   635      List releases = []
   636      fileContent.eachLine { tag ->
   637          releases << tag
   638      }
   639      return releases
   640  }
   641  
   642  def getLatestReleaseVersion() {
   643      final String releaseTags = readFile(file: "${workspace}/tags.txt")
   644      list gitTags = extractReleaseTags(releaseTags)
   645      echo "gitTags = ${gitTags}"
   646      return gitTags.pop()
   647  }