2019-07-18 15:06:11 +02:00
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GenerateDocumentation
import com.sap.piper.JenkinsUtils
import com.sap.piper.JsonUtils
import com.sap.piper.Utils
import com.sap.piper.integration.TransportManagementService
import groovy.transform.Field
import static com . sap . piper . Prerequisites . checkScript
@Field String STEP_NAME = getClass ( ) . getName ( )
2023-04-06 10:09:57 +02:00
@Field String METADATA_FILE = 'metadata/tmsUpload.yaml'
2019-07-18 15:06:11 +02:00
@Field Set GENERAL_CONFIG_KEYS = [
/ * *
* Print more detailed information into the log .
* @possibleValues ` true ` , ` false `
* /
'verbose'
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS . plus ( [
/ * *
* If specific stashes should be considered , their names need to be passed via the parameter ` stashContent ` .
* /
'stashContent' ,
/ * *
2020-08-31 09:06:18 +02:00
* Defines the relative path to * . mtar for the upload to the Transport Management Service . If not specified , it will use the mtar file created in mtaBuild .
2019-07-18 15:06:11 +02:00
* /
'mtaPath' ,
/ * *
* Defines the name of the node to which the * . mtar file should be uploaded .
* /
'nodeName' ,
2020-06-22 11:34:10 +02:00
/ * *
* Defines the version of the MTA for which the MTA extension descriptor will be used . You can use an asterisk ( * ) to accept any MTA version , or use a specific version compliant with SemVer 2.0 , e . g . 1.0 . 0 ( see semver . org ) . If the parameter is not configured , an asterisk is used .
* /
'mtaVersion' ,
/ * *
* Available only for transports in Cloud Foundry environment . Defines a mapping between a transport node name and an MTA extension descriptor file path that you want to use for the transport node , e . g . nodeExtDescriptorMapping: [ nodeName: 'example.mtaext' , nodeName2: 'example2.mtaext' , … ] ` .
* /
'nodeExtDescriptorMapping' ,
2019-07-18 15:06:11 +02:00
/ * *
* Credentials to be used for the file and node uploads to the Transport Management Service .
* /
'credentialsId' ,
/ * *
* Can be used as the description of a transport request . Will overwrite the default . ( Default: Corresponding Git Commit - ID )
* /
2019-12-03 15:55:53 +02:00
'customDescription' ,
/ * *
* Proxy which should be used for the communication with the Transport Management Service Backend .
* /
2023-04-06 10:09:57 +02:00
'proxy' ,
/ * *
2023-09-14 11:02:10 +02:00
* The new Golang implementation of the step is used now by default . Utilizing this toggle with value true is therefore redundant and can be omitted . If used with value false , the toggle deactivates the new Golang implementation and instructs the step to use the old Groovy one . Note that possibility to switch to the old Groovy implementation will be completely removed and this toggle will be deprecated after February 29 th , 2024 .
2023-04-06 10:09:57 +02:00
* @possibleValues true , false
* /
'useGoStep'
2019-07-18 15:06:11 +02:00
] )
2020-04-01 10:00:36 +02:00
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS + GENERAL_CONFIG_KEYS
2019-07-18 15:06:11 +02:00
/ * *
2022-11-29 10:52:23 +02:00
* This step allows you to upload an MTA file ( multi - target application archive ) and multiple MTA extension descriptors into a TMS ( SAP BTP Transport Management Service ) landscape for further TMS - controlled distribution through a TMS - configured landscape .
* TMS lets you manage transports between SAP BTP accounts in Neo and Cloud Foundry , such as from DEV to TEST and PROD accounts .
2019-07-18 15:06:11 +02:00
* For more information , see [ official documentation of Transport Management Service ] ( https: //help.sap.com/viewer/p/TRANSPORT_MANAGEMENT_SERVICE)
*
* ! ! ! note "Prerequisites"
2022-11-29 10:52:23 +02:00
* * You have subscribed to and set up TMS , as described in [ Setup and Configuration of SAP BTP Transport Management ] ( https: //help.sap.com/viewer/7f7160ec0d8546c6b3eab72fb5ad6fd8/Cloud/en-US/66fd7283c62f48adb23c56fb48c84a60.html), which includes the configuration of a node to be used for uploading an MTA file.
2019-07-18 15:06:11 +02:00
* * A corresponding service key has been created , as described in [ Set Up the Environment to Transport Content Archives directly in an Application ] ( https: //help.sap.com/viewer/7f7160ec0d8546c6b3eab72fb5ad6fd8/Cloud/en-US/8d9490792ed14f1bbf8a6ac08a6bca64.html). This service key (JSON) must be stored as a secret text within the Jenkins secure store.
*
* /
@GenerateDocumentation
void call ( Map parameters = [ : ] ) {
handlePipelineStepErrors ( stepName: STEP_NAME , stepParameters: parameters ) {
def script = checkScript ( this , parameters ) ? : this
def utils = parameters . juStabUtils ? : new Utils ( )
def jenkinsUtils = parameters . jenkinsUtilsStub ? : new JenkinsUtils ( )
2020-08-26 15:32:58 +02:00
String stageName = parameters . stageName ? : env . STAGE_NAME
2019-07-18 15:06:11 +02:00
// load default & individual configuration
Map config = ConfigurationHelper . newInstance ( this )
2020-08-26 15:32:58 +02:00
. loadStepDefaults ( [ : ] , stageName )
2019-07-18 15:06:11 +02:00
. mixinGeneralConfig ( script . commonPipelineEnvironment , GENERAL_CONFIG_KEYS )
. mixinStepConfig ( script . commonPipelineEnvironment , STEP_CONFIG_KEYS )
2020-08-26 15:32:58 +02:00
. mixinStageConfig ( script . commonPipelineEnvironment , stageName , STEP_CONFIG_KEYS )
2019-07-18 15:06:11 +02:00
. mixin ( parameters , PARAMETER_KEYS )
2020-07-15 08:32:05 +02:00
. addIfEmpty ( 'mtaPath' , script . commonPipelineEnvironment . mtarFilePath )
2019-07-18 15:06:11 +02:00
//mandatory parameters
. withMandatoryProperty ( 'mtaPath' )
. withMandatoryProperty ( 'nodeName' )
. withMandatoryProperty ( 'credentialsId' )
. use ( )
2023-04-06 10:09:57 +02:00
def namedUser = jenkinsUtils . getJobStartedByUserId ( )
2023-09-14 11:02:10 +02:00
if ( config . useGoStep ! = false ) {
2023-04-06 10:09:57 +02:00
List credentials = [
[ type: 'token' , id: 'credentialsId' , env: [ 'PIPER_tmsServiceKey' ] ]
]
if ( namedUser ) {
parameters . namedUser = namedUser
}
utils . unstashAll ( config . stashContent )
piperExecuteBin ( parameters , STEP_NAME , METADATA_FILE , credentials )
return
}
2023-09-14 11:02:10 +02:00
echo "[TransportManagementService] Using deprecated Groovy implementation of '${STEP_NAME}' step instead of the default Golang one, since 'useGoStep' toggle parameter is explicitly set to 'false'."
echo "[TransportManagementService] WARNING: Note that the deprecated Groovy implementation will be completely removed after February 29th, 2024. Consider using the Golang implementation by not setting the 'useGoStep' toggle parameter to 'false'."
2019-07-18 15:06:11 +02:00
// telemetry reporting
new Utils ( ) . pushToSWA ( [
step : STEP_NAME ,
stepParamKey1: 'scriptMissing' ,
stepParam1 : parameters ? . script = = null
] , config )
def jsonUtilsObject = new JsonUtils ( )
// make sure that all relevant descriptors, are available in workspace
utils . unstashAll ( config . stashContent )
// make sure that for further execution whole workspace, e.g. also downloaded artifacts are considered
config . stashContent = [ ]
def customDescription = config . customDescription ? "${config.customDescription}" : "Git CommitId: ${script.commonPipelineEnvironment.getGitCommitId()}"
def description = customDescription
2023-04-06 10:09:57 +02:00
if ( ! namedUser ) {
namedUser = config . namedUser
}
2019-07-18 15:06:11 +02:00
def nodeName = config . nodeName
def mtaPath = config . mtaPath
2020-06-22 11:34:10 +02:00
def mtaVersion = config . mtaVersion ? "${config.mtaVersion}" : "*"
Map nodeExtDescriptorMapping = ( config . nodeExtDescriptorMapping & & config . nodeExtDescriptorMapping . size ( ) > 0 ) ? config . nodeExtDescriptorMapping : null
2019-10-22 12:28:43 +02:00
if ( ! fileExists ( mtaPath ) ) {
error ( "Mta file '${mtaPath}' does not exist." )
}
2019-07-18 15:06:11 +02:00
if ( config . verbose ) {
echo "[TransportManagementService] CredentialsId: '${config.credentialsId}'"
echo "[TransportManagementService] Node name: '${nodeName}'"
echo "[TransportManagementService] MTA path: '${mtaPath}'"
echo "[TransportManagementService] Named user: '${namedUser}'"
}
def tms = parameters . transportManagementService ? : new TransportManagementService ( script , config )
withCredentials ( [ string ( credentialsId: config . credentialsId , variable: 'tmsServiceKeyJSON' ) ] ) {
def tmsServiceKey = jsonUtilsObject . jsonStringToGroovyObject ( tmsServiceKeyJSON )
def clientId = tmsServiceKey . uaa . clientid
def clientSecret = tmsServiceKey . uaa . clientsecret
def uaaUrl = tmsServiceKey . uaa . url
def uri = tmsServiceKey . uri
if ( config . verbose ) {
echo "[TransportManagementService] UAA URL: '${uaaUrl}'"
echo "[TransportManagementService] TMS URL: '${uri}'"
echo "[TransportManagementService] ClientId: '${clientId}'"
}
def token = tms . authentication ( uaaUrl , clientId , clientSecret )
2020-06-22 11:34:10 +02:00
if ( nodeExtDescriptorMapping ) {
// validate the whole mapping and then throw errors together,
// so that user can get them in one pipeline run
// put the validation here, because we need uri and token to call tms get nodes api
List nodes = tms . getNodes ( uri , token ) . getAt ( "nodes" ) ;
2021-12-14 15:43:02 +02:00
Map mtaYaml = getMtaYaml ( script . commonPipelineEnvironment . getValue ( 'mtaBuildToolDesc' ) ) ;
2020-06-22 11:34:10 +02:00
Map nodeIdExtDesMap = validateNodeExtDescriptorMapping ( nodeExtDescriptorMapping , nodes , mtaYaml , mtaVersion )
if ( nodeIdExtDesMap ) {
nodeIdExtDesMap . each { key , value - >
Map mtaExtDescriptor = tms . getMtaExtDescriptor ( uri , token , key , mtaYaml . ID , mtaVersion )
if ( mtaExtDescriptor ) {
def updateMtaExtDescriptorResponse = tms . updateMtaExtDescriptor ( uri , token , key , mtaExtDescriptor . getAt ( "id" ) , "${workspace}/${value.get(1)}" , mtaVersion , description , namedUser )
2020-09-24 07:41:06 +02:00
echo "[TransportManagementService] MTA Extension Descriptor with ID '${updateMtaExtDescriptorResponse.mtaExtId}' successfully updated for Node '${value.get(0)}'."
2020-06-22 11:34:10 +02:00
} else {
def uploadMtaExtDescriptorToNodeResponse = tms . uploadMtaExtDescriptorToNode ( uri , token , key , "${workspace}/${value.get(1)}" , mtaVersion , description , namedUser )
2020-09-24 07:41:06 +02:00
echo "[TransportManagementService] MTA Extension Descriptor with ID '${uploadMtaExtDescriptorToNodeResponse.mtaExtId}' successfully uploaded to Node '${value.get(0)}'."
2020-06-22 11:34:10 +02:00
}
}
}
}
2019-07-18 15:06:11 +02:00
def fileUploadResponse = tms . uploadFile ( uri , token , "${workspace}/${mtaPath}" , namedUser )
def uploadFileToNodeResponse = tms . uploadFileToNode ( uri , token , nodeName , fileUploadResponse . fileId , description , namedUser )
echo "[TransportManagementService] File '${fileUploadResponse.fileName}' successfully uploaded to Node '${uploadFileToNodeResponse.queueEntries.nodeName}' (Id: '${uploadFileToNodeResponse.queueEntries.nodeId}')."
echo "[TransportManagementService] Corresponding Transport Request: '${uploadFileToNodeResponse.transportRequestDescription}' (Id: '${uploadFileToNodeResponse.transportRequestId}')"
2020-06-22 11:34:10 +02:00
}
}
}
2019-07-18 15:06:11 +02:00
2020-06-22 11:34:10 +02:00
def String getMtaId ( String extDescriptorFilePath ) {
def extDescriptor = readYaml file: extDescriptorFilePath
def mtaId = ""
if ( extDescriptor . extends ) {
mtaId = extDescriptor . extends
}
return mtaId
}
2021-12-14 15:43:02 +02:00
def Map getMtaYaml ( String mtaBuildToolDesc ) {
mtaBuildToolDesc = mtaBuildToolDesc ? : "mta.yaml"
if ( fileExists ( mtaBuildToolDesc ) ) {
def mtaYaml = readYaml file: mtaBuildToolDesc
2020-06-22 11:34:10 +02:00
if ( ! mtaYaml . ID | | ! mtaYaml . version ) {
def errorMsg
if ( ! mtaYaml . ID ) {
2021-12-14 15:43:02 +02:00
errorMsg = "Property 'ID' is not found in ${mtaBuildToolDesc}."
2020-06-22 11:34:10 +02:00
}
if ( ! mtaYaml . version ) {
2021-12-14 15:43:02 +02:00
errorMsg + = "Property 'version' is not found in ${mtaBuildToolDesc}."
2020-06-22 11:34:10 +02:00
}
error errorMsg
}
return mtaYaml
} else {
2021-12-14 15:43:02 +02:00
error "${mtaBuildToolDesc} is not found in the root folder of the project."
2020-06-22 11:34:10 +02:00
}
}
def Map validateNodeExtDescriptorMapping ( Map nodeExtDescriptorMapping , List nodes , Map mtaYaml , String mtaVersion ) {
def errorPathList = [ ]
def errorMtaId = [ ]
def errorNodeNameList = [ ]
def errorMsg = ""
Map nodeIdExtDesMap = [ : ]
if ( mtaVersion ! = "*" & & mtaVersion ! = mtaYaml . version ) {
errorMsg = "Parameter 'mtaVersion' does not match the MTA version in mta.yaml. "
}
nodeExtDescriptorMapping . each { key , value - >
if ( nodes . any { it . name = = key } ) {
nodeIdExtDesMap . put ( nodes . find { it . name = = key } . getAt ( "id" ) , [ key , value ] )
} else {
errorNodeNameList . add ( key )
2019-07-18 15:06:11 +02:00
}
2020-06-22 11:34:10 +02:00
if ( ! fileExists ( value ) ) {
errorPathList . add ( value )
} else {
if ( mtaYaml . ID ! = getMtaId ( "${value}" ) ) {
errorMtaId . add ( value )
}
}
}
if ( ! errorPathList . isEmpty ( ) | | ! errorMtaId . isEmpty ( ) | | ! errorNodeNameList . isEmpty ( ) ) {
if ( ! errorPathList . isEmpty ( ) ) {
errorMsg + = "MTA extension descriptor files ${errorPathList} don't exist. "
}
if ( ! errorMtaId . isEmpty ( ) ) {
errorMsg + = "Parameter [extends] in MTA extension descriptor files ${errorMtaId} is not the same as MTA ID."
}
if ( ! errorNodeNameList . isEmpty ( ) ) {
errorMsg + = "Nodes ${errorNodeNameList} don't exist. Please check the node name or create these nodes."
}
error ( errorMsg )
2019-07-18 15:06:11 +02:00
}
2020-06-22 11:34:10 +02:00
return nodeIdExtDesMap
2019-07-18 15:06:11 +02:00
}