2020-02-25 11:20:15 +02:00
import com.sap.piper.BashUtils
import com.sap.piper.CfManifestUtils
import com.sap.piper.ConfigurationHelper
2019-03-28 15:38:25 +02:00
import com.sap.piper.GenerateDocumentation
2020-02-25 11:20:15 +02:00
import com.sap.piper.JenkinsUtils
2018-07-30 09:28:24 +02:00
import com.sap.piper.Utils
import groovy.transform.Field
2020-02-25 11:20:15 +02:00
import static com . sap . piper . Prerequisites . checkScript
2018-11-29 10:54:05 +02:00
@Field String STEP_NAME = getClass ( ) . getName ( )
2018-10-25 14:24:17 +02:00
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
2018-07-30 09:28:24 +02:00
@Field Set STEP_CONFIG_KEYS = [
'cloudFoundry' ,
2019-03-28 15:38:25 +02:00
/ * *
* Cloud Foundry API endpoint .
* @parentConfigKey cloudFoundry
* /
'apiEndpoint' ,
/ * *
* Defines the name of the application to be deployed to the Cloud Foundry space .
* @parentConfigKey cloudFoundry
* /
'appName' ,
/ * *
* Credentials to be used for deployment .
* @parentConfigKey cloudFoundry
* /
'credentialsId' ,
/ * *
* Defines the manifest to be used for deployment to Cloud Foundry .
* @parentConfigKey cloudFoundry
* /
'manifest' ,
2019-09-24 08:49:25 +02:00
/ * *
* Defines the manifest variables Yaml files to be used to replace variable references in manifest . This parameter
* is optional and will default to ` [ "manifest-variables.yml" ] ` . This can be used to set variable files like it
* is provided by ` cf push - - vars - file < file > ` .
*
* If the manifest is present and so are all variable files , a variable substitution will be triggered that uses
* the ` cfManifestSubstituteVariables ` step before deployment . The format of variable references follows the
* [ Cloud Foundry standard ] ( https: //docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#variable-substitution).
* @parentConfigKey cloudFoundry
* /
'manifestVariablesFiles' ,
/ * *
* Defines a ` List ` of variables as key - value ` Map ` objects used for variable substitution within the file given by ` manifest ` .
* Defaults to an empty list , if not specified otherwise . This can be used to set variables like it is provided
* by ` cf push - - var key = value ` .
*
* The order of the maps of variables given in the list is relevant in case there are conflicting variable names and values
* between maps contained within the list . In case of conflicts , the last specified map in the list will win .
*
* Though each map entry in the list can contain more than one key - value pair for variable substitution , it is recommended
* to stick to one entry per map , and rather declare more maps within the list . The reason is that
* if a map in the list contains more than one key - value entry , and the entries are conflicting , the
* conflict resolution behavior is undefined ( since map entries have no sequence ) .
*
* Note: variables defined via ` manifestVariables ` always win over conflicting variables defined via any file given
* by ` manifestVariablesFiles ` - no matter what is declared before . This is the same behavior as can be
* observed when using ` cf push - - var ` in combination with ` cf push - - vars - file ` .
* /
'manifestVariables' ,
2019-03-28 15:38:25 +02:00
/ * *
* Cloud Foundry target organization .
* @parentConfigKey cloudFoundry
* /
'org' ,
/ * *
* Cloud Foundry target space .
* @parentConfigKey cloudFoundry
* /
'space' ,
/ * *
* Defines the tool which should be used for deployment .
* @possibleValues 'cf_native' , 'mtaDeployPlugin'
* /
2018-07-30 09:28:24 +02:00
'deployTool' ,
2019-03-28 15:38:25 +02:00
/ * *
* Defines the type of deployment , either ` standard ` deployment which results in a system downtime or a zero - downtime ` blue - green ` deployment .
2020-02-07 19:46:03 +02:00
* If 'cf_native' as deployType and 'blue-green' as deployTool is used in combination , your manifest . yaml may only contain one application .
* If this application has the option 'no-route' active the deployType will be changed to 'standard' .
2019-03-28 15:38:25 +02:00
* @possibleValues 'standard' , 'blue-green'
* /
2018-07-30 09:28:24 +02:00
'deployType' ,
2019-03-28 15:38:25 +02:00
/ * *
* In case of a ` blue - green ` deployment the old instance will be deleted by default . If this option is set to true the old instance will remain stopped in the Cloud Foundry space .
* @possibleValues true , false
* /
2018-11-27 12:47:44 +02:00
'keepOldInstance' ,
2019-03-28 15:38:25 +02:00
/** @see dockerExecute */
2018-07-30 09:28:24 +02:00
'dockerImage' ,
2019-03-28 15:38:25 +02:00
/** @see dockerExecute */
2018-07-30 09:28:24 +02:00
'dockerWorkspace' ,
2019-03-28 15:38:25 +02:00
/** @see dockerExecute */
'stashContent' ,
/ * *
2019-10-02 13:28:54 +02:00
* Additional parameters passed to cf native deployment command .
* /
'cfNativeDeployParameters' ,
/ * *
* Addition command line options for cf api command .
* No escaping / quoting is performed . Not recommanded for productive environments .
* /
'apiParameters' ,
/ * *
* Addition command line options for cf login command .
* No escaping / quoting is performed . Not recommanded for productive environments .
* /
'loginParameters' ,
/ * *
* Additional parameters passed to mta deployment command .
2019-03-28 15:38:25 +02:00
* /
2018-07-30 09:28:24 +02:00
'mtaDeployParameters' ,
2019-03-28 15:38:25 +02:00
/ * *
* Defines additional extension descriptor file for deployment with the mtaDeployPlugin .
* /
2018-07-30 09:28:24 +02:00
'mtaExtensionDescriptor' ,
2019-03-28 15:38:25 +02:00
/ * *
* Defines the path to * . mtar for deployment with the mtaDeployPlugin .
* /
2018-07-30 09:28:24 +02:00
'mtaPath' ,
2019-03-28 15:38:25 +02:00
/ * *
* Allows to specify a script which performs a check during blue - green deployment . The script gets the FQDN as parameter and returns ` exit code 0 ` in case check returned ` smokeTestStatusCode ` .
* More details can be found [ here ] ( https: //github.com/bluemixgaragelondon/cf-blue-green-deploy#how-to-use) <br /> Currently this option is only considered for deployTool `cf_native`.
* /
2018-07-30 09:28:24 +02:00
'smokeTestScript' ,
2019-03-28 15:38:25 +02:00
/ * *
* Expected status code returned by the check .
* /
2019-09-27 16:10:43 +02:00
'smokeTestStatusCode' ,
2019-10-02 13:28:54 +02:00
/ * *
2020-02-25 11:20:15 +02:00
* Provides more output . May reveal sensitive information .
* @possibleValues true , false
* /
2019-09-27 16:10:43 +02:00
'verbose' ,
2020-02-25 11:20:15 +02:00
/ * *
* Docker image deployments are supported ( via manifest file in general ) [ https: //docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#docker].
* If no manifest is used , this parameter defines the image to be deployed . The specified name of the image is
* passed to the ` - - docker - image ` parameter of the cf CLI and must adhere it ' s naming pattern ( e . g . REPO / IMAGE: TAG ) .
* See ( cf CLI documentation ) [ https: //docs.cloudfoundry.org/devguide/deploy-apps/push-docker.html] for details.
*
* Note: The used Docker registry must be visible for the targeted Cloud Foundry instance .
* /
'deployDockerImage' ,
/ * *
* If the specified image in ` deployDockerImage ` is contained in a Docker registry , which requires authorization
* this defines the credentials to be used .
* /
'dockerCredentialsId' ,
2019-03-28 15:38:25 +02:00
]
2019-09-24 08:49:25 +02:00
@Field Map CONFIG_KEY_COMPATIBILITY = [ cloudFoundry: [ apiEndpoint: 'cfApiEndpoint' , appName: 'cfAppName' , credentialsId: 'cfCredentialsId' , manifest: 'cfManifest' , manifestVariablesFiles: 'cfManifestVariablesFiles' , manifestVariables: 'cfManifestVariables' , org: 'cfOrg' , space: 'cfSpace' ] ]
2018-10-25 14:24:17 +02:00
2018-07-30 09:28:24 +02:00
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
2019-03-28 15:38:25 +02:00
/ * *
* Deploys an application to a test or production space within Cloud Foundry .
* Deployment can be done
*
* * in a standard way
* * in a zero downtime manner ( using a [ blue - green deployment approach ] ( https: //martinfowler.com/bliki/BlueGreenDeployment.html))
*
* ! ! ! note "Deployment supports multiple deployment tools"
* Currently the following are supported:
*
* * Standard ` cf push ` and [ Bluemix blue - green plugin ] ( https: //github.com/bluemixgaragelondon/cf-blue-green-deploy#how-to-use)
* * [ MTA CF CLI Plugin ] ( https: //github.com/cloudfoundry-incubator/multiapps-cli-plugin)
*
* ! ! ! note
* Due to [ an incompatible change ] ( https: //github.com/cloudfoundry/cli/issues/1445) in the Cloud Foundry CLI, multiple buildpacks are not supported by this step.
* If your ` application ` contains a list of ` buildpacks ` instead a single ` buildpack ` , this will be automatically re - written by the step when blue - green deployment is used .
*
* ! ! ! note
* Cloud Foundry supports the deployment of multiple applications using a single manifest file .
* This option is supported with Piper .
*
* In this case define ` appName: '' ` since the app name for the individual applications have to be defined via the manifest .
* You can find details in the [ Cloud Foundry Documentation ] ( https: //docs.cloudfoundry.org/devguide/deploy-apps/manifest.html#multi-apps)
* /
@GenerateDocumentation
2018-08-30 16:33:07 +02:00
void call ( Map parameters = [ : ] ) {
2018-07-30 09:28:24 +02:00
handlePipelineStepErrors ( stepName: STEP_NAME , stepParameters: parameters ) {
2019-01-18 09:25:22 +02:00
def utils = parameters . juStabUtils ? : new Utils ( )
def jenkinsUtils = parameters . jenkinsUtilsStub ? : new JenkinsUtils ( )
2018-07-30 09:28:24 +02:00
2019-02-07 09:58:00 +02:00
final script = checkScript ( this , parameters ) ? : this
2018-07-30 09:28:24 +02:00
2018-10-17 11:05:20 +02:00
Map config = ConfigurationHelper . newInstance ( this )
2018-09-07 10:08:16 +02:00
. loadStepDefaults ( )
2018-10-25 14:24:17 +02:00
. mixinGeneralConfig ( script . commonPipelineEnvironment , GENERAL_CONFIG_KEYS , CONFIG_KEY_COMPATIBILITY )
2018-09-06 16:45:30 +02:00
. mixinStepConfig ( script . commonPipelineEnvironment , STEP_CONFIG_KEYS , CONFIG_KEY_COMPATIBILITY )
. mixinStageConfig ( script . commonPipelineEnvironment , parameters . stageName ? : env . STAGE_NAME , STEP_CONFIG_KEYS , CONFIG_KEY_COMPATIBILITY )
. mixin ( parameters , PARAMETER_KEYS , CONFIG_KEY_COMPATIBILITY )
2018-07-30 09:28:24 +02:00
. dependingOn ( 'deployTool' ) . mixin ( 'dockerImage' )
. dependingOn ( 'deployTool' ) . mixin ( 'dockerWorkspace' )
. withMandatoryProperty ( 'cloudFoundry/org' )
. withMandatoryProperty ( 'cloudFoundry/space' )
. withMandatoryProperty ( 'cloudFoundry/credentialsId' )
. use ( )
2019-01-21 09:47:34 +02:00
utils . pushToSWA ( [
step: STEP_NAME ,
stepParamKey1: 'deployTool' ,
stepParam1: config . deployTool ,
stepParamKey2: 'deployType' ,
stepParam2: config . deployType ,
stepParamKey3: 'scriptMissing' ,
stepParam3: parameters ? . script = = null
] , config )
2018-08-09 11:35:33 +02:00
2019-03-28 15:38:25 +02:00
echo "[${STEP_NAME}] General parameters: deployTool=${config.deployTool}, deployType=${config.deployType}, cfApiEndpoint=${config.cloudFoundry.apiEndpoint}, cfOrg=${config.cloudFoundry.org}, cfSpace=${config.cloudFoundry.space}, cfCredentialsId=${config.cloudFoundry.credentialsId}"
2018-07-30 09:28:24 +02:00
2018-11-27 17:02:06 +02:00
//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
2018-11-27 17:29:38 +02:00
config . stashContent = [ ]
2018-07-30 09:28:24 +02:00
2019-09-20 09:57:28 +02:00
boolean deployTriggered = false
2019-01-18 09:25:22 +02:00
boolean deploySuccess = true
try {
if ( config . deployTool = = 'mtaDeployPlugin' ) {
2019-09-20 09:57:28 +02:00
deployTriggered = true
handleMTADeployment ( config , script )
2018-07-30 09:28:24 +02:00
}
2019-09-20 09:57:28 +02:00
else if ( config . deployTool = = 'cf_native' ) {
deployTriggered = true
handleCFNativeDeployment ( config , script )
}
else {
deployTriggered = false
echo "[${STEP_NAME}] WARNING! Found unsupported deployTool. Skipping deployment."
2019-01-18 09:25:22 +02:00
}
} catch ( err ) {
deploySuccess = false
throw err
} finally {
2019-09-20 09:57:28 +02:00
if ( deployTriggered ) {
2019-01-18 09:25:22 +02:00
reportToInflux ( script , config , deploySuccess , jenkinsUtils )
2018-07-30 09:28:24 +02:00
}
}
2019-01-18 09:25:22 +02:00
2018-07-30 09:28:24 +02:00
}
}
2019-09-20 09:57:28 +02:00
private void handleMTADeployment ( Map config , script ) {
// set default mtar path
config = ConfigurationHelper . newInstance ( this , config )
. addIfEmpty ( 'mtaPath' , config . mtaPath ? : findMtar ( ) )
. use ( )
dockerExecute ( script: script , dockerImage: config . dockerImage , dockerWorkspace: config . dockerWorkspace , stashContent: config . stashContent ) {
deployMta ( config )
}
}
2018-07-30 09:28:24 +02:00
def findMtar ( ) {
2019-02-07 09:58:00 +02:00
def mtarFiles = findFiles ( glob: '**/*.mtar' )
2018-07-30 09:28:24 +02:00
if ( mtarFiles . length > 1 ) {
2020-02-07 19:46:03 +02:00
error "[${STEP_NAME}] Found multiple *.mtar files, please specify file via mtaPath parameter! ${mtarFiles}"
2018-07-30 09:28:24 +02:00
}
if ( mtarFiles . length = = 1 ) {
return mtarFiles [ 0 ] . path
}
2020-02-07 19:46:03 +02:00
error "[${STEP_NAME}] No *.mtar file found!"
2018-07-30 09:28:24 +02:00
}
2020-02-25 11:20:15 +02:00
def deployMta ( config ) {
2019-09-20 09:57:28 +02:00
if ( config . mtaExtensionDescriptor = = null ) config . mtaExtensionDescriptor = ''
if ( ! config . mtaExtensionDescriptor . isEmpty ( ) & & ! config . mtaExtensionDescriptor . startsWith ( '-e ' ) ) config . mtaExtensionDescriptor = "-e ${config.mtaExtensionDescriptor}"
def deployCommand = 'deploy'
if ( config . deployType = = 'blue-green' ) {
deployCommand = 'bg-deploy'
if ( config . mtaDeployParameters . indexOf ( '--no-confirm' ) < 0 ) {
config . mtaDeployParameters + = ' --no-confirm'
}
}
2019-10-22 13:53:08 +02:00
def deployStatement = "cf ${deployCommand} ${config.mtaPath} ${config.mtaDeployParameters} ${config.mtaExtensionDescriptor}"
def apiStatement = "cf api ${config.cloudFoundry.apiEndpoint} ${config.apiParameters}"
2019-10-02 13:28:54 +02:00
2019-10-22 13:53:08 +02:00
echo "[${STEP_NAME}] Deploying MTA (${config.mtaPath}) with following parameters: ${config.mtaExtensionDescriptor} ${config.mtaDeployParameters}"
deploy ( apiStatement , deployStatement , config , null )
2018-07-30 09:28:24 +02:00
}
2020-02-07 19:46:03 +02:00
private checkAndUpdateDeployTypeForNotSupportedManifest ( Map config ) {
String manifestFile = config . cloudFoundry . manifest ? : 'manifest.yml'
if ( config . deployType = = 'blue-green' & & fileExists ( manifestFile ) ) {
Map manifest = readYaml file: manifestFile
List applications = manifest . applications
if ( applications ) {
if ( applications . size ( ) > 1 ) {
error "[${STEP_NAME}] Your manifest contains more than one application. For blue green deployments your manifest file may contain only one application."
}
if ( applications . size = = 1 & & applications [ 0 ] [ 'no-route' ] ) {
echo '[WARNING] Blue green deployment is not possible for application without route. Using deployment type "standard" instead.'
config . deployType = 'standard'
}
}
}
}
2019-09-20 09:57:28 +02:00
private void handleCFNativeDeployment ( Map config , script ) {
config . smokeTest = ''
2020-02-07 19:46:03 +02:00
checkAndUpdateDeployTypeForNotSupportedManifest ( config )
2018-11-27 12:47:44 +02:00
if ( config . deployType = = 'blue-green' ) {
2020-02-25 11:20:15 +02:00
prepareBlueGreenCfNativeDeploy ( config , script )
2018-11-27 12:47:44 +02:00
} else {
2019-09-20 09:57:28 +02:00
prepareCfPushCfNativeDeploy ( config )
2018-11-27 12:47:44 +02:00
}
2019-09-20 09:57:28 +02:00
echo "[${STEP_NAME}] CF native deployment (${config.deployType}) with:"
echo "[${STEP_NAME}] - cfAppName=${config.cloudFoundry.appName}"
echo "[${STEP_NAME}] - cfManifest=${config.cloudFoundry.manifest}"
2020-02-25 11:20:15 +02:00
echo "[${STEP_NAME}] - cfManifestVariables=${config.cloudFoundry.manifestVariables ?: 'none specified'}"
echo "[${STEP_NAME}] - cfManifestVariablesFiles=${config.cloudFoundry.manifestVariablesFiles ?: 'none specified'}"
echo "[${STEP_NAME}] - cfdeployDockerImage=${config.deployDockerImage ?: 'none specified'}"
echo "[${STEP_NAME}] - cfdockerCredentialsId=${config.dockerCredentialsId ?: 'none specified'}"
2019-09-20 09:57:28 +02:00
echo "[${STEP_NAME}] - smokeTestScript=${config.smokeTestScript}"
checkIfAppNameIsAvailable ( config )
2020-02-25 11:20:15 +02:00
def dockerCredentials = [ ]
if ( config . dockerCredentialsId ! = null & & config . dockerCredentialsId ! = '' ) {
dockerCredentials . add ( usernamePassword (
credentialsId: config . dockerCredentialsId ,
passwordVariable: 'dockerPassword' ,
usernameVariable: 'dockerUsername'
) )
}
withCredentials ( dockerCredentials ) {
dockerExecute (
script: script ,
dockerImage: config . dockerImage ,
dockerWorkspace: config . dockerWorkspace ,
stashContent: config . stashContent ,
dockerEnvVars: [
CF_HOME : "${config.dockerWorkspace}" ,
CF_PLUGIN_HOME : "${config.dockerWorkspace}" ,
// if the Docker registry requires authentication the DOCKER_PASSWORD env variable must be set
CF_DOCKER_PASSWORD: "${binding.hasVariable(" dockerPassword ") ? dockerPassword : ''}" ,
STATUS_CODE : "${config.smokeTestStatusCode}"
]
) {
deployCfNative ( config )
}
2018-11-27 12:47:44 +02:00
}
}
2019-09-24 08:49:25 +02:00
private prepareBlueGreenCfNativeDeploy ( config , script ) {
2019-09-20 09:57:28 +02:00
if ( config . smokeTestScript = = 'blueGreenCheckScript.sh' ) {
writeFile file: config . smokeTestScript , text: libraryResource ( config . smokeTestScript )
}
2019-01-30 11:07:00 +02:00
2019-09-20 09:57:28 +02:00
config . smokeTest = '--smoke-test $(pwd)/' + config . smokeTestScript
sh "chmod +x ${config.smokeTestScript}"
2019-01-30 11:07:00 +02:00
2019-09-20 09:57:28 +02:00
config . deployCommand = 'blue-green-deploy'
2019-09-24 08:49:25 +02:00
cfManifestSubstituteVariables (
script: script ,
manifestFile: config . cloudFoundry . manifest ,
manifestVariablesFiles: config . cloudFoundry . manifestVariablesFiles ,
manifestVariables: config . cloudFoundry . manifestVariables
)
2019-09-20 09:57:28 +02:00
handleLegacyCfManifest ( config )
if ( ! config . keepOldInstance ) {
config . deployOptions = '--delete-old-apps'
}
}
2019-01-30 11:07:00 +02:00
2019-09-20 09:57:28 +02:00
def handleLegacyCfManifest ( config ) {
def manifest = readYaml file: config . cloudFoundry . manifest
String originalManifest = manifest . toString ( )
manifest = CfManifestUtils . transform ( manifest )
String transformedManifest = manifest . toString ( )
if ( originalManifest ! = transformedManifest ) {
echo "" " The file $ { config . cloudFoundry . manifest } is not compatible with the Cloud Foundry blue - green deployment plugin . Re - writing inline .
See this issue if you are interested in the background: https: //github.com/cloudfoundry/cli/issues/1445.\n
Original manifest file content: $originalManifest \ n
Transformed manifest file content: $transformedManifest "" "
sh "rm ${config.cloudFoundry.manifest}"
writeYaml file: config . cloudFoundry . manifest , data: manifest
2018-11-27 12:47:44 +02:00
}
}
2019-09-20 09:57:28 +02:00
private prepareCfPushCfNativeDeploy ( config ) {
config . deployCommand = 'push'
2019-09-24 08:49:25 +02:00
config . deployOptions = "${varOptions(config)}${varFileOptions(config)}"
}
private varOptions ( Map config ) {
String varPart = ''
if ( config . cloudFoundry . manifestVariables ) {
if ( ! ( config . cloudFoundry . manifestVariables in List ) ) {
error "[${STEP_NAME}] ERROR: Parameter config.cloudFoundry.manifestVariables is not a List!"
}
config . cloudFoundry . manifestVariables . each {
if ( ! ( it in Map ) ) {
error "[${STEP_NAME}] ERROR: Parameter config.cloudFoundry.manifestVariables.$it is not a Map!"
}
it . keySet ( ) . each { varKey - >
String varValue = BashUtils . quoteAndEscape ( it . get ( varKey ) . toString ( ) )
varPart + = " --var $varKey=$varValue"
}
}
}
if ( varPart ) echo "We will add the following string to the cf push call:$varPart !"
return varPart
}
private String varFileOptions ( Map config ) {
String varFilePart = ''
if ( config . cloudFoundry . manifestVariablesFiles ) {
if ( ! ( config . cloudFoundry . manifestVariablesFiles in List ) ) {
error "[${STEP_NAME}] ERROR: Parameter config.cloudFoundry.manifestVariablesFiles is not a List!"
}
config . cloudFoundry . manifestVariablesFiles . each {
if ( fileExists ( it ) ) {
varFilePart + = " --vars-file ${BashUtils.quoteAndEscape(it)}"
} else {
echo "[${STEP_NAME}] [WARNING] We skip adding not-existing file '$it' as a vars-file to the cf create-service-push call"
}
}
}
if ( varFilePart ) echo "We will add the following string to the cf push call:$varFilePart !"
return varFilePart
2019-09-20 09:57:28 +02:00
}
2018-07-30 09:28:24 +02:00
2019-09-20 09:57:28 +02:00
private checkIfAppNameIsAvailable ( config ) {
if ( config . cloudFoundry . appName = = null | | config . cloudFoundry . appName = = '' ) {
if ( config . deployType = = 'blue-green' ) {
error "[${STEP_NAME}] ERROR: Blue-green plugin requires app name to be passed (see https://github.com/bluemixgaragelondon/cf-blue-green-deploy/issues/27)"
}
if ( fileExists ( config . cloudFoundry . manifest ) ) {
def manifest = readYaml file: config . cloudFoundry . manifest
if ( ! manifest | | ! manifest . applications | | ! manifest . applications [ 0 ] . name ) {
error "[${STEP_NAME}] ERROR: No appName available in manifest ${config.cloudFoundry.manifest}."
}
} else {
error "[${STEP_NAME}] ERROR: No manifest file ${config.cloudFoundry.manifest} found."
2019-01-22 17:13:59 +02:00
}
}
2019-09-20 09:57:28 +02:00
}
2018-07-30 09:28:24 +02:00
2020-02-25 11:20:15 +02:00
def deployCfNative ( config ) {
// the deployStatement is complex and has lot of options; using a list and findAll allows to put each option
// as a single list element; if a option is not set (= null or '') this removed before every element is joined
// via a single whitespace; results in a single line deploy statement
def deployStatement = [
'cf' ,
config . deployCommand ,
config . cloudFoundry . appName ,
config . deployOptions ,
config . cloudFoundry . manifest ? "-f '${config.cloudFoundry.manifest}'" : null ,
config . deployDockerImage ? "--docker-image ${config.deployDockerImage}" : null ,
binding . hasVariable ( "dockerUsername" ) ? "--docker-username ${dockerUsername}}" : null ,
config . smokeTest ,
config . cfNativeDeployParameters
] . findAll { s - > s ! = null & & s ! = '' } . join ( " " )
deploy ( null , deployStatement , config , { c - > stopOldAppIfRunning ( c ) } )
2019-10-22 13:53:08 +02:00
}
2020-02-25 11:20:15 +02:00
private deploy ( String cfApiStatement , String cfDeployStatement , config , Closure postDeployAction ) {
2019-10-22 13:53:08 +02:00
2018-07-30 09:28:24 +02:00
withCredentials ( [ usernamePassword (
credentialsId: config . cloudFoundry . credentialsId ,
passwordVariable: 'password' ,
usernameVariable: 'username'
) ] ) {
2019-09-27 16:10:43 +02:00
def cfTraceFile = 'cf.log'
2019-10-02 13:28:54 +02:00
def deployScript = "" " # ! /bin/ bash
2018-07-30 09:28:24 +02:00
set + x
2019-01-07 13:54:00 +02:00
set - e
2019-09-20 09:57:28 +02:00
export HOME = $ { config . dockerWorkspace }
2019-09-27 16:10:43 +02:00
export CF_TRACE = $ { cfTraceFile }
2019-10-22 13:53:08 +02:00
$ { cfApiStatement ? : '' }
2019-10-02 13:28:54 +02:00
cf login - u \ "${username}\" -p '${password}' -a ${config.cloudFoundry.apiEndpoint} -o \"${config.cloudFoundry.org}\" -s \"${config.cloudFoundry.space}\" $ { config . loginParameters }
2018-07-30 09:28:24 +02:00
cf plugins
2019-10-22 13:53:08 +02:00
$ { cfDeployStatement }
2019-09-20 09:57:28 +02:00
"" "
2020-02-25 11:20:15 +02:00
if ( config . verbose ) {
2019-10-02 13:28:54 +02:00
// Password contained in output below is hidden by withCredentials
echo "[INFO][${STEP_NAME}] Executing command: '${deployScript}'."
}
2019-10-22 13:53:08 +02:00
2020-02-14 14:41:36 +02:00
try {
sh deployScript
} catch ( e ) {
handleCfCliLog ( cfTraceFile )
2019-09-27 16:10:43 +02:00
2019-09-20 09:57:28 +02:00
error "[${STEP_NAME}] ERROR: The execution of the deploy command failed, see the log for details."
2019-04-12 09:07:53 +02:00
}
2020-02-25 11:20:15 +02:00
if ( config . verbose ) {
2020-02-14 14:41:36 +02:00
handleCfCliLog ( cfTraceFile )
}
2019-10-22 13:53:08 +02:00
2020-02-25 11:20:15 +02:00
if ( postDeployAction ) postDeployAction ( config )
2019-10-22 13:53:08 +02:00
2018-07-30 09:28:24 +02:00
sh "cf logout"
}
}
2018-10-17 11:01:09 +02:00
2020-02-14 14:41:36 +02:00
private void handleCfCliLog ( String logFile ) {
if ( fileExists ( file: logFile ) ) {
echo '### START OF CF CLI TRACE OUTPUT ###'
// Would be nice to inline the two next lines, but that is not understood by the test framework
def cfTrace = readFile ( file: logFile )
echo cfTrace
echo '### END OF CF CLI TRACE OUTPUT ###'
} else {
echo "No trace file found at '${logFile}'"
}
}
2019-09-20 09:57:28 +02:00
private void stopOldAppIfRunning ( Map config ) {
String oldAppName = "${config.cloudFoundry.appName}-old"
String cfStopOutputFileName = "${UUID.randomUUID()}-cfStopOutput.txt"
if ( config . keepOldInstance & & config . deployType = = 'blue-green' ) {
2020-02-14 14:41:36 +02:00
try {
sh "cf stop $oldAppName &> $cfStopOutputFileName"
} catch ( e ) {
2019-09-20 09:57:28 +02:00
String cfStopOutput = readFile ( file: cfStopOutputFileName )
if ( ! cfStopOutput . contains ( "$oldAppName not found" ) ) {
error "[${STEP_NAME}] ERROR: Could not stop application $oldAppName. Error: $cfStopOutput"
}
}
2018-10-17 11:01:09 +02:00
}
}
2019-01-18 09:25:22 +02:00
private void reportToInflux ( script , config , deploySuccess , JenkinsUtils jenkinsUtils ) {
def deployUser = ''
withCredentials ( [ usernamePassword (
credentialsId: config . cloudFoundry . credentialsId ,
passwordVariable: 'password' ,
usernameVariable: 'username'
) ] ) {
deployUser = username
}
def timeFinished = new Date ( ) . format ( 'MMM dd, yyyy - HH:mm:ss' )
def triggerCause = jenkinsUtils . isJobStartedByUser ( ) ? 'USER' : ( jenkinsUtils . isJobStartedByTimer ( ) ? 'TIMER' : 'OTHER' )
def deploymentData = [ deployment_data: [
artifactUrl: 'n/a' , //might be added later on during pipeline run (written to commonPipelineEnvironment)
deployTime: timeFinished ,
jobTrigger: triggerCause
] ]
def deploymentDataTags = [ deployment_data: [
artifactVersion: script . commonPipelineEnvironment . getArtifactVersion ( ) ,
deployUser: deployUser ,
deployResult: deploySuccess ? 'SUCCESS' : 'FAILURE' ,
cfApiEndpoint: config . cloudFoundry . apiEndpoint ,
cfOrg: config . cloudFoundry . org ,
cfSpace: config . cloudFoundry . space ,
] ]
2019-01-18 16:33:36 +02:00
influxWriteData script: script , customData: [ : ] , customDataTags: [ : ] , customDataMap: deploymentData , customDataMapTags: deploymentDataTags
2019-01-18 09:25:22 +02:00
}