import com.sap.piper.GenerateDocumentation import com.sap.piper.CloudPlatform import com.sap.piper.DeploymentType import com.sap.piper.ConfigurationHelper import com.sap.piper.Utils import com.sap.piper.JenkinsUtils import com.sap.piper.k8s.ContainerMap import groovy.transform.Field import static com.sap.piper.Prerequisites.checkScript @Field String STEP_NAME = getClass().getName() @Field Set GENERAL_CONFIG_KEYS = [ /** Defines the targets to deploy on Cloud Foundry.*/ 'cfTargets', /** Defines the targets to deploy on neo.*/ 'neoTargets', /** Executes the deployments in parallel.*/ 'parallelExecution' ] @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ /** * Defines Cloud Foundry service instances to create as part of the deployment. * This is a _list_ of _objects_ with the following properties each: * - apiEndpoint * - credentialsId * - serviceManifest * - manifestVariablesFiles * - org * - space */ 'cfCreateServices', /** Defines the deployment type.*/ 'enableZeroDowntimeDeployment', ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([ /** The source file to deploy to SAP Cloud Platform.*/ 'source', /** Closure which is executed before calling the deployment steps.*/ 'preDeploymentHook' ]) @Field Map CONFIG_KEY_COMPATIBILITY = [parallelExecution: 'features/parallelTestExecution'] /** * Deploys an application to multiple platforms (Cloud Foundry, SAP Cloud Platform) or to multiple instances of multiple platforms or the same platform. */ @GenerateDocumentation void call(parameters = [:]) { handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { def script = checkScript(this, parameters) ?: this def utils = parameters.utils ?: new Utils() def jenkinsUtils = parameters.jenkinsUtils ?: new JenkinsUtils() String stageName = parameters.stage ?: env.STAGE_NAME ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this) .loadStepDefaults([:], stageName) .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY) .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY) .mixinStageConfig(script.commonPipelineEnvironment, stageName, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY) .mixin(parameters, PARAMETER_KEYS) Map config = configHelper.use() utils.pushToSWA([ step : STEP_NAME, stepParamKey1: 'enableZeroDowntimeDeployment', stepParam1 : config.enableZeroDowntimeDeployment ], config) def index = 1 def deployments = [:] if (config.cfCreateServices) { def createServices = [:] for (int i = 0; i < config.cfCreateServices.size(); i++) { Map createServicesConfig = config.cfCreateServices[i] createServices["Service Creation ${i + 1}"] = { cloudFoundryCreateService( script: script, cloudFoundry: [ apiEndpoint : createServicesConfig.apiEndpoint, credentialsId : createServicesConfig.credentialsId, serviceManifest : createServicesConfig.serviceManifest, manifestVariablesFiles: createServicesConfig.manifestVariablesFiles, org : createServicesConfig.org, space : createServicesConfig.space ] ) } } runClosures(script, createServices, config.parallelExecution, "cloudFoundryCreateService") } if (config.cfTargets) { def deploymentType = DeploymentType.selectFor(CloudPlatform.CLOUD_FOUNDRY, config.enableZeroDowntimeDeployment).toString() // An isolated workspace is required when using blue-green deployment with multiple cfTargets, // since the cloudFoundryDeploy step might edit the manifest.yml file in that case. // It is also required in case of parallel execution and use of mtaExtensionCredentials, since the // credentials are inserted in the mtaExtensionDescriptor file. Boolean runInIsolatedWorkspace = config.cfTargets.size() > 1 && (deploymentType == "blue-green" || config.parallelExecution) for (int i = 0; i < config.cfTargets.size(); i++) { def target = config.cfTargets[i] Closure deployment = { Utils deploymentUtils = new Utils() if (runInIsolatedWorkspace) { deploymentUtils.unstashStageFiles(script, stageName) } if (config.preDeploymentHook) { config.preDeploymentHook.call() } cloudFoundryDeploy( script: script, juStabUtils: utils, jenkinsUtilsStub: jenkinsUtils, deployType: deploymentType, cloudFoundry: target, mtaExtensionDescriptor: target.mtaExtensionDescriptor, mtaExtensionCredentials: target.mtaExtensionCredentials ) if (runInIsolatedWorkspace) { deploymentUtils.stashStageFiles(script, stageName) } } if (runInIsolatedWorkspace){ deployments["Deployment ${index}"] = { if (env.POD_NAME) { dockerExecuteOnKubernetes(script: script, containerMap: ContainerMap.instance.getMap().get(stageName) ?: [:]) { deployment.call() } } else { node(env.NODE_NAME) { deployment.call() } } } } else { deployments.put("Deployment ${index}", deployment) } index++ } } if (config.neoTargets) { def deploymentType = DeploymentType.selectFor(CloudPlatform.NEO, config.enableZeroDowntimeDeployment).toString() for (int i = 0; i < config.neoTargets.size(); i++) { def target = config.neoTargets[i] Closure deployment = { if (config.preDeploymentHook) { config.preDeploymentHook.call() } neoDeploy( script: script, warAction: deploymentType, source: config.source, neo: target ) } deployments.put("Deployment ${index}", deployment) index++ } } if (!config.cfTargets && !config.neoTargets) { error "Deployment skipped because no targets defined!" } runClosures(script, deployments, config.parallelExecution, "deployments") } }