mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-03-03 15:02:35 +02:00
ABAP deploy to front end server via existing fiori upload facilities (#1939)
The approach used up to now did not (... never) work since the uploaded content was always assigned to the `$TMP` package. In the meanwhile there is some fiori tooling available for managing such uploads. Now we re-use this fiori tooling. Co-authored-by: Oliver Feldmann <oliver.feldmann@sap.com>
This commit is contained in:
parent
dc3bf0e68d
commit
4f9bc698f9
@ -64,6 +64,80 @@ The properties can also be configured on a per-step basis:
|
||||
|
||||
The parameters can also be provided when the step is invoked. For examples see below.
|
||||
|
||||
## CTS Uploads
|
||||
|
||||
In order to be able to upload the application, it is required to build the application, e.g. via `npmExecute`
|
||||
or `mtaBuild`. The content of the app needs to be provided in a folder named `dist` in the root level of the project.
|
||||
Although the name of the step `transportRequestUploadFile` might suggest something else, in this case a folder needs
|
||||
to be provided. The application, which is provided in the `dist` folder is zipped and uploaded by the fiori toolset
|
||||
used for performing the upload.
|
||||
|
||||
For `CTS` related uploads we use a node based toolset. When running in a docker environment a standard node
|
||||
image can be used. In this case the required deploy tool dependencies will be installed prior to the deploy.
|
||||
It is also possible to provide a docker image which already contains the required deploy tool
|
||||
dependencies (`config.changeManagement.cts.nodeDocker.image`). In this case an empty list needs to be provided
|
||||
as `config.changeManagement.cts.deployToolDependencies`. Using an already pre-configured docker image speeds-up
|
||||
the deployment step, but comes with the disadvantage of having
|
||||
to maintain and provision the corresponding docker image.
|
||||
|
||||
When running in an environment without docker, it is recommanded to install the deploy tools manually on the
|
||||
system and to provide an empty list for the deploy tool dependencies (`config.changeManagement.cts.deployToolDependencies`).
|
||||
|
||||
### Examples
|
||||
|
||||
#### Upload based on preconfigured image
|
||||
|
||||
```groovy
|
||||
transportRequestUploadFile script: this,
|
||||
changeManagement: [
|
||||
credentialsId: 'CRED_ID', // credentials needs to be defined inside Jenkins
|
||||
type: 'CTS',
|
||||
endpoint: 'https://example.org:8000',
|
||||
client: '001',
|
||||
cts: [
|
||||
nodeDocker: [
|
||||
image: 'docker-image-name',
|
||||
pullImage: true, // needs to be set to false in case the image is
|
||||
// only available in the local docker cache (not recommended)
|
||||
],
|
||||
npmInstallOpts: [],
|
||||
deployToolDependencies: [], // empty since we use an already preconfigured image
|
||||
],
|
||||
],
|
||||
applicationName: 'APP',
|
||||
abapPackage: 'ABABPACKAGE',
|
||||
transportRequestId: 'XXXK123456', // can be omitted when resolved via commit history
|
||||
applicationDescription: 'An optional description' // only used in case a new application is deployed
|
||||
// description is not updated for re-deployments
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Upload based on a standard node image
|
||||
|
||||
```groovy
|
||||
transportRequestUploadFile script: this,
|
||||
changeManagement: [
|
||||
credentialsId: 'CRED_ID', // credentials needs to be defined inside Jenkins
|
||||
type: 'CTS',
|
||||
endpoint: 'https://example.org:8000',
|
||||
client: '001',
|
||||
cts: [
|
||||
npmInstallOpts: [
|
||||
'--verbose', // might be benefical for troubleshooting
|
||||
'--registry', 'https://your.npmregistry.org/', // an own registry can be specified here
|
||||
],
|
||||
],
|
||||
],
|
||||
applicationName: 'APP',
|
||||
abapPackage: 'ABABPACKAGE',
|
||||
transportRequestId: 'XXXK123456', // can be omitted when resolved via commit history
|
||||
applicationDescription: 'An optional description' // only used in case a new application is deployed
|
||||
// description is not updated for re-deployments
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Exceptions
|
||||
|
||||
* `IllegalArgumentException`:
|
||||
@ -91,15 +165,15 @@ transportRequestUploadFile(
|
||||
)
|
||||
// CTS
|
||||
|
||||
// NOTE: CTS upload currently not supported!
|
||||
|
||||
transportRequestUploadFile(
|
||||
script: this,
|
||||
transportRequestId: '001', // typically provided via git commit history
|
||||
filePath: '/path',
|
||||
changeManagement: [
|
||||
type: 'CTS'
|
||||
endpoint: 'https://example.org/cm'
|
||||
]
|
||||
endpoint: 'https://example.org/cm',
|
||||
client: '099',
|
||||
],
|
||||
applicationName: 'myApp',
|
||||
abapPackage: 'MYPACKAGE',
|
||||
)
|
||||
```
|
||||
|
@ -24,6 +24,18 @@ general:
|
||||
envVars: {}
|
||||
pullImage: true
|
||||
cts:
|
||||
osDeployUser: 'node'
|
||||
deployToolDependencies:
|
||||
- '@ui5/cli'
|
||||
- '@sap/ux-ui5-tooling'
|
||||
- '@ui5/logger'
|
||||
- '@ui5/fs'
|
||||
deployConfigFile: 'ui5-deploy.yaml'
|
||||
nodeDocker:
|
||||
image: 'node'
|
||||
options: []
|
||||
envVars: {}
|
||||
pullImage: true
|
||||
docker:
|
||||
image: 'ppiper/cm-client'
|
||||
options: []
|
||||
|
@ -172,29 +172,148 @@ public class ChangeManagement implements Serializable {
|
||||
void uploadFileToTransportRequestCTS(
|
||||
Map docker,
|
||||
String transportRequestId,
|
||||
String filePath,
|
||||
String endpoint,
|
||||
String credentialsId,
|
||||
String cmclientOpts = '') {
|
||||
String client,
|
||||
String applicationName,
|
||||
String description,
|
||||
String abapPackage, // "package" would be better, but this is a keyword
|
||||
String osDeployUser,
|
||||
def deployToolDependencies,
|
||||
def npmInstallOpts,
|
||||
String deployConfigFile,
|
||||
String credentialsId) {
|
||||
|
||||
def args = [
|
||||
'-tID', transportRequestId,
|
||||
"\"$filePath\""
|
||||
]
|
||||
def script = this.script
|
||||
|
||||
int rc = executeWithCredentials(
|
||||
BackendType.CTS,
|
||||
docker,
|
||||
endpoint,
|
||||
credentialsId,
|
||||
'upload-file-to-transport',
|
||||
args,
|
||||
false,
|
||||
cmclientOpts) as int
|
||||
def desc = description ?: 'Deployed with Piper based on SAP Fiori tools'
|
||||
|
||||
if(rc != 0) {
|
||||
throw new ChangeManagementException(
|
||||
"Cannot upload file into transport request. Return code from cm client: $rc.")
|
||||
if (deployToolDependencies in List) {
|
||||
deployToolDependencies = deployToolDependencies.join(' ')
|
||||
}
|
||||
|
||||
if (npmInstallOpts in List) {
|
||||
npmInstallOpts = npmInstallOpts.join(' ')
|
||||
}
|
||||
|
||||
deployToolDependencies = deployToolDependencies.trim()
|
||||
|
||||
/*
|
||||
In case the configuration has been adjusted so that no deployToolDependencies are provided
|
||||
we assume an image is used, which already contains all dependencies.
|
||||
In this case we don't invoke npm install and we run the image with the standard user
|
||||
already, since there is no need for being root. Hence we don't need to switch user also
|
||||
in the script.
|
||||
*/
|
||||
boolean noInstall = deployToolDependencies.isEmpty()
|
||||
|
||||
Iterable cmd = ['#!/bin/bash -e']
|
||||
|
||||
if (! noInstall) {
|
||||
cmd << "npm install --global ${npmInstallOpts} ${deployToolDependencies}"
|
||||
cmd << "su ${osDeployUser}"
|
||||
} else {
|
||||
script.echo "[INFO] no deploy dependencies provided. Skipping npm install call. Assuming docker image '${docker?.image}' already contains the dependencies for performing the deployment."
|
||||
}
|
||||
|
||||
Iterable params = []
|
||||
|
||||
boolean useConfigFile = true, noConfig = false
|
||||
|
||||
if (!deployConfigFile) {
|
||||
useConfigFile = false
|
||||
noConfig = !script.fileExists('ui5-deploy.yaml')
|
||||
} else {
|
||||
if (script.fileExists(deployConfigFile)) {
|
||||
// in this case we will use the config file
|
||||
useConfigFile = true
|
||||
} else {
|
||||
if (deployConfigFile == 'ui5-deploy.yaml') {
|
||||
// in this case this is most likely provided by the piper default config and
|
||||
// it was not explicitly configured. Hence we assume not having a config file
|
||||
useConfigFile = false
|
||||
noConfig = true
|
||||
} else {
|
||||
script.error("Configured deploy config file '${deployConfigFile}' does not exists.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noConfig) {
|
||||
params += ['--noConfig'] // no config file, but we will provide our parameters
|
||||
}
|
||||
|
||||
if (useConfigFile) {
|
||||
params += ['-c', "\"" + deployConfigFile + "\""]
|
||||
}
|
||||
|
||||
//
|
||||
// All the parameters below encapsulated in an if statement might also be provided in a config file.
|
||||
// In case they are empty we don't add to the command line and we trust in the config file.
|
||||
// In case they are finally missing the fiori deploy toolset will tell us.
|
||||
//
|
||||
|
||||
if (transportRequestId) {
|
||||
params += ['-t', transportRequestId]
|
||||
}
|
||||
|
||||
if (endpoint) {
|
||||
params += ['-u', endpoint]
|
||||
}
|
||||
|
||||
if (abapPackage) {
|
||||
params += ['-p', abapPackage]
|
||||
}
|
||||
|
||||
if (applicationName) {
|
||||
params += ['-n' , applicationName]
|
||||
}
|
||||
|
||||
if (client) {
|
||||
params += ['-l', client]
|
||||
}
|
||||
|
||||
params += ['-e', "\"" + desc + "\""]
|
||||
|
||||
params += ['-f'] // failfast --> provide return code != 0 in case of any failure
|
||||
|
||||
params += ['-y'] // autoconfirm --> no need to press 'y' key in order to confirm the params and trigger the deployment
|
||||
|
||||
// Here we provide the names of the environment variable holding username and password. Below we set these values.
|
||||
params += ['--username', 'ABAP_USER', '--password', 'ABAP_PASSWORD']
|
||||
|
||||
def fioriDeployCmd = "fiori deploy ${params.join(' ')}"
|
||||
script.echo "Executing deploy command: '${fioriDeployCmd}'"
|
||||
cmd << fioriDeployCmd
|
||||
|
||||
script.withCredentials([script.usernamePassword(
|
||||
credentialsId: credentialsId,
|
||||
passwordVariable: 'password',
|
||||
usernameVariable: 'username')]) {
|
||||
|
||||
/*
|
||||
After installing the deploy toolset we switch the user. Since we do not `su` with option `-l` the
|
||||
environment variables are preserved. Hence the environment variables for user and password are
|
||||
still available after the user switch.
|
||||
*/
|
||||
def dockerEnvVars = docker.envVars ?: [:]
|
||||
dockerEnvVars += [ABAP_USER: script.username, ABAP_PASSWORD: script.password]
|
||||
|
||||
def dockerOptions = docker.options ?: []
|
||||
if (!noInstall) {
|
||||
// when we install globally we need to be root, after preparing that we can su node` in the bash script.
|
||||
// in case there is already a u provided the latest (... what we set here wins).
|
||||
dockerOptions += ['-u', '0']
|
||||
}
|
||||
|
||||
script.dockerExecute(
|
||||
script: script,
|
||||
dockerImage: docker.image,
|
||||
dockerOptions: dockerOptions,
|
||||
dockerEnvVars: dockerEnvVars,
|
||||
dockerPullImage: docker.pullImage) {
|
||||
|
||||
script.sh script: cmd.join('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,48 +164,75 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
|
||||
@Test
|
||||
public void uploadFileToTransportRequestCTSSuccessTest() {
|
||||
|
||||
loggingRule.expect("[INFO] Uploading file '/path' to transport request '002'.")
|
||||
loggingRule.expect("[INFO] File '/path' has been successfully uploaded to transport request '002'.")
|
||||
loggingRule.expect("[INFO] Uploading application 'myApp' to transport request '002'.")
|
||||
loggingRule.expect("[INFO] Application 'myApp' has been successfully uploaded to transport request '002'.")
|
||||
|
||||
ChangeManagement cm = new ChangeManagement(nullScript) {
|
||||
void uploadFileToTransportRequestCTS(
|
||||
Map docker,
|
||||
String transportRequestId,
|
||||
String filePath,
|
||||
String endpoint,
|
||||
String credentialsId,
|
||||
String cmclientOpts) {
|
||||
String client,
|
||||
String appName,
|
||||
String appDescription,
|
||||
String abapPackage,
|
||||
String osDeployUser,
|
||||
def deployToolsDependencies,
|
||||
def npmInstallArgs,
|
||||
String deployConfigFile,
|
||||
String credentialsId) {
|
||||
|
||||
cmUtilReceivedParams.docker = docker
|
||||
cmUtilReceivedParams.transportRequestId = transportRequestId
|
||||
cmUtilReceivedParams.filePath = filePath
|
||||
cmUtilReceivedParams.endpoint = endpoint
|
||||
cmUtilReceivedParams.client = client
|
||||
cmUtilReceivedParams.appName = appName
|
||||
cmUtilReceivedParams.appDescription = appDescription
|
||||
cmUtilReceivedParams.abapPackage = abapPackage
|
||||
cmUtilReceivedParams.osDeployUser = osDeployUser
|
||||
cmUtilReceivedParams.deployToolDependencies = deployToolsDependencies
|
||||
cmUtilReceivedParams.npmInstallOpts = npmInstallArgs
|
||||
cmUtilReceivedParams.deployConfigFile = deployConfigFile
|
||||
cmUtilReceivedParams.credentialsId = credentialsId
|
||||
cmUtilReceivedParams.cmclientOpts = cmclientOpts
|
||||
}
|
||||
}
|
||||
|
||||
stepRule.step.transportRequestUploadFile(script: nullScript,
|
||||
changeManagement: [type: 'CTS'],
|
||||
changeManagement: [
|
||||
type: 'CTS',
|
||||
client: '001',
|
||||
cts: [
|
||||
osDeployUser: 'node2',
|
||||
deployToolDependencies: ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
npmInstallOpts: ['--verbose'],
|
||||
]
|
||||
],
|
||||
applicationName: 'myApp',
|
||||
applicationDescription: 'the description',
|
||||
abapPackage: 'myPackage',
|
||||
transportRequestId: '002',
|
||||
filePath: '/path',
|
||||
cmUtils: cm)
|
||||
|
||||
assert cmUtilReceivedParams ==
|
||||
[
|
||||
docker: [
|
||||
image: 'ppiper/cm-client',
|
||||
image: 'node',
|
||||
options:[],
|
||||
envVars:[:],
|
||||
pullImage:true
|
||||
],
|
||||
transportRequestId: '002',
|
||||
filePath: '/path',
|
||||
endpoint: 'https://example.org/cm',
|
||||
client: '001',
|
||||
appName: 'myApp',
|
||||
appDescription: 'the description',
|
||||
abapPackage: 'myPackage',
|
||||
osDeployUser: 'node2',
|
||||
deployToolDependencies: ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
npmInstallOpts: ['--verbose'],
|
||||
deployConfigFile: 'ui5-deploy.yaml',
|
||||
credentialsId: 'CM',
|
||||
cmclientOpts: ''
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.hasItem
|
||||
import static org.hamcrest.Matchers.is
|
||||
import static org.hamcrest.Matchers.not
|
||||
import static org.junit.Assert.assertThat
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
||||
import org.hamcrest.Matchers
|
||||
@ -24,6 +25,7 @@ import util.JenkinsScriptLoaderRule
|
||||
import util.JenkinsShellCallRule
|
||||
import util.JenkinsCredentialsRule
|
||||
import util.JenkinsDockerExecuteRule
|
||||
import util.JenkinsFileExistsRule
|
||||
import util.Rules
|
||||
|
||||
import hudson.AbortException
|
||||
@ -35,6 +37,7 @@ public class ChangeManagementTest extends BasePiperTest {
|
||||
private JenkinsShellCallRule script = new JenkinsShellCallRule(this)
|
||||
private JenkinsLoggingRule logging = new JenkinsLoggingRule(this)
|
||||
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
|
||||
private JenkinsFileExistsRule files = new JenkinsFileExistsRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules.getCommonRules(this)
|
||||
@ -43,6 +46,7 @@ public class ChangeManagementTest extends BasePiperTest {
|
||||
.around(logging)
|
||||
.around(new JenkinsCredentialsRule(this).withCredentials('me','user','password'))
|
||||
.around(dockerExecuteRule)
|
||||
.around(files)
|
||||
|
||||
@Test
|
||||
public void testRetrieveChangeDocumentIdOutsideGitWorkTreeTest() {
|
||||
@ -291,26 +295,183 @@ public void testGetCommandLineWithCMClientOpts() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadFileToTransportSucceedsCTS() {
|
||||
public void testUploadFileToTransportSucceedsCTSDeployConfigYamlExists() {
|
||||
|
||||
// the regex provided below is an implicit check that the command line is fine.
|
||||
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS upload-file-to-transport -tID 002 "/path"', 0)
|
||||
files.existingFiles.add('ui5-deploy.yaml')
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'ppiper/cmclient',
|
||||
image: 'node',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'/path',
|
||||
'https://example.org/cm',
|
||||
'me')
|
||||
'001',
|
||||
'myApp',
|
||||
'the description',
|
||||
'aPackage',
|
||||
'node2',
|
||||
['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
['--verbose'],
|
||||
'ui5-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
|
||||
assert dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cmclient'
|
||||
assert script.shell[0].contains('npm install --global --verbose @ui5/cli @sap/ux-ui5-tooling @ui5/logger @ui5/fs @dummy/foo')
|
||||
|
||||
assert script.shell[0].contains("fiori deploy -c \"ui5-deploy.yaml\" -t 002 -u https://example.org/cm")
|
||||
|
||||
assert dockerExecuteRule.getDockerParams().dockerImage == 'node'
|
||||
assert dockerExecuteRule.getDockerParams().dockerPullImage == true
|
||||
assert dockerExecuteRule.getDockerParams().dockerEnvVars == [ABAP_USER: "user", ABAP_PASSWORD: 'password']
|
||||
// we launch the container as root (uid 0) in order to be able to install
|
||||
// the deploytool. Before deploying we su to another user.
|
||||
assert dockerExecuteRule.getDockerParams().dockerOptions == ['-u', '0']
|
||||
}
|
||||
|
||||
// no assert for the shell command required here, since the regex registered
|
||||
// above to the script rule is an implicit check for the command line.
|
||||
@Test
|
||||
public void testUploadFileToTransportSucceedsCTSDefaultDeployConfigYamlDoesNotExist() {
|
||||
|
||||
// the file does not exist, since it was not explicity added to the files rule
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'node',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'https://example.org/cm',
|
||||
'001',
|
||||
'myApp',
|
||||
'the description',
|
||||
'aPackage',
|
||||
'node2',
|
||||
['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
['--verbose'],
|
||||
'ui5-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
|
||||
// more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
|
||||
assert script.shell[0].contains("fiori deploy --noConfig -t 002 -u https://example.org/cm")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadFileToTransportFailsCTSExplicitlyConfiguredDeployConfigYamlDoesNotExist() {
|
||||
|
||||
// the file does not exist, since it was not explicitly added to the files rule
|
||||
|
||||
thrown.expect(AbortException)
|
||||
thrown.expectMessage('Configured deploy config file \'my-deploy.yaml\' does not exists.')
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'node',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'https://example.org/cm',
|
||||
'001',
|
||||
'myApp',
|
||||
'the description',
|
||||
'aPackage',
|
||||
'node2',
|
||||
['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
['--verbose'],
|
||||
'my-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
|
||||
// more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
|
||||
assert script.shell[0].contains("fiori deploy --noConfig -t 002 -u https://example.org/cm")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadFileToTransportSucceesCTSExplicitlyConfiguredDeployConfigYamExists() {
|
||||
|
||||
files.existingFiles.add('my-deploy.yaml')
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'node',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'https://example.org/cm',
|
||||
'001',
|
||||
'myApp',
|
||||
'the description',
|
||||
'aPackage',
|
||||
'node2',
|
||||
['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
|
||||
['--verbose'],
|
||||
'my-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
|
||||
// more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
|
||||
assert script.shell[0].contains("fiori deploy -c \"my-deploy.yaml\" -t 002 -u https://example.org/cm")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadFileToTransportSucceedsEmptyDeployToolDependenciesCTS() {
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'fioriDeployImage',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'https://example.org/cm',
|
||||
'001',
|
||||
'myApp',
|
||||
'aPackage',
|
||||
'the description',
|
||||
'node2',
|
||||
[],
|
||||
[],
|
||||
'ui5-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
|
||||
assert ! script.shell[0].contains('npm install')
|
||||
assert ! script.shell[0].contains('su')
|
||||
|
||||
assert script.shell[0].contains("fiori deploy")
|
||||
|
||||
assert dockerExecuteRule.getDockerParams().dockerImage == 'fioriDeployImage'
|
||||
assert dockerExecuteRule.getDockerParams().dockerPullImage == true
|
||||
assert dockerExecuteRule.getDockerParams().dockerEnvVars == [ABAP_USER: "user", ABAP_PASSWORD: 'password']
|
||||
// we don't start with the root user since there is no need to install something (globally)
|
||||
assert dockerExecuteRule.getDockerParams().dockerOptions == []
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadFileToTransportShellFailsCTS() {
|
||||
|
||||
thrown.expect(AbortException)
|
||||
thrown.expectMessage('script returned exit code 1')
|
||||
|
||||
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*fiori deploy.*',
|
||||
{ throw new AbortException('script returned exit code 1') })
|
||||
|
||||
new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
|
||||
[
|
||||
image: 'node',
|
||||
pullImage: true
|
||||
],
|
||||
'002',
|
||||
'https://example.org/cm',
|
||||
'001',
|
||||
'myApp',
|
||||
'aPackage',
|
||||
'the description',
|
||||
'node',
|
||||
'@ui5/cli @sap/ux-ui5-tooling @ui5/logger @ui5/fs',
|
||||
[],
|
||||
'ui5-deploy.yaml',
|
||||
'me',
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -74,16 +74,61 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati
|
||||
* A pattern used for identifying lines holding the transport request id.
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'changeManagement/transportRequestLabel'
|
||||
'transportRequestLabel',
|
||||
/**
|
||||
* Some CTS related transport related steps are cm_client based, others are node based.
|
||||
* For the node based steps the docker image is specified here.
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'cts/nodeDocker/image',
|
||||
/**
|
||||
* The ABAP client. Only for `CTS`
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'client',
|
||||
/**
|
||||
* By default we use a standard node docker image and prepare some fiori related packages
|
||||
* before performing the deployment. For that we need to launch the image with root privileges.
|
||||
* After that, before actually performing the deployment we swith to a non root user. This user
|
||||
* can be specified here.
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'cts/osDeployUser',
|
||||
/**
|
||||
* By default we use a standard node docker iamge and prepare some fiori related packages
|
||||
* performing the deployment. The additional dependencies can be provided here. In case you
|
||||
* use an already prepared docker image which contains the required dependencies, the empty
|
||||
* list can be provide here. Caused hereby installing additional dependencies will be skipped.
|
||||
*
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'cts/deployToolDependencies',
|
||||
/**
|
||||
* A list containing additional options for the npm install call. `-g`, `--global` is always assumed.
|
||||
* Can be used for e.g. providing custom registries (`--registry https://your.registry.com`) or
|
||||
* for providing the verbose flag (`--verbose`) for troubleshooting.
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'cts/npmInstallOpts',
|
||||
/**
|
||||
* The file handed over to `fiori deploy` with flag `-c --config`.
|
||||
* @parentConfigKey changeManagement
|
||||
*/
|
||||
'cts/deployConfigFile',
|
||||
]
|
||||
|
||||
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
|
||||
'applicationName', // RFC
|
||||
/** The name of the application. `RFC` and `CTS` only. */
|
||||
'applicationName', // RFC, CTS
|
||||
/** The id of the application. Only for `SOLMAN`.*/
|
||||
'applicationId', // SOLMAN
|
||||
/** The application description, `RFC` and `CTS` only. For `CTS`: the desription is only
|
||||
taken into account for a new upload. In case of an update the description will not be
|
||||
updated.
|
||||
*/
|
||||
'applicationDescription',
|
||||
/** The path of the file to upload.*/
|
||||
'filePath', // SOLMAN, CTS
|
||||
/** The path of the file to upload, Only for `SOLMAN`.*/
|
||||
'filePath', // SOLMAN
|
||||
/** The URL where to find the UI5 package to upload to the transport request. Only for `RFC`. */
|
||||
'applicationUrl', // RFC
|
||||
/** The ABAP package name of your application. */
|
||||
@ -133,11 +178,12 @@ void call(Map parameters = [:]) {
|
||||
.withMandatoryProperty('changeManagement/clientOpts')
|
||||
.withMandatoryProperty('changeManagement/credentialsId')
|
||||
.withMandatoryProperty('changeManagement/endpoint')
|
||||
.withMandatoryProperty('changeManagement/client', null, {backendType == BackendType.CTS})
|
||||
.withMandatoryProperty('changeManagement/type')
|
||||
.withMandatoryProperty('changeManagement/git/from')
|
||||
.withMandatoryProperty('changeManagement/git/to')
|
||||
.withMandatoryProperty('changeManagement/git/format')
|
||||
.withMandatoryProperty('filePath', null, { backendType in [BackendType.SOLMAN, BackendType.CTS] })
|
||||
.withMandatoryProperty('filePath', null, { backendType == BackendType.SOLMAN })
|
||||
.withMandatoryProperty('applicationUrl', null, { backendType == BackendType.RFC })
|
||||
.withMandatoryProperty('codePage', null, { backendType == BackendType.RFC })
|
||||
.withMandatoryProperty('acceptUnixStyleLineEndings', null, { backendType == BackendType.RFC })
|
||||
@ -148,9 +194,9 @@ void call(Map parameters = [:]) {
|
||||
.withMandatoryProperty('changeManagement/rfc/docker/envVars', null, {backendType == BackendType.RFC})
|
||||
.withMandatoryProperty('changeManagement/rfc/docker/pullImage', null, {backendType == BackendType.RFC})
|
||||
.withMandatoryProperty('applicationDescription', null, { backendType == BackendType.RFC })
|
||||
.withMandatoryProperty('abapPackage', null, { backendType == BackendType.RFC })
|
||||
.withMandatoryProperty('abapPackage', null, { backendType in [BackendType.RFC, BackendType.CTS] })
|
||||
.withMandatoryProperty('applicationId', null, {backendType == BackendType.SOLMAN})
|
||||
.withMandatoryProperty('applicationName', null, {backendType == BackendType.RFC})
|
||||
.withMandatoryProperty('applicationName', null, {backendType in [BackendType.RFC, BackendType.CTS]})
|
||||
.withMandatoryProperty('failOnWarning', null, {backendType == BackendType.RFC})
|
||||
.withMandatoryProperty('verbose', null, {backendType == BackendType.RFC})
|
||||
|
||||
@ -184,20 +230,15 @@ void call(Map parameters = [:]) {
|
||||
"Transport request id not provided (parameter: \'transportRequestId\' provided to the step call or via commit history).")
|
||||
.use()
|
||||
|
||||
def uploadingMessage = ['[INFO] Uploading file ' +
|
||||
"'${backendType == BackendType.RFC ? configuration.applicationUrl : configuration.filePath}' " +
|
||||
"to transport request '${configuration.transportRequestId}'"]
|
||||
if(backendType == BackendType.SOLMAN)
|
||||
uploadingMessage << " of change document '${configuration.changeDocumentId}'"
|
||||
uploadingMessage << '.'
|
||||
|
||||
echo uploadingMessage.join()
|
||||
|
||||
try {
|
||||
|
||||
switch(backendType) {
|
||||
|
||||
case BackendType.SOLMAN:
|
||||
|
||||
echo "[INFO] Uploading file '${configuration.filePath}' to transport request '${configuration.transportRequestId}'" +
|
||||
" of change document '${configuration.changeDocumentId}'."
|
||||
|
||||
cm.uploadFileToTransportRequestSOLMAN(
|
||||
configuration.changeManagement.solman?.docker ?: [:],
|
||||
configuration.changeDocumentId,
|
||||
@ -207,18 +248,36 @@ void call(Map parameters = [:]) {
|
||||
configuration.changeManagement.endpoint,
|
||||
configuration.changeManagement.credentialsId,
|
||||
configuration.changeManagement.clientOpts)
|
||||
|
||||
echo "[INFO] File '${configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}'" +
|
||||
" of change document '${configuration.changeDocumentId}'."
|
||||
|
||||
break
|
||||
case BackendType.CTS:
|
||||
|
||||
echo "[INFO] Uploading application '${configuration.applicationName}' to transport request '${configuration.transportRequestId}'."
|
||||
|
||||
cm.uploadFileToTransportRequestCTS(
|
||||
configuration.changeManagement.cts?.docker ?: [:],
|
||||
configuration.changeManagement.cts?.nodeDocker ?: [:],
|
||||
configuration.transportRequestId,
|
||||
configuration.filePath,
|
||||
configuration.changeManagement.endpoint,
|
||||
configuration.changeManagement.credentialsId,
|
||||
configuration.changeManagement.clientOpts)
|
||||
configuration.changeManagement.client,
|
||||
configuration.applicationName,
|
||||
configuration.applicationDescription,
|
||||
configuration.abapPackage,
|
||||
configuration.changeManagement.cts.osDeployUser,
|
||||
configuration.changeManagement.cts.deployToolDependencies,
|
||||
configuration.changeManagement.cts.npmInstallOpts,
|
||||
configuration.changeManagement.cts.deployConfigFile,
|
||||
configuration.changeManagement.credentialsId)
|
||||
|
||||
echo "[INFO] Application '${configuration.applicationName}' has been successfully uploaded to transport request '${configuration.transportRequestId}'."
|
||||
|
||||
break
|
||||
case BackendType.RFC:
|
||||
|
||||
echo "[INFO] Uploading file '${configuration.applicationUrl}' to transport request '${configuration.transportRequestId}'."
|
||||
|
||||
cm.uploadFileToTransportRequestRFC(
|
||||
configuration.changeManagement.rfc.docker ?: [:],
|
||||
configuration.transportRequestId,
|
||||
@ -236,19 +295,13 @@ void call(Map parameters = [:]) {
|
||||
configuration.verbose
|
||||
)
|
||||
|
||||
break
|
||||
echo "[INFO] File 'configuration.applicationUrl' has been successfully uploaded to transport request '${configuration.transportRequestId}'."
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
} catch(ChangeManagementException ex) {
|
||||
throw new AbortException(ex.getMessage())
|
||||
}
|
||||
|
||||
|
||||
def uploadedMessage = ["[INFO] File '${backendType == BackendType.RFC ? configuration.applicationUrl : configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}'"]
|
||||
if(backendType == BackendType.SOLMAN)
|
||||
uploadedMessage << " of change document '${configuration.changeDocumentId}'"
|
||||
uploadedMessage << '.'
|
||||
echo uploadedMessage.join()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user