mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-14 11:03:09 +02:00
e54f18e6bc
Introduce xs deploy
480 lines
16 KiB
Groovy
480 lines
16 KiB
Groovy
import static org.junit.Assert.assertThat
|
|
|
|
import org.hamcrest.Matchers
|
|
|
|
import static org.hamcrest.Matchers.allOf
|
|
import static org.hamcrest.Matchers.contains
|
|
import static org.hamcrest.Matchers.containsString
|
|
import static org.hamcrest.Matchers.hasSize
|
|
import static org.hamcrest.Matchers.is
|
|
|
|
import org.junit.Rule
|
|
import org.junit.Test
|
|
import org.junit.rules.ExpectedException
|
|
import org.junit.rules.RuleChain
|
|
|
|
import util.BasePiperTest
|
|
import util.CommandLineMatcher
|
|
import util.JenkinsCredentialsRule
|
|
import util.JenkinsDockerExecuteRule
|
|
import util.JenkinsFileExistsRule
|
|
import util.JenkinsLockRule
|
|
import util.JenkinsLoggingRule
|
|
import util.JenkinsReadYamlRule
|
|
import util.JenkinsShellCallRule
|
|
import util.JenkinsStepRule
|
|
import util.Rules
|
|
|
|
import com.sap.piper.JenkinsUtils
|
|
|
|
import hudson.AbortException
|
|
|
|
class XsDeployTest extends BasePiperTest {
|
|
|
|
private ExpectedException thrown = ExpectedException.none()
|
|
|
|
private List existingFiles = [
|
|
'.xsconfig',
|
|
'myApp.mta'
|
|
]
|
|
|
|
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
|
|
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
|
private JenkinsLockRule lockRule = new JenkinsLockRule(this)
|
|
private JenkinsLoggingRule logRule = new JenkinsLoggingRule(this)
|
|
|
|
@Rule
|
|
public RuleChain ruleChain = Rules.getCommonRules(this)
|
|
.around(new JenkinsReadYamlRule(this))
|
|
.around(stepRule)
|
|
.around(new JenkinsDockerExecuteRule(this))
|
|
.around(new JenkinsCredentialsRule(this)
|
|
.withCredentials('myCreds', 'cred_xs', 'topSecret'))
|
|
.around(new JenkinsFileExistsRule(this, existingFiles))
|
|
.around(lockRule)
|
|
.around(shellRule)
|
|
.around(logRule)
|
|
.around(thrown)
|
|
|
|
@Test
|
|
public void testSanityChecks() {
|
|
|
|
thrown.expect(IllegalArgumentException)
|
|
thrown.expectMessage(
|
|
allOf(
|
|
containsString('ERROR - NO VALUE AVAILABLE FOR:'),
|
|
containsString('apiUrl'),
|
|
containsString('org'),
|
|
containsString('space'),
|
|
containsString('mtaPath')))
|
|
|
|
stepRule.step.xsDeploy(script: nullScript)
|
|
}
|
|
|
|
@Test
|
|
public void testLoginFailed() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage('xs login failed')
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '#!/bin/bash xs login .*', 1)
|
|
|
|
try {
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mtaPath: 'myApp.mta'
|
|
)
|
|
} catch(AbortException e ) {
|
|
|
|
assertThat(shellRule.shell,
|
|
allOf(
|
|
// first item: the login attempt
|
|
// second item: we try to provide the logs
|
|
hasSize(2),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs login'),
|
|
new CommandLineMatcher()
|
|
.hasProlog('LOG_FOLDER')
|
|
.hasSnippet('cat \\$\\{LOG_FOLDER\\}/\\*')
|
|
)
|
|
)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDeployFailed() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage('Failed command(s): [xs deploy]. Check earlier log for details.')
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '#!/bin/bash.*xs deploy .*', {throw new AbortException()})
|
|
|
|
try {
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mtaPath: 'myApp.mta'
|
|
)
|
|
} catch(AbortException e ) {
|
|
|
|
assertThat(shellRule.shell,
|
|
allOf(
|
|
hasSize(4),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs login'),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs deploy'),
|
|
new CommandLineMatcher()
|
|
.hasProlog('#!/bin/bash')
|
|
.hasSnippet('xs logout'), // logout must be present in case deployment failed.
|
|
new CommandLineMatcher()
|
|
.hasProlog('')
|
|
.hasSnippet('rm \\$\\{XSCONFIG\\}') // remove the session file after logout
|
|
)
|
|
)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testNothingHappensWhenModeIsNone() {
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
mode: 'NONE'
|
|
)
|
|
|
|
assertThat(logRule.log, containsString('Deployment skipped intentionally.'))
|
|
assertThat(shellRule.shell, hasSize(0))
|
|
}
|
|
|
|
@Test
|
|
public void testDeploymentFailsWhenDeployableIsNotPresent() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage('Deployable \'myApp.mta\' does not exist.')
|
|
|
|
existingFiles.remove('myApp.mta')
|
|
|
|
try {
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mtaPath: 'myApp.mta'
|
|
)
|
|
} catch(AbortException e) {
|
|
|
|
// no shell operation happened in this case.
|
|
assertThat(shellRule.shell.size(), is(0))
|
|
|
|
throw e
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDeployStraighForward() {
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
deployOpts: '-t 60',
|
|
mtaPath: 'myApp.mta'
|
|
)
|
|
|
|
assertThat(shellRule.shell,
|
|
allOf(
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash xs login")
|
|
.hasSnippet('xs login')
|
|
.hasOption('a', 'https://example.org/xs')
|
|
.hasOption('u', 'cred_xs')
|
|
.hasSingleQuotedOption('p', 'topSecret')
|
|
.hasOption('o', 'myOrg')
|
|
.hasOption('s', 'mySpace'),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs deploy')
|
|
.hasOption('t', '60')
|
|
.hasArgument('\'myApp.mta\''),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs logout')
|
|
)
|
|
)
|
|
|
|
assertThat(lockRule.getLockResources(), contains('xsDeploy:https://example.org/xs:myOrg:mySpace'))
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testInvalidDeploymentModeProviced() {
|
|
|
|
thrown.expect(IllegalArgumentException)
|
|
thrown.expectMessage('No enum constant')
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
deployOpts: '-t 60',
|
|
mtaPath: 'myApp.mta',
|
|
mode: 'DOES_NOT_EXIST'
|
|
)
|
|
}
|
|
|
|
@Test
|
|
public void testActionProvidedForStandardDeployment() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage(
|
|
'Cannot perform action \'resume\' in mode \'deploy\'. Only action \'none\' is allowed.')
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
deployOpts: '-t 60',
|
|
mtaPath: 'myApp.mta',
|
|
mode: 'DEPLOY', // this is the default anyway
|
|
action: 'RESUME'
|
|
)
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployFailes() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage('Failed command(s): [xs bg-deploy]')
|
|
|
|
logRule.expect('Something went wrong')
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '#!/bin/bash.*xs bg-deploy .*',
|
|
{ throw new AbortException('Something went wrong.') })
|
|
|
|
try {
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mtaPath: 'myApp.mta',
|
|
mode: 'BG_DEPLOY'
|
|
)
|
|
} catch(AbortException e) {
|
|
|
|
// in case there is a deployment failure we have to logout also for bg-deployments
|
|
assertThat(shellRule.shell,
|
|
new CommandLineMatcher()
|
|
.hasProlog('#!/bin/bash')
|
|
.hasSnippet('xs logout')
|
|
)
|
|
|
|
throw e
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployStraighForward() {
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '#!/bin/bash.*xs bg-deploy .*',
|
|
((CharSequence)''' |
|
|
|
|
|
|Uploading 1 files:
|
|
|/myFolder/my.mtar
|
|
|File upload finished
|
|
|
|
|
|Detected MTA schema version: "3.1.0"
|
|
|Detected deploy target as "myOrg mySpace"
|
|
|Detected deployed MTA with ID "my_mta" and version "0.0.1"
|
|
|Deployed MTA color: blue
|
|
|New MTA color: green
|
|
|Detected new MTA version: "0.0.1"
|
|
|Deployed MTA version: 0.0.1
|
|
|Service "xxx" is not modified and will not be updated
|
|
|Creating application "db-green" from MTA module "xx"...
|
|
|Uploading application "xx-green"...
|
|
|Staging application "xx-green"...
|
|
|Application "xx-green" staged
|
|
|Executing task "deploy" on application "xx-green"...
|
|
|Task execution status: succeeded
|
|
|Process has entered validation phase. After testing your new deployment you can resume or abort the process.
|
|
|Use "xs bg-deploy -i 1234 -a resume" to resume the process.
|
|
|Use "xs bg-deploy -i 1234 -a abort" to abort the process.
|
|
|Hint: Use the '--no-confirm' option of the bg-deploy command to skip this phase.
|
|
|''').stripMargin())
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
deployOpts: '-t 60',
|
|
mtaPath: 'myApp.mta',
|
|
mode: 'BG_DEPLOY'
|
|
)
|
|
|
|
assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, is('1234'))
|
|
|
|
assertThat(shellRule.shell,
|
|
allOf(
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash xs login")
|
|
.hasOption('a', 'https://example.org/xs')
|
|
.hasOption('u', 'cred_xs')
|
|
.hasSingleQuotedOption('p', 'topSecret')
|
|
.hasOption('o', 'myOrg')
|
|
.hasOption('s', 'mySpace'),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasOption('t', '60')
|
|
.hasArgument('\'myApp.mta\''),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
)
|
|
)
|
|
|
|
assertThat(lockRule.getLockResources(), contains('xsDeploy:https://example.org/xs:myOrg:mySpace'))
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployResumeWithoutDeploymentId() {
|
|
|
|
// this happens in case we would like to complete a deployment without having a (successful) deployments before.
|
|
|
|
thrown.expect(IllegalArgumentException)
|
|
thrown.expectMessage(
|
|
allOf(
|
|
containsString('No deployment id provided'),
|
|
containsString('Was there a deployment before?')))
|
|
|
|
nullScript.commonPipelineEnvironment.xsDeploymentId = null // is null anyway, just for clarification
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mode: 'BG_DEPLOY',
|
|
action: 'RESUME'
|
|
)
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployWithoutExistingSession() {
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage(
|
|
'For the current configuration an already existing session is required.' +
|
|
' But there is no already existing session')
|
|
|
|
existingFiles.remove('.xsconfig')
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mode: 'BG_DEPLOY',
|
|
action: 'RESUME'
|
|
)
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployResumeFails() {
|
|
|
|
// e.g. we try to resume a deployment which did not succeed or which was already resumed or aborted.
|
|
|
|
thrown.expect(AbortException)
|
|
thrown.expectMessage('Failed command(s): [xs bg-deploy -a resume].')
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'xs bg-deploy -i .*', 1)
|
|
|
|
nullScript.commonPipelineEnvironment.xsDeploymentId = '1234'
|
|
|
|
try {
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mode: 'BG_DEPLOY',
|
|
action: 'RESUME'
|
|
)
|
|
} catch(AbortException e) {
|
|
|
|
// logout must happen also in case of a failed deployment
|
|
assertThat(shellRule.shell,
|
|
new CommandLineMatcher()
|
|
.hasProlog('')
|
|
.hasSnippet('xs logout'))
|
|
throw e
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testBlueGreenDeployResume() {
|
|
|
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'xs bg-deploy -i .*', 0)
|
|
|
|
nullScript.commonPipelineEnvironment.xsDeploymentId = '1234'
|
|
|
|
stepRule.step.xsDeploy(
|
|
script: nullScript,
|
|
apiUrl: 'https://example.org/xs',
|
|
org: 'myOrg',
|
|
space: 'mySpace',
|
|
credentialsId: 'myCreds',
|
|
mode: 'BG_DEPLOY',
|
|
action: 'RESUME'
|
|
)
|
|
|
|
// there is no login in case of a resume since we have to use the old session which triggered the deployment.
|
|
assertThat(shellRule.shell,
|
|
allOf(
|
|
hasSize(3),
|
|
new CommandLineMatcher()
|
|
.hasProlog('#!/bin/bash')
|
|
.hasSnippet('xs bg-deploy')
|
|
.hasOption('i', '1234')
|
|
.hasOption('a', 'resume'),
|
|
new CommandLineMatcher()
|
|
.hasProlog("#!/bin/bash")
|
|
.hasSnippet('xs logout'),
|
|
new CommandLineMatcher()
|
|
.hasProlog('')
|
|
.hasSnippet('rm \\$\\{XSCONFIG\\}') // delete the session file after logout
|
|
)
|
|
)
|
|
|
|
assertThat(lockRule.getLockResources(), contains('xsDeploy:https://example.org/xs:myOrg:mySpace'))
|
|
|
|
}
|
|
|
|
} |