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

     1  // Copyright (c) 2021, 2024, 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 = "$OCI_OS_BUCKET"
    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          CLEAN_BRANCH_NAME = "${env.BRANCH_NAME.replace("/", "%2F")}"
   121          SERVICE_KEY = credentials('PAGERDUTY_SERVICE_KEY')
   122  
   123          STABLE_COMMIT_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-stable-commit.txt"
   124          LAST_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-backend-run-commit.txt"
   125          CLEAN_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt"
   126  
   127          STABLE_COMMIT_LOCATION = "${WORKSPACE}/last-stable-commit.txt"
   128          LAST_BACKEND_LOCATION = "${WORKSPACE}/last-backend-run-commit.txt"
   129          CLEAN_BACKEND_LOCATION = "${WORKSPACE}/last-clean-backend-commit.txt"
   130  
   131          OCI_OS_REGION="us-phoenix-1"
   132  
   133          PIPELINE_OWNERS = credentials('backendtests-owners')
   134      }
   135  
   136      // This job runs against the latest stable master commit. That is defined as the last clean master build and test run whose
   137      // commit has been stored in object storage. This job will fetch that commit from master and run extended tests using that.
   138      // This job is NOT currently setup to run extended tests from other branches, if you need to run those extended jobs you will
   139      // need to run those against your branch individually.
   140  
   141      stages {
   142          stage('Check last clean backend') {
   143              steps {
   144                  sh """
   145                      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}
   146                  """
   147  
   148                  script {
   149                      // Check if there is already a clean backend run at this commit already, and set the display name if
   150                      // it already is tested, or if doing a special run type (dry run, etc...)
   151                      preliminaryChecks()
   152                  }
   153              }
   154          }
   155          stage('Clean workspace and checkout') {
   156              when {
   157                  allOf {
   158                      expression { return runTests() }
   159                  }
   160              }
   161              steps {
   162                  script {
   163                      cleanWorkspaceAndCheckout()
   164                  }
   165              }
   166          }
   167  
   168          stage ('Backend Test Suites') {
   169              when {
   170                  allOf {
   171                      expression { return runTests() }
   172                  }
   173              }
   174              parallel {
   175                  stage('Quick Create Tests') {
   176                      steps {
   177                          script {
   178                              build job: "/verrazzano-clusterAPI-qc-kind-tests/${CLEAN_BRANCH_NAME}",
   179                                      parameters: [
   180                                              string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   181                                      ], wait: false, propagate: false
   182                          }
   183                      }
   184                  }
   185  
   186                  stage('Upgrade Path Minor Release Tests') {
   187                      steps {
   188                          script {
   189                              build job: "/verrazzano-push-triggered-upgrade-minor-release-tests/${CLEAN_BRANCH_NAME}",
   190                                  parameters: [
   191                                      string(name: 'N_JOBS_FOR_EACH_BATCH', value: '6'),
   192                                      string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   193                                      string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS),
   194                                      string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS),
   195                                      string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS),
   196                                  ], wait: true, propagate: true
   197                          }
   198                      }
   199                  }
   200  
   201                  stage('vz analyze tool') {
   202                      steps {
   203                          retry(count: JOB_PROMOTION_RETRIES) {
   204                              script {
   205                                  build job: "/verrazzano-analyze-tool-test/${CLEAN_BRANCH_NAME}",
   206                                      parameters: [
   207                                          string(name: 'KUBERNETES_CLUSTER_VERSION', value: '1.27'),
   208                                          string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   209                                      ], wait: true
   210                              }
   211                          }
   212                      }
   213                  }
   214  
   215                  stage('A la carte tests') {
   216                      steps {
   217                          script {
   218                              build job: "/verrazzano-a-la-carte-triggered/${CLEAN_BRANCH_NAME}",
   219                                  parameters: [
   220                                      string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   221                                  ], wait: true, propagate: true
   222                          }
   223                      }
   224                  }
   225  
   226                  stage('Examples on OKE OCI DNS Tests') {
   227                      steps {
   228                          retry(count: JOB_PROMOTION_RETRIES) {
   229                              script {
   230                                  build job: "verrazzano-new-oci-dns-examples-tests/${CLEAN_BRANCH_NAME}",
   231                                      parameters: [
   232                                          string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT),
   233                                          string(name: 'TEST_ENV', value: "ocidns_oke"),
   234                                          string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS),
   235                                          string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS),
   236                                          string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS)
   237                                      ], wait: true
   238                              }
   239                          }
   240                      }
   241                      post {
   242                          failure {
   243                              script {
   244                                  TESTS_FAILED = true
   245                              }
   246                          }
   247                      }
   248                  }
   249              }
   250          }
   251          stage('Update Last Clean Backend Test') {
   252              when {
   253                  allOf {
   254                      expression { return runTests() }
   255                      expression { TESTS_FAILED == false }
   256                  }
   257              }
   258              environment {
   259                  GIT_COMMIT_USED = "${env.GIT_COMMIT}"
   260              }
   261              steps {
   262                  script {
   263                      sh """
   264                          # Update the clean backend commit
   265                          echo "git-commit=${GIT_COMMIT_USED}" > commit-that-passed.txt
   266                          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
   267                      """
   268                  }
   269              }
   270          }
   271      }
   272      post {
   273          always {
   274              script {
   275                  sh """
   276                      # Update the last backend commit
   277                      echo "git-commit=${env.GIT_COMMIT}" > commit-used.txt
   278                      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
   279                  """
   280              }
   281          }
   282          failure {
   283              script {
   284                  failedOrAborted()
   285              }
   286          }
   287          aborted {
   288              script {
   289                  failedOrAborted()
   290              }
   291          }
   292          cleanup {
   293              deleteDir()
   294          }
   295      }
   296  }
   297  
   298  def failedOrAborted() {
   299      if (isAlertingEnabled()) {
   300          if (isPagerDutyEnabled()) {
   301              pagerduty(resolve: false, serviceKey: "$SERVICE_KEY",
   302              incDescription: "Verrazzano Backend Tests: ${env.JOB_NAME} - Failed",
   303              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}")
   304          }
   305          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}" )
   306          echo "done alerts"
   307      }
   308  }
   309  
   310  // Preliminary job checks and display updates
   311  def preliminaryChecks() {
   312      // Get the last stable commit ID to pass the triggered tests
   313      def stableCommitProps = readProperties file: "${STABLE_COMMIT_LOCATION}"
   314      GIT_COMMIT_TO_USE = stableCommitProps['git-commit']
   315      echo "Last stable commit: ${GIT_COMMIT_TO_USE}"
   316  
   317      LAST_CLEAN_BACKEND_COMMIT=getLastCleanBackendCommit()
   318      echo "Last clean backend commit: ${LAST_CLEAN_BACKEND_COMMIT}"
   319  
   320      if (LAST_CLEAN_BACKEND_COMMIT == GIT_COMMIT_TO_USE) {
   321          backendTestsUpToDate = true
   322      } else {
   323          // 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)
   324          LAST_BACKEND_RUN_COMMIT=getLastBackendRunCommit()
   325          if (LAST_BACKEND_RUN_COMMIT != null && LAST_BACKEND_RUN_COMMIT == GIT_COMMIT_TO_USE) {
   326              backendTestsUpToDateFailed = true
   327          }
   328      }
   329  
   330      echo "Up to date: ${backendTestsUpToDate}"
   331      echo "Dry run: ${params.DRY_RUN}"
   332      echo "Force run: ${params.FORCE}"
   333      echo "Execute tests: " + runTests()
   334  
   335      // Indicate in title if run is up-to-date or dry-run
   336      if (params.DRY_RUN) {
   337          currentBuild.displayName = "${currentBuild.displayName} : DRY-RUN"
   338      }
   339      if (backendTestsUpToDate) {
   340          currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE"
   341      }
   342      if (params.FORCE) {
   343          currentBuild.displayName = "${currentBuild.displayName} : FORCE"
   344      } else if (backendTestsUpToDateFailed) {
   345         currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE-FAILED"
   346         currentBuild.result = 'FAILURE'
   347         error('Failing the build since the current commit matches the commit of previously failing backend build')
   348      }
   349  
   350      if (runTests()) {
   351          echo "Executing backend tests for commit ${GIT_COMMIT_TO_USE}"
   352      }
   353  }
   354  
   355  def dockerLogins() {
   356      try {
   357          sh """
   358              echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin
   359          """
   360      } catch(error) {
   361          echo "docker login failed, retrying after sleep"
   362          retry(4) {
   363              sleep(30)
   364              sh """
   365              echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin
   366              """
   367          }
   368      }
   369      if (!(env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) {
   370          try {
   371              sh """
   372                  echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin
   373              """
   374          } catch(error) {
   375              echo "docker login failed, retrying after sleep"
   376              retry(4) {
   377                  sleep(30)
   378                  sh """
   379                      echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin
   380                  """
   381              }
   382          }
   383      }
   384  }
   385  
   386  def scmCheckout() {
   387      echo "${NODE_LABELS}"
   388      echo "SCM checkout of ${GIT_COMMIT_TO_USE}"
   389      def scmInfo = checkout([
   390          $class: 'GitSCM',
   391          branches: [[name: GIT_COMMIT_TO_USE]],
   392          doGenerateSubmoduleConfigurations: false,
   393          extensions: [],
   394          submoduleCfg: [],
   395          userRemoteConfigs: [[url: env.SCM_VERRAZZANO_GIT_URL]]])
   396      env.GIT_COMMIT = scmInfo.GIT_COMMIT
   397      env.GIT_BRANCH = scmInfo.GIT_BRANCH
   398      echo "SCM checkout of ${env.GIT_BRANCH} at ${env.GIT_COMMIT}"
   399      // If the commit we were handed is not what the SCM says we are using, fail
   400      if (!env.GIT_COMMIT.equals(GIT_COMMIT_TO_USE)) {
   401          error( "SCM didn't checkout the commit we expected. Expected: ${GIT_COMMIT_TO_USE}, Found: ${scmInfo.GIT_COMMIT}")
   402      }
   403  
   404      if (LAST_CLEAN_BACKEND_COMMIT != null) {
   405          COMPARISON_URL_ON_FAILURE = "https://github.com/verrazzano/verrazzano/compare/${LAST_CLEAN_BACKEND_COMMIT}...${GIT_COMMIT_TO_USE}"
   406          def lastClean = "${LAST_CLEAN_BACKEND_COMMIT}"
   407          def currentStable = "${GIT_COMMIT_TO_USE}"
   408          def commitList = getCommitListFromGitLog(lastClean, currentStable)
   409          withCredentials([file(credentialsId: 'jenkins-to-slack-users', variable: 'JENKINS_TO_SLACK_JSON')]) {
   410              def userMappings = readJSON file: JENKINS_TO_SLACK_JSON
   411              SUSPECT_LIST = getSuspectList(commitList, userMappings)
   412              echo "Suspect list: ${SUSPECT_LIST}"
   413          }
   414      }
   415      echo "URL if fails: ${COMPARISON_URL_ON_FAILURE}"
   416  }
   417  
   418  def cleanWorkspaceAndCheckout() {
   419      scmCheckout()
   420      dockerLogins()
   421      TIMESTAMP = sh(returnStdout: true, script: "date +%Y%m%d%H%M%S").trim()
   422      SHORT_COMMIT_HASH = sh(returnStdout: true, script: "git rev-parse --short=8 HEAD").trim()
   423      // update the description with some meaningful info
   424      currentBuild.description = SHORT_COMMIT_HASH + " : " + env.GIT_COMMIT + " : " + GIT_COMMIT_TO_USE
   425      storeLocation="ephemeral/${env.BRANCH_NAME}/${SHORT_COMMIT_HASH}"
   426  }
   427  
   428  // Returns the last clean commit for the backends, or null if the commit file does not exist yet.
   429  // - fails the pipeline if any error other than 404 is returned by the OCI CLI
   430  def getLastCleanBackendCommit() {
   431      lastBackendCommitCommandOutput = sh (
   432          label: "Get last clean backend commit ID",
   433          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",
   434          returnStdout: true
   435          ).trim()
   436      echo "command out: ${lastBackendCommitCommandOutput}"
   437      if (lastBackendCommitCommandOutput.length() > 0) {
   438          // We can get warning messages here as well even when the command succeeded, so be more precise on the checking
   439          if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) {
   440              // If we think we had a status: NNN, we ignore 404 and fail for others
   441              assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   442          } else {
   443              // 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
   444              sh """
   445                  if [ ! -f ${CLEAN_BACKEND_LOCATION} ]; then
   446                      echo "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   447                      exit 1
   448                  fi
   449              """
   450          }
   451      }
   452      // Get the commit ID for the last known clean pass of the Backend tests
   453      def cleanBackendsCommitProps = readProperties file: "${CLEAN_BACKEND_LOCATION}"
   454      return cleanBackendsCommitProps['git-commit']
   455  }
   456  
   457  // Returns the last run commit for the Backend, or null if the commit file does not exist yet.
   458  // - fails the pipeline if any error other than 404 is returned by the OCI CLI
   459  def getLastBackendRunCommit() {
   460      lastBackendCommitCommandOutput = sh (
   461          label: "Get last clean backend commit ID",
   462          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",
   463          returnStdout: true
   464          ).trim()
   465      echo "command out: ${lastBackendCommitCommandOutput}"
   466      if (lastBackendCommitCommandOutput.length() > 0) {
   467          // We can get warning messages here as well even when the command succeeded, so be more precise on the checking
   468          if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) {
   469              // If we think we had a status: NNN, we ignore 404 and fail for others
   470              assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   471          } else {
   472              // 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
   473              sh """
   474                  if [ ! -f ${LAST_BACKEND_LOCATION} ]; then
   475                      echo "An unexpected error occurred getting last backend run commit from ObjectStore: ${lastBackendCommitCommandOutput}"
   476                      exit 1
   477                  fi
   478              """
   479          }
   480      }
   481      // Get the commit ID for the last known clean pass of the Backend tests
   482      def lastBackendCommitProps = readProperties file: "${LAST_BACKEND_LOCATION}"
   483      return lastBackendCommitProps['git-commit']
   484  }
   485  
   486  // Checks all the conditions gating test execution and collates the result
   487  def runTests() {
   488    return params.FORCE || ( ! backendTestsUpToDate && ! backendTestsUpToDateFailed && ! params.DRY_RUN )
   489  }
   490  
   491  def isAlertingEnabled() {
   492      // this controls whether any alerting happens for these tests
   493      if (NOTIFY_BACKEND_FAILURES.equals("true") && (env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) {
   494          echo "Alert notifications enabled for ${env.BRANCH_NAME}"
   495          return true
   496      }
   497      return false
   498  }
   499  
   500  def isPagerDutyEnabled() {
   501      // this additionally controls whether PD alerts are enabled (note that you must also enable alerting in general as well if you want these)
   502      if (NOTIFY_PAGERDUTY_BACKEND_FAILURES.equals("true")) {
   503          echo "Pager-Duty notifications enabled via global override setting"
   504          return true
   505      }
   506      return false
   507  }
   508  
   509  //  def getCronSchedule() {
   510  //      if (env.BRANCH_NAME.equals("master")) {
   511  //          return "@weekly"
   512  //      } else if (env.BRANCH_NAME.startsWith("release-")) {
   513  //          return "@weekly"
   514  //      }
   515  //      return ""
   516  //  }
   517  
   518  // Called in Stage Clean workspace and checkout steps
   519  def getCommitListFromGitLog(lastClean, currentStable) {
   520      echo "Checking for change sets"
   521      def commitList = sh(returnStdout: true, script: "git log ${lastClean}...${currentStable} --oneline | cut -d \" \" -f 1").trim().split('\n')
   522      for (int i = 0; i < commitList.size(); i++) {
   523          echo "Found commit id: ${commitList[i]}"
   524      }
   525      return commitList
   526  }
   527  
   528  def trimIfGithubNoreplyUser(userIn) {
   529      if (userIn == null) {
   530          echo "Not a github noreply user, not trimming: ${userIn}"
   531          return userIn
   532      }
   533      if (userIn.matches(".*\\+.*@users.noreply.github.com.*")) {
   534          def userOut = userIn.substring(userIn.indexOf("+") + 1, userIn.indexOf("@"))
   535          return userOut;
   536      }
   537      if (userIn.matches(".*<.*@users.noreply.github.com.*")) {
   538          def userOut = userIn.substring(userIn.indexOf("<") + 1, userIn.indexOf("@"))
   539          return userOut;
   540      }
   541      if (userIn.matches(".*@users.noreply.github.com")) {
   542          def userOut = userIn.substring(0, userIn.indexOf("@"))
   543          return userOut;
   544      }
   545      echo "Not a github noreply user, not trimming: ${userIn}"
   546      return userIn
   547  }
   548  
   549  def getSuspectList(commitList, userMappings) {
   550      def retValue = ""
   551      def suspectList = []
   552      if (commitList == null || commitList.size() == 0) {
   553          echo "No commits to form suspect list"
   554      } else {
   555          for (int i = 0; i < commitList.size(); i++) {
   556              def id = commitList[i]
   557              try {
   558                  def gitAuthor = sh(
   559                      script: "git log --format='%ae' '$id^!'",
   560                      returnStdout: true
   561                  ).trim()
   562                  if (gitAuthor != null) {
   563                      def author = trimIfGithubNoreplyUser(gitAuthor)
   564                      echo "DEBUG: author: ${gitAuthor}, ${author}, id: ${id}"
   565                      if (userMappings.containsKey(author)) {
   566                          def slackUser = userMappings.get(author)
   567                          if (!suspectList.contains(slackUser)) {
   568                              echo "Added ${slackUser} as suspect"
   569                              retValue += " ${slackUser}"
   570                              suspectList.add(slackUser)
   571                          }
   572                      } else {
   573                          // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated
   574                          if (!suspectList.contains(author)) {
   575                              echo "Added ${author} as suspect"
   576                              retValue += " ${author}"
   577                              suspectList.add(author)
   578                          }
   579                      }
   580                  } else {
   581                      echo "No author returned from git"
   582                  }
   583              } catch (Exception e) {
   584                  echo "INFO: Problem processing commit ${id}, skipping commit: " + e.toString()
   585              }
   586          }
   587      }
   588      def startedByUser = "";
   589      def causes = currentBuild.getBuildCauses()
   590      echo "causes: " + causes.toString()
   591      for (cause in causes) {
   592          def causeString = cause.toString()
   593          echo "current cause: " + causeString
   594          def causeInfo = readJSON text: causeString
   595          if (causeInfo.userId != null) {
   596              startedByUser = causeInfo.userId
   597          }
   598      }
   599  
   600      if (startedByUser.length() > 0) {
   601          echo "Build was started by a user, adding them to the suspect notification list: ${startedByUser}"
   602          def author = trimIfGithubNoreplyUser(startedByUser)
   603          echo "DEBUG: author: ${startedByUser}, ${author}"
   604          if (userMappings.containsKey(author)) {
   605              def slackUser = userMappings.get(author)
   606              if (!suspectList.contains(slackUser)) {
   607                  echo "Added ${slackUser} as suspect"
   608                  retValue += " ${slackUser}"
   609                  suspectList.add(slackUser)
   610              }
   611          } else {
   612              // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated
   613              if (!suspectList.contains(author)) {
   614                 echo "Added ${author} as suspect"
   615                 retValue += " ${author}"
   616                 suspectList.add(author)
   617              }
   618          }
   619      } else {
   620          echo "Build not started by a user, not adding to notification list"
   621      }
   622      echo "returning suspect list: ${retValue}"
   623      return retValue
   624  }
   625  
   626  @NonCPS
   627  List extractReleaseTags(final String fileContent) {
   628      List releases = []
   629      fileContent.eachLine { tag ->
   630          releases << tag
   631      }
   632      return releases
   633  }
   634  
   635  def getLatestReleaseVersion() {
   636      final String releaseTags = readFile(file: "${workspace}/tags.txt")
   637      list gitTags = extractReleaseTags(releaseTags)
   638      echo "gitTags = ${gitTags}"
   639      return gitTags.pop()
   640  }