diff --git a/cmd/cloudFoundryDeploy_generated.go b/cmd/cloudFoundryDeploy_generated.go index c0f581cf8..51b52aa16 100644 --- a/cmd/cloudFoundryDeploy_generated.go +++ b/cmd/cloudFoundryDeploy_generated.go @@ -205,7 +205,7 @@ func addCloudFoundryDeployFlags(cmd *cobra.Command, stepConfig *cloudFoundryDepl cmd.Flags().BoolVar(&stepConfig.KeepOldInstance, "keepOldInstance", false, "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.") cmd.Flags().StringVar(&stepConfig.LoginParameters, "loginParameters", os.Getenv("PIPER_loginParameters"), "Addition command line options for cf login command. No escaping/quoting is performed. Not recommended for productive environments.") cmd.Flags().StringVar(&stepConfig.Manifest, "manifest", os.Getenv("PIPER_manifest"), "Defines the manifest to be used for deployment to Cloud Foundry.") - cmd.Flags().StringSliceVar(&stepConfig.ManifestVariables, "manifestVariables", []string{}, "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 value 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'.") + cmd.Flags().StringSliceVar(&stepConfig.ManifestVariables, "manifestVariables", []string{}, "Defines a list of variables in the form `key=value` which are used for variable substitution within the file given by manifest.") cmd.Flags().StringSliceVar(&stepConfig.ManifestVariablesFiles, "manifestVariablesFiles", []string{`manifest-variables.yml`}, "path(s) of the Yaml file(s) containing the variable values to use as a replacement in the manifest file. The order of the files is relevant in case there are conflicting variable names and values within variable files. In such a case, the values of the last file win.") cmd.Flags().StringVar(&stepConfig.MtaDeployParameters, "mtaDeployParameters", `-f`, "Additional parameters passed to mta deployment command") cmd.Flags().StringVar(&stepConfig.MtaExtensionDescriptor, "mtaExtensionDescriptor", os.Getenv("PIPER_mtaExtensionDescriptor"), "Defines additional extension descriptor file for deployment with the mtaDeployPlugin") diff --git a/resources/metadata/cloudFoundryDeploy.yaml b/resources/metadata/cloudFoundryDeploy.yaml index c6dfa65c6..0e6b4526a 100644 --- a/resources/metadata/cloudFoundryDeploy.yaml +++ b/resources/metadata/cloudFoundryDeploy.yaml @@ -209,21 +209,28 @@ spec: - name: cloudFoundry/manifest - name: manifestVariables type: "[]string" - description: - "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. + description: Defines a list of variables in the form `key=value` which are used for variable substitution within the file given by manifest. + longDescription: | + Defines a list of variables in the form `key=value` which are used for variable substitution + within the file given by manifest. + 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 value 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 + + **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'." + + Example: + + ``` + ... + cloudFoundryDeploy: + manifestVariables: + - 'key1=value1' + - 'key2=value2' + ... + ``` scope: - PARAMETERS - STAGES diff --git a/test/groovy/CloudFoundryDeployTest.groovy b/test/groovy/CloudFoundryDeployTest.groovy index d7036b51b..9811ef7cd 100644 --- a/test/groovy/CloudFoundryDeployTest.groovy +++ b/test/groovy/CloudFoundryDeployTest.groovy @@ -1,73 +1,22 @@ -import com.sap.piper.JenkinsUtils -import hudson.AbortException -import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import org.junit.rules.ExpectedException import org.junit.rules.RuleChain import util.BasePiperTest import util.JenkinsCredentialsRule -import util.JenkinsDockerExecuteRule -import util.JenkinsEnvironmentRule -import util.JenkinsFileExistsRule -import util.JenkinsLoggingRule -import util.JenkinsReadFileRule -import util.JenkinsReadYamlRule -import util.JenkinsShellCallRule import util.JenkinsStepRule -import util.JenkinsWriteFileRule import util.Rules - -import static org.hamcrest.Matchers.allOf -import static org.hamcrest.Matchers.containsString -import static org.hamcrest.Matchers.equalTo -import static org.hamcrest.Matchers.hasEntry -import static org.hamcrest.Matchers.hasItem -import static org.hamcrest.Matchers.is -import static org.hamcrest.Matchers.not -import static org.hamcrest.Matchers.stringContainsInOrder import static org.junit.Assert.assertEquals -import static org.junit.Assert.assertNotNull -import static org.junit.Assert.assertThat import static org.junit.Assert.assertTrue class CloudFoundryDeployTest extends BasePiperTest { - private File tmpDir = File.createTempDir() - private ExpectedException thrown = ExpectedException.none() - private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this) - private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this) - private JenkinsWriteFileRule writeFileRule = new JenkinsWriteFileRule(this) - private JenkinsReadFileRule readFileRule = new JenkinsReadFileRule(this, tmpDir.getAbsolutePath()) - private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this) private JenkinsStepRule stepRule = new JenkinsStepRule(this) - private JenkinsEnvironmentRule environmentRule = new JenkinsEnvironmentRule(this) - private JenkinsReadYamlRule readYamlRule = new JenkinsReadYamlRule(this) - private JenkinsFileExistsRule fileExistsRule = new JenkinsFileExistsRule(this, []) private JenkinsCredentialsRule credentialsRule = new JenkinsCredentialsRule(this) - - private writeInfluxMap = [:] - - class JenkinsUtilsMock extends JenkinsUtils { - def isJobStartedByUser() { - return true - } - } - @Rule public RuleChain rules = Rules .getCommonRules(this) - .around(readYamlRule) - .around(thrown) - .around(loggingRule) - .around(shellRule) - .around(writeFileRule) - .around(readFileRule) - .around(fileExistsRule) - .around(dockerExecuteRule) - .around(environmentRule) .around(credentialsRule) .around(stepRule) // needs to be activated after dockerExecuteRule, otherwise executeDocker is not mocked @@ -76,1264 +25,10 @@ class CloudFoundryDeployTest extends BasePiperTest { // removing additional credentials tests might have added; adding default credentials credentialsRule.reset() .withCredentials('test_cfCredentialsId', 'test_cf', '********') - - UUID.metaClass.static.randomUUID = { -> 1 } - helper.registerAllowedMethod('influxWriteData', [Map.class], { m -> - writeInfluxMap = m - }) - - helper.registerAllowedMethod('findFiles', [Map.class], { m -> - return [].toArray() - }) - } - - @After - void tearDown() { - UUID.metaClass = null } @Test - void testNoTool() throws Exception { - nullScript.commonPipelineEnvironment.configuration = [ - general: [ - camSystemRole: 'testRole', - cfCredentialsId: 'test_cfCredentialsId' - ], - stages: [ - acceptance: [ - cfOrg: 'testOrg', - cfSpace: 'testSpace', - deployUser: 'testUser', - ] - ], - steps: [ - cloudFoundryDeploy: [] - ] - ] - nullScript.commonPipelineEnvironment.setBuildTool('mta') - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - mtaPath: 'target/test.mtar', - stageName: 'acceptance', - ]) - // asserts - assertThat(loggingRule.log, containsString('[cloudFoundryDeploy] General parameters: deployTool=mtaDeployPlugin, deployType=standard, cfApiEndpoint=https://api.cf.eu10.hana.ondemand.com, cfOrg=testOrg, cfSpace=testSpace, cfCredentialsId=test_cfCredentialsId')) - } - - @Test - void testNotAvailableTool() throws Exception { - nullScript.commonPipelineEnvironment.configuration = [ - general: [ - cfCredentialsId: 'myCreds' - ], - stages: [ - acceptance: [ - cfOrg: 'testOrg', - cfSpace: 'testSpace', - deployUser: 'testUser', - ] - ], - steps: [ - cloudFoundryDeploy: [] - ] - ] - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'notAvailable', - stageName: 'acceptance' - ]) - // asserts - assertThat(loggingRule.log, containsString('[cloudFoundryDeploy] General parameters: deployTool=notAvailable, deployType=standard, cfApiEndpoint=https://api.cf.eu10.hana.ondemand.com, cfOrg=testOrg, cfSpace=testSpace, cfCredentialsId=myCreds')) - assertThat(loggingRule.log, containsString('[cloudFoundryDeploy] WARNING! Found unsupported deployTool. Skipping deployment.')) - } - - - - @Test - void testCfNativeWithAppName() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfNativeWithAppNameCustomApi() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfApiEndpoint: 'https://customApi', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - // asserts - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://customApi -o "testOrg" -s "testSpace"'))) - } - - @Test - void testCfNativeWithAppNameCompatible() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - credentialsId: 'test_cfCredentialsId', - appName: 'testAppName', - manifest: 'test.yml' - ] - ]) - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfNativeWithDockerImage() { - // adding additional credentials for Docker registry authorization - credentialsRule.withCredentials('test_cfDockerCredentialsId', 'test_cf_docker', '********') - readYamlRule.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployDockerImage: 'repo/image:tag', - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - credentialsId: 'test_cfCredentialsId', - appName: 'testAppName' - ] - ]) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString('cf push testAppName --docker-image repo/image:tag'))) - assertThat(shellRule.shell, hasItem(containsString('cf logout'))) - } - - @Test - void testCfNativeWithDockerImageAndCredentials() { - // adding additional credentials for Docker registry authorization - credentialsRule.withCredentials('test_cfDockerCredentialsId', 'test_cf_docker', '********') - readYamlRule.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployDockerImage: 'repo/image:tag', - dockerCredentialsId: 'test_cfDockerCredentialsId', - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - credentialsId: 'test_cfCredentialsId', - appName: 'testAppName' - ] - ]) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry(equalTo('CF_DOCKER_PASSWORD'), equalTo("${'********'}"))) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString('cf push testAppName --docker-image repo/image:tag --docker-username test_cf_docker'))) - assertThat(shellRule.shell, hasItem(containsString('cf logout'))) - } - - @Test - void testCfNativeWithManifestAndDockerCredentials() { - // Docker image can be done via manifest.yml; if a private Docker registry is used, --docker-username and DOCKER_PASSWORD - // must be set; this is checked by this test - - // adding additional credentials for Docker registry authorization - credentialsRule.withCredentials('test_cfDockerCredentialsId', 'test_cf_docker', '********') - readYamlRule.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - dockerCredentialsId: 'test_cfDockerCredentialsId', - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - credentialsId: 'test_cfCredentialsId', - appName: 'testAppName', - manifest: 'manifest.yml' - ] - ]) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry(equalTo('CF_DOCKER_PASSWORD'), equalTo("${'********'}"))) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'manifest.yml' --docker-username test_cf_docker"))) - assertThat(shellRule.shell, hasItem(containsString('cf logout'))) - } - - @Test - void testCfNativeBlueGreenWithManifestAndDockerCredentials() { - // Blue Green Deploy cf cli plugin does not support --docker-username and --docker-image parameters - // docker username and docker image have to be set in the manifest file - // if a private docker repository is used the CF_DOCKER_PASSWORD env variable must be set - - credentialsRule.withCredentials('test_cfDockerCredentialsId', 'test_cf_docker', '********') - readYamlRule.registerYaml('manifest.yml', "applications: [[name: 'manifestAppName']]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - dockerCredentialsId: 'test_cfDockerCredentialsId', - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - credentialsId: 'test_cfCredentialsId', - appName: 'testAppName', - manifest: 'manifest.yml' - ] - ]) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry(equalTo('CF_DOCKER_PASSWORD'), equalTo("${'********'}"))) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName --delete-old-apps -f 'manifest.yml'"))) - assertThat(shellRule.shell, hasItem(containsString('cf logout'))) - } - - @Test - void testCfNativeAppNameFromManifest() { - fileExistsRule.registerExistingFile('test.yml') - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfManifest: 'test.yml' - ]) - // asserts - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfNativeWithoutAppName() { - fileExistsRule.registerExistingFile('test.yml') - readYamlRule.registerYaml('test.yml', "applications: [{}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - thrown.expect(hudson.AbortException) - thrown.expectMessage('[cloudFoundryDeploy] ERROR: No appName available in manifest test.yml.') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfManifest: 'test.yml' - ]) - } - - @Test - void testCfNativeBlueGreenDefaultDeleteOldInstance() { - - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName --delete-old-apps -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - - } - - @Test - void testCfNativeBlueGreenExplicitDeleteOldInstance() { - - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - keepOldInstance: false, - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName --delete-old-apps -f 'test.yml'"))) - assertThat(shellRule.shell, not(hasItem(containsString("cf stop testAppName-old &>")))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - - } - - @Test - void testCfNativeBlueGreenKeepOldInstance() { - - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - keepOldInstance: true, - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf stop testAppName-old &>"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfNativeBlueGreenMultipleApplications() { - - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName1'},{name: 'manifestAppName2'}]") - fileExistsRule.registerExistingFile('test.yml') - - thrown.expect(hudson.AbortException) - thrown.expectMessage("[cloudFoundryDeploy] Your manifest contains more than one application. For blue green deployments your manifest file may contain only one application.") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - } - - @Test - void testCfNativeBlueGreenWithNoRoute() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName1', no-route: true}]") - fileExistsRule.registerExistingFile('test.yml') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - } - - @Test - void testCfNativeBlueGreenKeepOldInstanceShouldThrowErrorOnStopError(){ - new File(tmpDir, '1-cfStopOutput.txt').write('any error message') - - helper.registerAllowedMethod("sh", [String], { cmd -> - if (cmd.toString().contains('cf stop testAppName-old')) - throw new Exception('fail') - }) - - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - thrown.expect(hudson.AbortException) - thrown.expectMessage("[cloudFoundryDeploy] ERROR: Could not stop application testAppName-old. Error: any error message") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - keepOldInstance: true, - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf stop testAppName-old &> 1-cfStopOutput.txt"))) - } - - @Test - void testCfNativeStandardShouldNotStopInstance() { - - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'standard', - keepOldInstance: true, - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(shellRule.shell, not(hasItem(containsString("cf stop testAppName-old &>")))) - } - - @Test - void testCfNativeWithoutAppNameBlueGreen() { - - fileExistsRule.registerExistingFile('test.yml') - readYamlRule.registerYaml('test.yml', "applications: [{}]") - - thrown.expect(hudson.AbortException) - thrown.expectMessage('[cloudFoundryDeploy] ERROR: Blue-green plugin requires app name to be passed (see https://github.com/bluemixgaragelondon/cf-blue-green-deploy/issues/27)') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfManifest: 'test.yml' - ]) - } - - @Test - void testCfNativeFailureInShellCall() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - helper.registerAllowedMethod("sh", [String], { cmd -> - if (cmd.toString().contains('cf login -u "test_cf"')) - throw new Exception('fail') - }) - - thrown.expect(hudson.AbortException) - thrown.expectMessage('[cloudFoundryDeploy] ERROR: The execution of the deploy command failed, see the log for details.') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - - @Test - void testMta() { - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - mtaPath: 'target/test.mtar' - ]) - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString('cf deploy target/test.mtar -f'))) - assertThat(shellRule.shell, hasItem(containsString('cf logout'))) - } - - @Test - void useMtaFilePathFromPipelineEnvironment() { - environmentRule.env.mtarFilePath = 'target/test.mtar' - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin' - ]) - // asserts - assertThat(shellRule.shell, hasItem(containsString('cf deploy target/test.mtar -f'))) - } - - @Test - void testMtaBlueGreen() { - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - deployType: 'blue-green', - mtaPath: 'target/test.mtar' - ]) - - assertThat(shellRule.shell, hasItem(stringContainsInOrder(["cf login -u \"test_cf\"", 'cf bg-deploy', '-f', '--no-confirm']))) - } - - @Test - void testInfluxReporting() { - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - nullScript.commonPipelineEnvironment.setArtifactVersion('1.2.3') - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - // asserts - assertThat(writeInfluxMap.customDataMap.deployment_data.artifactUrl, is('n/a')) - assertThat(writeInfluxMap.customDataMap.deployment_data.deployTime, containsString(new Date().format( 'MMM dd, yyyy'))) - assertThat(writeInfluxMap.customDataMap.deployment_data.jobTrigger, is('USER')) - - assertThat(writeInfluxMap.customDataMapTags.deployment_data.artifactVersion, is('1.2.3')) - assertThat(writeInfluxMap.customDataMapTags.deployment_data.deployUser, is('test_cf')) - assertThat(writeInfluxMap.customDataMapTags.deployment_data.deployResult, is('SUCCESS')) - assertThat(writeInfluxMap.customDataMapTags.deployment_data.cfApiEndpoint, is('https://api.cf.eu10.hana.ondemand.com')) - assertThat(writeInfluxMap.customDataMapTags.deployment_data.cfOrg, is('testOrg')) - assertThat(writeInfluxMap.customDataMapTags.deployment_data.cfSpace, is('testSpace')) - } - - @Test - void testCfPushDeploymentWithVariableSubstitutionFromFile() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - fileExistsRule.registerExistingFile('test.yml') - fileExistsRule.registerExistingFile('vars.yml') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariablesFiles: ['vars.yml'] - ]) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName --vars-file 'vars.yml' -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - assertThat(loggingRule.log,containsString("We will add the following string to the cf push call: --vars-file 'vars.yml' !")) - assertThat(loggingRule.log,not(containsString("We will add the following string to the cf push call: !"))) - } - - @Test - void testCfPushDeploymentWithVariableSubstitutionFromNotExistingFilePrintsWarning() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - fileExistsRule.registerExistingFile('test.yml') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariablesFiles: ['vars.yml'] - ]) - - // asserts - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - assertThat(loggingRule.log, containsString("[WARNING] We skip adding not-existing file 'vars.yml' as a vars-file to the cf create-service-push call")) - } - - @Test - void testCfPushDeploymentWithVariableSubstitutionFromVarsList() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - List varsList = [["appName" : "testApplicationFromVarsList"]] - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariables: varsList - ]) - - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName --var appName='testApplicationFromVarsList' -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - assertThat(loggingRule.log,containsString("We will add the following string to the cf push call: --var appName='testApplicationFromVarsList' !")) - assertThat(loggingRule.log,not(containsString("We will add the following string to the cf push call: !"))) - } - - @Test - void testCfPushDeploymentWithVariableSubstitutionFromVarsListNotAList() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - - thrown.expect(hudson.AbortException) - thrown.expectMessage('[cloudFoundryDeploy] ERROR: Parameter config.cloudFoundry.manifestVariables is not a List!') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariables: 'notAList' - ]) - - } - - @Test - void testCfPushDeploymentWithVariableSubstitutionFromVarsListAndVarsFile() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - List varsList = [["appName" : "testApplicationFromVarsList"]] - fileExistsRule.registerExistingFile('vars.yml') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariablesFiles: ['vars.yml'], - cfManifestVariables: varsList - ]) - - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName --var appName='testApplicationFromVarsList' --vars-file 'vars.yml' -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfPushDeploymentWithoutVariableSubstitution() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - // asserts - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf push testAppName -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfBlueGreenDeploymentWithVariableSubstitution() { - - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - readYamlRule.registerYaml('vars.yml', "[appName: 'testApplication']") - - fileExistsRule.registerExistingFile("test.yml") - fileExistsRule.registerExistingFile("vars.yml") - - boolean testYamlWritten = false - def testYamlData = null - helper.registerAllowedMethod('writeYaml', [Map], { Map m -> - if (m.file.equals("test.yml")) { - testYamlWritten = true - testYamlData = m.data - } - }) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariablesFiles: ['vars.yml'] - ]) - - // asserts - assertTrue(testYamlWritten) - assertNotNull(testYamlData) - assertThat(testYamlData.get("applications").get(0).get("name"), is("testApplication")) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName --delete-old-apps -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testCfBlueGreenDeploymentWithVariableSubstitutionFromVarsList() { - readYamlRule.registerYaml('test.yml', "applications: [{name: '((appName))'}]") - readYamlRule.registerYaml('vars.yml', "[appName: 'testApplication']") - List varsList = [["appName" : "testApplicationFromVarsList"]] - - fileExistsRule.registerExistingFile("test.yml") - fileExistsRule.registerExistingFile("vars.yml") - - boolean testYamlWritten = false - def testYamlData = null - helper.registerAllowedMethod('writeYaml', [Map], { Map m -> - if (m.file.equals("test.yml")) { - testYamlWritten = true - testYamlData = m.data - } - }) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - deployType: 'blue-green', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml', - cfManifestVariablesFiles: ['vars.yml'], - cfManifestVariables: varsList - ]) - - // asserts - assertTrue(testYamlWritten) - assertNotNull(testYamlData) - assertThat(testYamlData.get("applications").get(0).get("name"), is("testApplicationFromVarsList")) - - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerImage', 'ppiper/cf-cli:6')) - assertThat(dockerExecuteRule.dockerParams, hasEntry('dockerWorkspace', '/home/piper')) - assertThat(dockerExecuteRule.dockerParams.dockerEnvVars, hasEntry('STATUS_CODE', "${200}")) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "test_cf" -p \'********\' -a https://api.cf.eu10.hana.ondemand.com -o "testOrg" -s "testSpace"'))) - assertThat(shellRule.shell, hasItem(containsString("cf blue-green-deploy testAppName --delete-old-apps -f 'test.yml'"))) - assertThat(shellRule.shell, hasItem(containsString("cf logout"))) - } - - @Test - void testTraceOutputOnVerbose() { - - fileExistsRule.existingFiles.addAll( - 'test.yml', - 'cf.log' - ) - - new File(tmpDir, 'cf.log') << 'Hello SAP' - - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - manifest: 'test.yml', - ], - deployTool: 'cf_native', - cfCredentialsId: 'test_cfCredentialsId', - verbose: true - ]) - - assertThat(loggingRule.log, allOf( - containsString('### START OF CF CLI TRACE OUTPUT ###'), - containsString('Hello SAP'), - containsString('### END OF CF CLI TRACE OUTPUT ###'))) - } - - @Test - void testTraceNoTraceFileWritten() { - - fileExistsRule.existingFiles.addAll( - 'test.yml', - ) - - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - manifest: 'test.yml', - ], - deployTool: 'cf_native', - cfCredentialsId: 'test_cfCredentialsId', - verbose: true - ]) - - assertThat(loggingRule.log, containsString('No trace file found')) - } - - @Test - void testAdditionCfNativeOpts() { - - readYamlRule.registerYaml('test.yml', "applications: [{name: 'manifestAppName'}]") - helper.registerAllowedMethod('writeYaml', [Map], { Map parameters -> - generatedFile = parameters.file - data = parameters.data - }) - nullScript.commonPipelineEnvironment.setArtifactVersion('1.2.3') - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - deployTool: 'cf_native', - cfOrg: 'testOrg', - cfSpace: 'testSpace', - loginParameters: '--some-login-opt value', - cfNativeDeployParameters: '--some-deploy-opt cf-value', - cfCredentialsId: 'test_cfCredentialsId', - cfAppName: 'testAppName', - cfManifest: 'test.yml' - ]) - - assertThat(shellRule.shell, hasItem( - stringContainsInOrder([ - 'cf login ', '--some-login-opt value', - 'cf push', '--some-deploy-opt cf-value']))) - - } - - @Test - void testAdditionMtaOpts() { - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - ], - apiParameters: '--some-api-opt value', - loginParameters: '--some-login-opt value', - mtaDeployParameters: '--some-deploy-opt mta-value', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - deployType: 'blue-green', - mtaPath: 'target/test.mtar' - ]) - - assertThat(shellRule.shell, hasItem( - stringContainsInOrder([ - 'cf api', '--some-api-opt value', - 'cf login ', '--some-login-opt value', - 'cf bg-deploy', '--some-deploy-opt mta-value']))) - - } - - @Test - void 'appName with underscores should throw an error'() { - String expected = "Your application name my_invalid_app_name contains a '_' (underscore) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement.\n" + - "For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings." - String actual = "" - helper.registerAllowedMethod('error', [String.class], {s -> actual = s}) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'irrelevant', - space: 'irrelevant', - appName: 'my_invalid_app_name' - ], - cfCredentialsId: 'test_cfCredentialsId', - mtaPath: 'irrelevant' - ]) - - assertEquals(expected, actual) - } - - @Test - void 'appName with alpha-numeric chars and leading dash should throw an error'() { - String expected = "Your application name -my-Invalid-AppName123 contains a starts or ends with a '-' (dash) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement.\nFor more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings." - String actual = "" - helper.registerAllowedMethod('error', [String.class], {s -> actual = s}) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'irrelevant', - space: 'irrelevant', - appName: '-my-Invalid-AppName123' - ], - cfCredentialsId: 'test_cfCredentialsId', - mtaPath: 'irrelevant' - ]) - - assertEquals(expected, actual) - } - - @Test - void 'appName with alpha-numeric chars and trailing dash should throw an error'() { - String expected = "Your application name my-Invalid-AppName123- contains a starts or ends with a '-' (dash) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement.\nFor more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings." - String actual = "" - helper.registerAllowedMethod('error', [String.class], {s -> actual = s}) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'irrelevant', - space: 'irrelevant', - appName: 'my-Invalid-AppName123-' - ], - cfCredentialsId: 'test_cfCredentialsId', - mtaPath: 'irrelevant' - ]) - - assertEquals(expected, actual) - } - - @Test - void 'appName with alpha-numeric chars should work'() { - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'irrelevant', - space: 'irrelevant', - appName: 'myValidAppName123' - ], - deployTool: 'cf_native', - cfCredentialsId: 'test_cfCredentialsId', - mtaPath: 'irrelevant' - ]) - - assertTrue(loggingRule.log.contains("cfAppName=myValidAppName123")) - } - - @Test - void 'appName with alpha-numeric chars and dash should work'() { - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'irrelevant', - space: 'irrelevant', - appName: 'my-Valid-AppName123' - ], - deployTool: 'cf_native', - cfCredentialsId: 'test_cfCredentialsId', - mtaPath: 'irrelevant' - ]) - - assertTrue(loggingRule.log.contains("cfAppName=my-Valid-AppName123")) - } - - @Test - void testMtaExtensionDescriptor() { - fileExistsRule.existingFiles.addAll( - 'globalMtaDescriptor.mtaext', - ) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace' - ], - mtaExtensionDescriptor: 'globalMtaDescriptor.mtaext', - mtaDeployParameters: '--some-deploy-opt mta-value', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - deployType: 'blue-green', - mtaPath: 'target/test.mtar' - ]) - - assertThat(shellRule.shell, hasItem(containsString("-e globalMtaDescriptor.mtaext"))) - } - - @Test - void testTargetMtaExtensionDescriptor() { - fileExistsRule.existingFiles.addAll( - 'targetMtaDescriptor.mtaext', - ) - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cloudFoundry: [ - org: 'testOrg', - space: 'testSpace', - mtaExtensionDescriptor: 'targetMtaDescriptor.mtaext' - ], - mtaDeployParameters: '--some-deploy-opt mta-value', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - deployType: 'blue-green', - mtaPath: 'target/test.mtar' - ]) - assertThat(shellRule.shell, hasItem(containsString("-e targetMtaDescriptor.mtaext"))) - } - - @Test - void testMtaExtensionCredentials() { - fileExistsRule.existingFiles.addAll( - 'mtaext.mtaext', - ) - credentialsRule.withCredentials("mtaExtCredTest","token") - - helper.registerAllowedMethod('readFile', [String], {file -> - if (file == 'mtaext.mtaext') { - return '_schema-version: \'3.1\'\n' + - 'ID: test.ext\n' + - 'extends: test\n' + - '\n' + - 'parameters:\n' + - ' test-credentials: "<%= testCred %>"' - } - return '' - }) - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - mtaPath: 'target/test.mtar', - mtaExtensionDescriptor: "mtaext.mtaext", - mtaExtensionCredentials: [ - testCred: 'mtaExtCredTest' - ] - ]) - - assertThat(shellRule.shell, hasItem(containsString('cp mtaext.mtaext mtaext.mtaext.original'))) - assertThat(shellRule.shell, hasItem(containsString('mv --force mtaext.mtaext.original mtaext.mtaext'))) - assertThat(writeFileRule.files['mtaext.mtaext'], is('_schema-version: \'3.1\'\n' + - 'ID: test.ext\n' + - 'extends: test\n' + - '\n' + - 'parameters:\n' + - ' test-credentials: "token"')) - } - - @Test - void testMtaExtensionDescriptorNotFound() { - thrown.expect(hudson.AbortException) - thrown.expectMessage('[cloudFoundryDeploy] The mta extension descriptor file mtaext.mtaext does not exist at the configured location.') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - mtaPath: 'target/test.mtar', - mtaExtensionDescriptor: "mtaext.mtaext" - ]) - } - - @Test - void testMtaExtensionDescriptorReadFails() { - fileExistsRule.existingFiles.addAll( - 'mtaext.mtaext', - ) - - thrown.expect(Exception) - thrown.expectMessage('[cloudFoundryDeploy] Unable to read mta extension file mtaext.mtaext.') - - stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - cfOrg: 'testOrg', - cfSpace: 'testSpace', - cfCredentialsId: 'test_cfCredentialsId', - deployTool: 'mtaDeployPlugin', - mtaPath: 'target/test.mtar', - mtaExtensionDescriptor: "mtaext.mtaext", - mtaExtensionCredentials: [ - testCred: 'mtaExtCredTest' - ], - ]) - } - - @Test - void testGoStepFeatureToggleOn() { + void testGoStepWithMtaExtensionCredentialsFromParams() { String calledStep = '' String usedMetadataFile = '' List credInfo = [] @@ -1346,14 +41,9 @@ class CloudFoundryDeployTest extends BasePiperTest { }) stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - useGoStep: true, - deployTool: 'irrelevant', - cfOrg: 'irrelevant', - cfSpace: 'irrelevant', - cfCredentialsId: 'irrelevant', + script : nullScript, + juStabUtils : utils, + useGoStep : true, mtaExtensionCredentials: [myCred: 'Mta.ExtensionCredential~Credential_Id1'], ]) @@ -1362,37 +52,47 @@ class CloudFoundryDeployTest extends BasePiperTest { // contains assertion does not work apparently when comparing a list of lists agains an expected list. boolean found = false - credInfo.each { entry -> - if (entry == [type:'token', id:'Mta.ExtensionCredential~Credential_Id1', env:['MTA_EXTENSION_CREDENTIAL_CREDENTIAL_ID1'], resolveCredentialsId:false]) { - found = true + credInfo.each { entry -> + if (entry == [type: 'token', id: 'Mta.ExtensionCredential~Credential_Id1', env: ['MTA_EXTENSION_CREDENTIAL_CREDENTIAL_ID1'], resolveCredentialsId: false]) { + found = true } - } + } assertTrue(found) } @Test - void testGoStepFeatureToggleOff() { + void testGoStepWithMtaExtensionCredentialsFromConfig() { String calledStep = '' String usedMetadataFile = '' + List credInfo = [] helper.registerAllowedMethod('piperExecuteBin', [Map, String, String, List], { Map parameters, String stepName, String metadataFile, List credentialInfo -> calledStep = stepName usedMetadataFile = metadataFile + credInfo = credentialInfo }) + nullScript.commonPipelineEnvironment.configuration = [steps:[cloudFoundryDeploy:[ + mtaExtensionCredentials: [myCred: 'Mta.ExtensionCredential~Credential_Id1'] + ]]] + stepRule.step.cloudFoundryDeploy([ - script: nullScript, - juStabUtils: utils, - jenkinsUtilsStub: new JenkinsUtilsMock(), - useGoStep: 'false', - deployTool: 'irrelevant', - cfOrg: 'irrelevant', - cfSpace: 'irrelevant', - cfCredentialsId: 'irrelevant', + script : nullScript, + juStabUtils : utils, + useGoStep : true, ]) - assertEquals('', calledStep) - assertEquals('', usedMetadataFile) + assertEquals('cloudFoundryDeploy', calledStep) + assertEquals('metadata/cloudFoundryDeploy.yaml', usedMetadataFile) + + // contains assertion does not work apparently when comparing a list of lists agains an expected list. + boolean found = false + credInfo.each { entry -> + if (entry == [type: 'token', id: 'Mta.ExtensionCredential~Credential_Id1', env: ['MTA_EXTENSION_CREDENTIAL_CREDENTIAL_ID1'], resolveCredentialsId: false]) { + found = true + } + } + assertTrue(found) } } diff --git a/test/groovy/CommonStepsTest.groovy b/test/groovy/CommonStepsTest.groovy index 9c9948d8c..018bd4274 100644 --- a/test/groovy/CommonStepsTest.groovy +++ b/test/groovy/CommonStepsTest.groovy @@ -128,6 +128,7 @@ public class CommonStepsTest extends BasePiperTest{ 'cloudFoundryCreateSpace', //implementing new golang pattern without fields 'cloudFoundryDeleteService', //implementing new golang pattern without fields 'cloudFoundryDeleteSpace', //implementing new golang pattern without fields + 'cloudFoundryDeploy', //implementing new golang pattern without fields 'cnbBuild', //implementing new golang pattern without fields 'durationMeasure', // only expects parameters via signature 'prepareDefaultValues', // special step (infrastructure) diff --git a/test/groovy/FioriOnCloudPlatformPipelineTest.groovy b/test/groovy/FioriOnCloudPlatformPipelineTest.groovy index a96095f75..f6223e727 100644 --- a/test/groovy/FioriOnCloudPlatformPipelineTest.groovy +++ b/test/groovy/FioriOnCloudPlatformPipelineTest.groovy @@ -166,6 +166,13 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest { @Test void straightForwardTestCF() { + def calledStep + helper.registerAllowedMethod('piperExecuteBin', [Map, String, String, List], { + Map parameters, String stepName, + String metadataFile, List credentialInfo -> + calledStep = stepName + }) + nullScript .commonPipelineEnvironment .configuration = [steps: @@ -187,10 +194,9 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest { stepRule.step.fioriOnCloudPlatformPipeline( script: nullScript, - jenkinsUtilsStub: new JenkinsUtilsMock() ) - assertThat(shellRule.shell, hasItem(containsString('cf login -u "foo" -p \'terceSpot\' -a https://api.cf.hana.example.com -o "testOrg" -s "testSpace"'))) + assertThat(calledStep, is('cloudFoundryDeploy')) } @Test diff --git a/vars/cloudFoundryDeploy.groovy b/vars/cloudFoundryDeploy.groovy index b8042a6ad..98d08b9a9 100644 --- a/vars/cloudFoundryDeploy.groovy +++ b/vars/cloudFoundryDeploy.groovy @@ -1,303 +1,30 @@ -import com.sap.piper.BashUtils -import com.sap.piper.CfManifestUtils -import com.sap.piper.ConfigurationHelper -import com.sap.piper.JenkinsUtils import com.sap.piper.Utils import groovy.transform.Field - import static com.sap.piper.Prerequisites.checkScript @Field String STEP_NAME = getClass().getName() +@Field String METADATA_FILE = 'metadata/cloudFoundryDeploy.yaml' -@Field Set GENERAL_CONFIG_KEYS = [ - /** - * This is set in the common pipeline environment by the build tool, e.g. during the mtaBuild step. - */ - 'buildTool', - 'cloudFoundry', - /** - * 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', - /** - * 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 `. - * - * 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`. - * @parentConfigKey cloudFoundry - */ - 'manifestVariables', - /** - * Cloud Foundry target organization. - * @parentConfigKey cloudFoundry - */ - 'org', - /** - * Cloud Foundry target space. - * @parentConfigKey cloudFoundry - */ - 'space', - /** - * Defines the tool which should be used for deployment. - * If it is not set it will be inferred automatically based on the buildTool, i.e., for MTA projects `mtaDeployPlugin` will be used and `cf_native` for other types of projects. - * @possibleValues 'cf_native', 'mtaDeployPlugin' - */ - 'deployTool', - /** - * Defines the type of deployment, either `standard` deployment, which results in a system downtime, or a zero-downtime `blue-green` deployment. - * If 'cf_native' as deployTool and 'blue-green' as deployType 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'. - * @possibleValues 'standard', 'blue-green' - */ - 'deployType', - /** - * 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 - */ - 'keepOldInstance', - /** @see dockerExecute */ - 'dockerImage', - /** @see dockerExecute */ - 'dockerWorkspace', - /** @see dockerExecute */ - 'stashContent', - /** - * 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. - */ - 'mtaDeployParameters', - /** - * Defines a map of credentials that need to be replaced in the `mtaExtensionDescriptor`. - * This map needs to be created as `value-to-be-replaced`:`id-of-a-credential-in-jenkins` - */ - 'mtaExtensionCredentials', - /** - * Defines additional extension descriptor file for deployment with the mtaDeployPlugin. - */ - 'mtaExtensionDescriptor', - /** - * Defines the path to *.mtar for deployment with the mtaDeployPlugin. If not specified, it will use the mta file created in mtaBuild or search for an mtar file in the workspace. - */ - 'mtaPath', - /** - * 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)
Currently this option is only considered for deployTool `cf_native`. - */ - 'smokeTestScript', - /** - * Expected status code returned by the check. - */ - 'smokeTestStatusCode', - /** - * Provides more output. May reveal sensitive information. - * @possibleValues true, false - */ - 'verbose', - /** - * 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', - /** - * Output the CloudFoundry trace logs. If not specified, takes the value of config.verbose. - */ - 'cfTrace', - /** - * Toggle to activate the new go-implementation of the step. Off by default. - * @possibleValues true, false - */ - 'useGoStep', -] -@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS -@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS - -@Field Map CONFIG_KEY_COMPATIBILITY = [ - cloudFoundry: [ - apiEndpoint: 'cfApiEndpoint', - appName:'cfAppName', - credentialsId: 'cfCredentialsId', - manifest: 'cfManifest', - manifestVariablesFiles: 'cfManifestVariablesFiles', - manifestVariables: 'cfManifestVariables', - org: 'cfOrg', - space: 'cfSpace', - ], - mtaExtensionDescriptor: 'cloudFoundry/mtaExtensionDescriptor' -] - -/** - * Deploys an application to a test or production space within Cloud Foundry. - */ void call(Map parameters = [:]) { + final script = checkScript(this, parameters) ?: this + String stageName = parameters.stageName ?: env.STAGE_NAME - handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) { + def utils = parameters.juStabUtils ?: new Utils() + utils.unstashAll(["deployDescriptor"]) + List credentials = [ + [type: 'usernamePassword', id: 'cfCredentialsId', env: ['PIPER_username', 'PIPER_password']], + [type: 'usernamePassword', id: 'dockerCredentialsId', env: ['PIPER_dockerUsername', 'PIPER_dockerPassword']] + ] - final script = checkScript(this, parameters) ?: this - def utils = parameters.juStabUtils ?: new Utils() - def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils() - String stageName = parameters.stageName ?: env.STAGE_NAME + Map mtaExtensionCredentials = parameters.mtaExtensionCredentials ?: script.commonPipelineEnvironment.getStepConfiguration(STEP_NAME, stageName).mtaExtensionCredentials - Map config = 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, CONFIG_KEY_COMPATIBILITY) - .addIfEmpty('buildTool', script.commonPipelineEnvironment.getBuildTool()) - .dependingOn('buildTool').mixin('deployTool') - .dependingOn('deployTool').mixin('dockerImage') - .dependingOn('deployTool').mixin('dockerWorkspace') - .withMandatoryProperty('cloudFoundry/org') - .withMandatoryProperty('cloudFoundry/space') - .withMandatoryProperty('cloudFoundry/credentialsId', null, {c -> return !c.containsKey('vaultAppRoleTokenCredentialsId') || !c.containsKey('vaultAppRoleSecretTokenCredentialsId')}) - .use() - - if (config.cfTrace == null) config.cfTrace = true - - if (config.useGoStep == true) { - utils.unstashAll(["deployDescriptor"]) - List credentials = [ - [type: 'usernamePassword', id: 'cfCredentialsId', env: ['PIPER_username', 'PIPER_password']], - [type: 'usernamePassword', id: 'dockerCredentialsId', env: ['PIPER_dockerUsername', 'PIPER_dockerPassword']] - ] - - if (config.mtaExtensionCredentials) { - config.mtaExtensionCredentials.each { key, credentialsId -> - echo "[INFO]${STEP_NAME}] Preparing credential for being used by piper-go. key: ${key}, credentialsId is: ${credentialsId}, exposed as environment variable ${toEnvVarKey(credentialsId)}" - credentials << [type: 'token', id: credentialsId, env: [toEnvVarKey(credentialsId)], resolveCredentialsId: false] - } - } - piperExecuteBin(parameters, STEP_NAME, 'metadata/cloudFoundryDeploy.yaml', credentials) - return + if (mtaExtensionCredentials) { + mtaExtensionCredentials.each { key, credentialsId -> + echo "[INFO]${STEP_NAME}] Preparing credential for being used by piper-go. key: ${key}, credentialsId is: ${credentialsId}, exposed as environment variable ${toEnvVarKey(credentialsId)}" + credentials << [type: 'token', id: credentialsId, env: [toEnvVarKey(credentialsId)], resolveCredentialsId: false] } - - utils.pushToSWA([ - step: STEP_NAME, - stepParamKey1: 'deployTool', - stepParam1: config.deployTool, - stepParamKey2: 'deployType', - stepParam2: config.deployType, - stepParamKey3: 'scriptMissing', - stepParam3: parameters?.script == null - ], config) - - 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}" - - //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 = [] - - // validate cf app name to avoid a failing deployment due to invalid chars - if (config.cloudFoundry.appName) { - String appName = config.cloudFoundry.appName.toString() - boolean isValidCfAppName = appName.matches("^[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9]\$") - - if (!isValidCfAppName) { - echo "WARNING: Your application name $appName contains non-alphanumeric characters which may lead to errors in the future, as they are not supported by CloudFoundry.\n" + - "For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings" - - // Underscore in the app name will lead to errors because cf uses the appname as part of the url which may not contain underscores - if (appName.contains("_")) { - error("Your application name $appName contains a '_' (underscore) which is not allowed, only letters, dashes and numbers can be used. " + - "Please change the name to fit this requirement.\n" + - "For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") - } - - if (appName.startsWith("-") || appName.endsWith("-")) { - error("Your application name $appName contains a starts or ends with a '-' (dash) which is not allowed, only letters, dashes and numbers can be used. " + - "Please change the name to fit this requirement.\n" + - "For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") - } - } - } - - boolean deployTriggered = false - boolean deploySuccess = true - try { - if (config.deployTool == 'mtaDeployPlugin') { - deployTriggered = true - handleMTADeployment(config, script) - } - else if (config.deployTool == 'cf_native') { - deployTriggered = true - handleCFNativeDeployment(config, script) - } - else { - deployTriggered = false - echo "[${STEP_NAME}] WARNING! Found unsupported deployTool. Skipping deployment." - } - } catch (err) { - deploySuccess = false - throw err - } finally { - if (deployTriggered) { - reportToInflux(script, config, deploySuccess, jenkinsUtils) - } - - } - } + piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) } /* @@ -310,368 +37,3 @@ private static String toEnvVarKey(String key) { key = key.replaceAll(/(.)(? 1){ - error "[${STEP_NAME}] Found multiple *.mtar files, please specify file via mtaPath parameter! ${mtarFiles}" - } - if(mtarFiles.length == 1){ - return mtarFiles[0].path - } - error "[${STEP_NAME}] No *.mtar file found!" -} - -def deployMta(config) { - String mtaExtensionDescriptorParam = '' - - if (config.mtaExtensionDescriptor) { - if (!fileExists(config.mtaExtensionDescriptor)) { - error "[${STEP_NAME}] The mta extension descriptor file ${config.mtaExtensionDescriptor} does not exist at the configured location." - } - - mtaExtensionDescriptorParam = "-e ${config.mtaExtensionDescriptor}" - - if (config.mtaExtensionCredentials) { - handleMtaExtensionCredentials(config) - } - } - - def deployCommand = 'deploy' - if (config.deployType == 'blue-green') { - deployCommand = 'bg-deploy' - if (config.mtaDeployParameters.indexOf('--no-confirm') < 0) { - config.mtaDeployParameters += ' --no-confirm' - } - } - - def deployStatement = "cf ${deployCommand} ${config.mtaPath} ${config.mtaDeployParameters} ${mtaExtensionDescriptorParam}" - def apiStatement = "cf api ${config.cloudFoundry.apiEndpoint} ${config.apiParameters}" - - echo "[${STEP_NAME}] Deploying MTA (${config.mtaPath}) with following parameters: ${mtaExtensionDescriptorParam} ${config.mtaDeployParameters}" - try { - deploy(apiStatement, deployStatement, config, null) - } finally { - if (config.mtaExtensionCredentials && config.mtaExtensionDescriptor && fileExists(config.mtaExtensionDescriptor)) { - sh "mv --force ${config.mtaExtensionDescriptor}.original ${config.mtaExtensionDescriptor} || echo 'The file ${config.mtaExtensionDescriptor}.original could not be renamed.'" - } - } -} - -private void handleMtaExtensionCredentials(Map config) { - echo "[${STEP_NAME}] Modifying ${config.mtaExtensionDescriptor}. Adding credential values from Jenkins." - sh "cp ${config.mtaExtensionDescriptor} ${config.mtaExtensionDescriptor}.original" - - Map mtaExtensionCredentials = config.mtaExtensionCredentials - - String fileContent = '' - - try { - fileContent = readFile config.mtaExtensionDescriptor - } catch (Exception e) { - error("[${STEP_NAME}] Unable to read mta extension file ${config.mtaExtensionDescriptor}. (${e.getMessage()})") - } - - mtaExtensionCredentials.each { key, credentialsId -> - withCredentials([string(credentialsId: credentialsId, variable: 'mtaExtensionCredential')]) { - fileContent = fileContent.replace('<%= ' + key.toString() + ' %>', mtaExtensionCredential.toString()) - } - } - - writeFile file: config.mtaExtensionDescriptor, text: fileContent -} - -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' - } - } - } -} - -private void handleCFNativeDeployment(Map config, script) { - config.smokeTest = '' - - checkAndUpdateDeployTypeForNotSupportedManifest(config) - - if (config.deployType == 'blue-green') { - prepareBlueGreenCfNativeDeploy(config, script) - } else { - prepareCfPushCfNativeDeploy(config) - } - - echo "[${STEP_NAME}] CF native deployment (${config.deployType}) with:" - echo "[${STEP_NAME}] - cfAppName=${config.cloudFoundry.appName}" - echo "[${STEP_NAME}] - cfManifest=${config.cloudFoundry.manifest}" - 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'}" - echo "[${STEP_NAME}] - smokeTestScript=${config.smokeTestScript}" - - checkIfAppNameIsAvailable(config) - - 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: "${dockerCredentials.isEmpty() ? '' : dockerPassword}", - STATUS_CODE : "${config.smokeTestStatusCode}" - ] - ) { - if(dockerCredentials.size() > 0) { - config.dockerUsername = dockerUsername - } - deployCfNative(config) - } - } -} - -private prepareBlueGreenCfNativeDeploy(config,script) { - if (config.smokeTestScript == 'blueGreenCheckScript.sh') { - writeFile file: config.smokeTestScript, text: libraryResource(config.smokeTestScript) - } - - if (config.smokeTestScript) { - config.smokeTest = '--smoke-test $(pwd)/' + config.smokeTestScript - sh "chmod +x ${config.smokeTestScript}" - } - - config.deployCommand = 'blue-green-deploy' - cfManifestSubstituteVariables( - script: script, - manifestFile: config.cloudFoundry.manifest, - manifestVariablesFiles: config.cloudFoundry.manifestVariablesFiles, - manifestVariables: config.cloudFoundry.manifestVariables - ) - handleLegacyCfManifest(config) - if (!config.keepOldInstance) { - config.deployOptions = '--delete-old-apps' - } -} - -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 - } -} - -private prepareCfPushCfNativeDeploy(config) { - config.deployCommand = 'push' - 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 -} - -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." - } - } -} - -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 && config.deployType != 'blue-green' ? "--docker-image ${config.deployDockerImage}" : null, - config.dockerUsername && config.deployType != 'blue-green' ? "--docker-username ${dockerUsername}" : null, - config.smokeTest, - config.cfNativeDeployParameters - ].findAll { s -> s != null && s != '' }.join(" ") - deploy(null, deployStatement, config, { c -> stopOldAppIfRunning(c) }) -} - -private deploy(String cfApiStatement, String cfDeployStatement, config, Closure postDeployAction) { - - withCredentials([usernamePassword( - credentialsId: config.cloudFoundry.credentialsId, - passwordVariable: 'password', - usernameVariable: 'username' - )]) { - def cfTraceFile = 'cf.log' - def deployScript = """#!/bin/bash - set +x - set -e - export HOME=${config.dockerWorkspace} - ${config.cfTrace ? "export CF_TRACE=${cfTraceFile}" : ""} - ${cfApiStatement ?: ''} - cf login -u \"${username}\" -p '${password}' -a ${config.cloudFoundry.apiEndpoint} -o \"${config.cloudFoundry.org}\" -s \"${config.cloudFoundry.space}\" ${config.loginParameters} - cf plugins - ${cfDeployStatement} - """ - - if (config.verbose) { - // Password contained in output below is hidden by withCredentials - echo "[INFO][${STEP_NAME}] Executing command: '${deployScript}'." - } - - try { - sh deployScript - } catch (e) { - handleCfCliLog(cfTraceFile) - - error "[${STEP_NAME}] ERROR: The execution of the deploy command failed, see the log for details." - } - if (config.verbose) { - handleCfCliLog(cfTraceFile) - } - - if (postDeployAction) postDeployAction(config) - - sh "cf logout" - } -} - -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}'" - } -} - -private void stopOldAppIfRunning(Map config) { - String oldAppName = "${config.cloudFoundry.appName}-old" - String cfStopOutputFileName = "${UUID.randomUUID()}-cfStopOutput.txt" - - if (config.keepOldInstance && config.deployType == 'blue-green') { - try { - sh "cf stop $oldAppName &> $cfStopOutputFileName" - } catch (e) { - String cfStopOutput = readFile(file: cfStopOutputFileName) - - if (!cfStopOutput.contains("$oldAppName not found")) { - error "[${STEP_NAME}] ERROR: Could not stop application $oldAppName. Error: $cfStopOutput" - } - } - } -} - -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, - ]] - influxWriteData script: script, customData: [:], customDataTags: [:], customDataMap: deploymentData, customDataMapTags: deploymentDataTags -}