github.com/xgoffin/jenkins-library@v1.154.0/vars/xsDeploy.groovy (about)

     1  import static com.sap.piper.Prerequisites.checkScript
     2  
     3  import com.sap.piper.ConfigurationHelper
     4  
     5  import com.sap.piper.DefaultValueCache
     6  import com.sap.piper.JenkinsUtils
     7  import com.sap.piper.PiperGoUtils
     8  
     9  
    10  import com.sap.piper.GenerateDocumentation
    11  import com.sap.piper.Utils
    12  
    13  import groovy.transform.Field
    14  
    15  @Field String METADATA_FILE = 'metadata/xsDeploy.yaml'
    16  @Field String PIPER_DEFAULTS = 'default_pipeline_environment.yml'
    17  @Field String STEP_NAME = getClass().getName()
    18  @Field String METADATA_FOLDER = '.pipeline' // metadata file contains already the "metadata" folder level, hence we end up in a folder ".pipeline/metadata"
    19  @Field String ADDITIONAL_CONFIGS_FOLDER='.pipeline/additionalConfigs'
    20  
    21  
    22  enum DeployMode {
    23      DEPLOY,
    24      BG_DEPLOY,
    25      NONE
    26  
    27      String toString() {
    28          name().toLowerCase(Locale.ENGLISH).replaceAll('_', '-')
    29      }
    30  }
    31  
    32  enum Action {
    33      RESUME,
    34      ABORT,
    35      RETRY,
    36      NONE
    37  
    38      String toString() {
    39          name().toLowerCase(Locale.ENGLISH)
    40      }
    41  }
    42  
    43  void call(Map parameters = [:]) {
    44  
    45      handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
    46  
    47          final script = checkScript(this, parameters) ?: null
    48  
    49          if(! script) {
    50              error "Reference to surrounding pipeline script not provided (script: this)."
    51          }
    52  
    53          def utils = parameters.juStabUtils ?: new Utils()
    54          def piperGoUtils = parameters.piperGoUtils ?: new PiperGoUtils(utils)
    55  
    56          //
    57          // The parameters map in provided from outside. That map might be used elsewhere in the pipeline
    58          // hence we should not modify it here. So we create a new map based on the parameters map.
    59          parameters = [:] << parameters
    60  
    61          // hard to predict how these parameters looks like in its serialized form. Anyhow it is better
    62          // not to have these parameters forwarded somehow to the go layer.
    63          parameters.remove('juStabUtils')
    64          parameters.remove('piperGoUtils')
    65          parameters.remove('script')
    66  
    67          piperGoUtils.unstashPiperBin()
    68  
    69          //
    70          // Printing the piper-go version. Should not be done here, but somewhere during materializing
    71          // the piper binary. As long as we don't have it elsewhere we should keep it here.
    72          def piperGoVersion = sh(returnStdout: true, script: "./piper version")
    73          echo "PiperGoVersion: ${piperGoVersion}"
    74  
    75          //
    76          // since there is no valid config provided (... null) telemetry is disabled (same for other go releated steps at the moment).
    77          utils.pushToSWA([
    78              step: STEP_NAME,
    79          ], null)
    80  
    81          String configFiles = prepareConfigurations([PIPER_DEFAULTS].plus(script.commonPipelineEnvironment.getCustomDefaults()), ADDITIONAL_CONFIGS_FOLDER)
    82  
    83          writeFile(file: "${METADATA_FOLDER}/${METADATA_FILE}", text: libraryResource(METADATA_FILE))
    84  
    85          withEnv([
    86              "PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(parameters)}",
    87          ]) {
    88  
    89              //
    90              // context config gives us e.g. the docker image name. --> How does this work for customer maintained images?
    91              // There is a name provided in the metadata file. But we do not provide a docker image for that.
    92              // The user has to build that for her/his own. How do we expect to configure this?
    93  
    94              String projectConfigScript = "./piper getConfig --stepMetadata '${METADATA_FOLDER}/${METADATA_FILE}' --defaultConfig ${configFiles}"
    95              String contextConfigScript = projectConfigScript + " --contextConfig"
    96              Map projectConfig = readJSON (text: sh(returnStdout: true, script: projectConfigScript))
    97              Map contextConfig = readJSON (text: sh(returnStdout: true, script: contextConfigScript))
    98  
    99              Map options = getOptions(parameters, projectConfig, contextConfig, script.commonPipelineEnvironment)
   100  
   101              Action action = options.action
   102              DeployMode mode = options.mode
   103  
   104              if(parameters.verbose) {
   105                  echo "[INFO] ContextConfig: ${contextConfig}"
   106                  echo "[INFO] ProjectConfig: ${projectConfig}"
   107              }
   108  
   109              def mtarFilePath = script.commonPipelineEnvironment.mtarFilePath
   110  
   111              def operationId = parameters.operationId
   112              if(! operationId && mode == DeployMode.BG_DEPLOY && action != Action.NONE) {
   113                  operationId = script.commonPipelineEnvironment.xsDeploymentId
   114                  if (! operationId) {
   115                      throw new IllegalArgumentException('No operationId provided. Was there a deployment before?')
   116                  }
   117              }
   118  
   119              def xsDeployStdout
   120  
   121              lock(getLockIdentifier(projectConfig)) {
   122  
   123                  withCredentials([usernamePassword(
   124                          credentialsId: contextConfig.credentialsId,
   125                          passwordVariable: 'PASSWORD',
   126                          usernameVariable: 'USERNAME')]) {
   127  
   128                      dockerExecute([script: this].plus([dockerImage: options.dockerImage, dockerPullImage: options.dockerPullImage])) {
   129                          xsDeployStdout = sh returnStdout: true, script: """#!/bin/bash
   130                          ./piper xsDeploy --defaultConfig ${configFiles} --username \${USERNAME} --password \${PASSWORD} ${mtarFilePath ? '--mtaPath ' + mtarFilePath : ''} ${operationId ? '--operationId ' + operationId : ''}
   131                          """
   132                      }
   133  
   134                  }
   135              }
   136  
   137              if(mode == DeployMode.BG_DEPLOY && action == Action.NONE) {
   138                  script.commonPipelineEnvironment.xsDeploymentId = readJSON(text: xsDeployStdout).operationId
   139                  if (!script.commonPipelineEnvironment.xsDeploymentId) {
   140                      error "No Operation id returned from xs deploy step. This is required for mode '${mode}' and action '${action}'."
   141                  }
   142                  echo "[INFO] OperationId for subsequent resume or abort: '${script.commonPipelineEnvironment.xsDeploymentId}'."
   143              }
   144          }
   145      }
   146  }
   147  
   148  String getLockIdentifier(Map config) {
   149      "$STEP_NAME:${config.apiUrl}:${config.org}:${config.space}"
   150  }
   151  
   152  /*
   153   * The returned string can be used directly in the command line for retrieving the configuration via go
   154   */
   155  String prepareConfigurations(List configs, String configCacheFolder) {
   156  
   157      for(def customDefault : configs) {
   158          writeFile(file: "${ADDITIONAL_CONFIGS_FOLDER}/${customDefault}", text: libraryResource(customDefault))
   159      }
   160      joinAndQuote(configs.reverse(), configCacheFolder)
   161  }
   162  
   163  /*
   164   * prefix is supposed to be provided without trailing slash
   165   */
   166  String joinAndQuote(List l, String prefix = '') {
   167      _l = []
   168  
   169      if(prefix == null) {
   170          prefix = ''
   171      }
   172      if(prefix.endsWith('/') || prefix.endsWith('\\'))
   173          throw new IllegalArgumentException("Provide prefix (${prefix}) without trailing slash")
   174  
   175      for(def e : l) {
   176          def _e = ''
   177          if(prefix.length() > 0) {
   178              _e += prefix
   179              _e += '/'
   180          }
   181          _e += e
   182          _l << '"' + _e + '"'
   183      }
   184      _l.join(' ')
   185  }
   186  
   187  /*
   188     ugly backward compatibility handling
   189     retrieves docker options from project config or from landscape config layer(s)
   190  
   191     precedence is
   192     1.) parameters via signature
   193     2.) project config (not nested)
   194     3.) project config (nested inside docker node)
   195     4.) context config (if applicable (docker))
   196  */
   197  Map getOptions(Map parameters, Map projectConfig, Map contextConfig, def cpe) {
   198  
   199      Set configKeys = ['docker', 'mode', 'action', 'dockerImage', 'dockerPullImage']
   200      Map config = ConfigurationHelper.newInstance(this)
   201          .loadStepDefaults()
   202          .mixinGeneralConfig(cpe, configKeys)
   203          .mixinStepConfig(cpe, configKeys)
   204          .mixinStageConfig(cpe, env.STAGE_NAME, configKeys)
   205          .mixin(parameters, configKeys)
   206          .use()
   207  
   208      def dockerImage = config.dockerImage ?: (projectConfig.dockerImage ?: (config.docker?.dockerImage ?: contextConfig.dockerImage))
   209      def dockerPullImage =  config.dockerPullImage ?: (projectConfig.dockerPullImage ?: (config.docker?.dockerPullImage ?: contextConfig.dockerPullImage))
   210      def mode = config.mode ?: projectConfig.mode
   211      def action = config.action ?: projectConfig.action
   212  
   213      [dockerImage: dockerImage, dockerPullImage: dockerPullImage, mode: mode, action: action]
   214  }