mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-04-15 11:56:45 +02:00
TMS integration (#782)
This commit is contained in:
parent
338314b08a
commit
cb245b1ce2
9
documentation/docs/steps/tmsUpload.md
Normal file
9
documentation/docs/steps/tmsUpload.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# ${docGenStepName}
|
||||||
|
|
||||||
|
## ${docGenDescription}
|
||||||
|
|
||||||
|
## ${docGenParameters}
|
||||||
|
|
||||||
|
## ${docGenConfiguration}
|
||||||
|
|
||||||
|
## ${docJenkinsPluginDependencies}
|
@ -535,6 +535,10 @@ steps:
|
|||||||
active: false
|
active: false
|
||||||
checkChangeInDevelopment:
|
checkChangeInDevelopment:
|
||||||
failIfStatusIsNotInDevelopment: true
|
failIfStatusIsNotInDevelopment: true
|
||||||
|
tmsUpload:
|
||||||
|
namedUser: 'Piper-Pipeline'
|
||||||
|
stashContent:
|
||||||
|
- 'buildResult'
|
||||||
transportRequestCreate:
|
transportRequestCreate:
|
||||||
developmentSystemId: null
|
developmentSystemId: null
|
||||||
verbose: false
|
verbose: false
|
||||||
|
@ -104,3 +104,7 @@ String getIssueCommentTriggerAction() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getJobStartedByUserId() {
|
||||||
|
return getRawBuild().getCause(hudson.model.Cause.UserIdCause.class)?.getUserId()
|
||||||
|
}
|
||||||
|
137
src/com/sap/piper/integration/TransportManagementService.groovy
Normal file
137
src/com/sap/piper/integration/TransportManagementService.groovy
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package com.sap.piper.integration
|
||||||
|
|
||||||
|
import com.sap.piper.JsonUtils
|
||||||
|
|
||||||
|
class TransportManagementService implements Serializable {
|
||||||
|
|
||||||
|
final Script script
|
||||||
|
final Map config
|
||||||
|
|
||||||
|
def jsonUtils = new JsonUtils()
|
||||||
|
|
||||||
|
TransportManagementService(Script script, Map config) {
|
||||||
|
this.script = script
|
||||||
|
this.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
def authentication(String uaaUrl, String oauthClientId, String oauthClientSecret) {
|
||||||
|
echo("OAuth Token retrieval started.")
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo("UAA-URL: '${uaaUrl}', ClientId: '${oauthClientId}''")
|
||||||
|
}
|
||||||
|
|
||||||
|
def encodedUsernameColonPassword = "${oauthClientId}:${oauthClientSecret}".bytes.encodeBase64().toString()
|
||||||
|
def urlEncodedFormData = "grant_type=password&" +
|
||||||
|
"username=${urlEncodeAndReplaceSpace(oauthClientId)}&" +
|
||||||
|
"password=${urlEncodeAndReplaceSpace(oauthClientSecret)}"
|
||||||
|
|
||||||
|
def parameters = [
|
||||||
|
url : "${uaaUrl}/oauth/token/?grant_type=client_credentials&response_type=token",
|
||||||
|
httpMode : "POST",
|
||||||
|
requestBody : urlEncodedFormData,
|
||||||
|
customHeaders: [
|
||||||
|
[
|
||||||
|
maskValue: false,
|
||||||
|
name : 'Content-Type',
|
||||||
|
value : 'application/x-www-form-urlencoded'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
maskValue: true,
|
||||||
|
name : 'authorization',
|
||||||
|
value : "Basic ${encodedUsernameColonPassword}"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
def response = sendApiRequest(parameters)
|
||||||
|
echo("OAuth Token retrieved successfully.")
|
||||||
|
|
||||||
|
return jsonUtils.jsonStringToGroovyObject(response).access_token
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def uploadFile(String url, String token, String file, String namedUser) {
|
||||||
|
|
||||||
|
echo("File upload started.")
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo("URL: '${url}', File: '${file}'")
|
||||||
|
}
|
||||||
|
|
||||||
|
script.sh """#!/bin/sh -e
|
||||||
|
curl -H 'Authorization: Bearer ${token}' -F 'file=@${file}' -F 'namedUser=${namedUser}' -o responseFileUpload.txt --fail '${url}/v2/files/upload'
|
||||||
|
"""
|
||||||
|
|
||||||
|
def responseContent = script.readFile("responseFileUpload.txt")
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo("${responseContent}")
|
||||||
|
}
|
||||||
|
|
||||||
|
echo("File upload successful.")
|
||||||
|
|
||||||
|
return jsonUtils.jsonStringToGroovyObject(responseContent)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def uploadFileToNode(String url, String token, String nodeName, int fileId, String description, String namedUser) {
|
||||||
|
|
||||||
|
echo("Node upload started.")
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo("URL: '${url}', NodeName: '${nodeName}', FileId: '${fileId}''")
|
||||||
|
}
|
||||||
|
|
||||||
|
def bodyMap = [nodeName: nodeName, contentType: 'MTA', description: description, storageType: 'FILE', namedUser: namedUser, entries: [[uri: fileId]]]
|
||||||
|
|
||||||
|
def parameters = [
|
||||||
|
url : "${url}/v2/nodes/upload",
|
||||||
|
httpMode : "POST",
|
||||||
|
contentType : 'APPLICATION_JSON',
|
||||||
|
requestBody : jsonUtils.groovyObjectToPrettyJsonString(bodyMap),
|
||||||
|
customHeaders: [
|
||||||
|
[
|
||||||
|
maskValue: true,
|
||||||
|
name : 'authorization',
|
||||||
|
value : "Bearer ${token}"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
def response = sendApiRequest(parameters)
|
||||||
|
echo("Node upload successful.")
|
||||||
|
|
||||||
|
return jsonUtils.jsonStringToGroovyObject(response)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendApiRequest(parameters) {
|
||||||
|
def defaultParameters = [
|
||||||
|
acceptType : 'APPLICATION_JSON',
|
||||||
|
quiet : !config.verbose,
|
||||||
|
consoleLogResponseBody: !config.verbose,
|
||||||
|
ignoreSslErrors : true,
|
||||||
|
validResponseCodes : "100:399"
|
||||||
|
]
|
||||||
|
|
||||||
|
def response = script.httpRequest(defaultParameters + parameters)
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo("Received response '${response.content}' with status ${response.status}.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.content
|
||||||
|
}
|
||||||
|
|
||||||
|
private echo(message) {
|
||||||
|
script.echo "[${getClass().getSimpleName()}] ${message}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String urlEncodeAndReplaceSpace(String data) {
|
||||||
|
return URLEncoder.encode(data, "UTF-8").replace('%20', '+')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
182
test/groovy/TmsUploadTest.groovy
Normal file
182
test/groovy/TmsUploadTest.groovy
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import com.sap.piper.JenkinsUtils
|
||||||
|
import com.sap.piper.integration.TransportManagementService
|
||||||
|
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.*
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString
|
||||||
|
import static org.hamcrest.Matchers.is
|
||||||
|
import static org.hamcrest.Matchers.not
|
||||||
|
import static org.junit.Assert.assertThat
|
||||||
|
|
||||||
|
public class TmsUploadTest extends BasePiperTest {
|
||||||
|
|
||||||
|
private ExpectedException thrown = new ExpectedException()
|
||||||
|
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
|
||||||
|
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
|
||||||
|
private JenkinsEnvironmentRule envRule = new JenkinsEnvironmentRule(this)
|
||||||
|
|
||||||
|
def tmsStub
|
||||||
|
def jenkinsUtilsStub
|
||||||
|
def calledTmsMethodsWithArgs = []
|
||||||
|
def uri = "https://dummy-url.com"
|
||||||
|
def uaaUrl = "https://oauth.com"
|
||||||
|
def oauthClientId = "myClientId"
|
||||||
|
def oauthClientSecret = "myClientSecret"
|
||||||
|
def serviceKeyContent = """{
|
||||||
|
"uri": "${uri}",
|
||||||
|
"uaa": {
|
||||||
|
"clientid": "${oauthClientId}",
|
||||||
|
"clientsecret": "${oauthClientSecret}",
|
||||||
|
"url": "${uaaUrl}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
class JenkinsUtilsMock extends JenkinsUtils {
|
||||||
|
def userId
|
||||||
|
|
||||||
|
JenkinsUtilsMock(userId) {
|
||||||
|
this.userId = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
def getJobStartedByUserId(){
|
||||||
|
return this.userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public RuleChain ruleChain = Rules.getCommonRules(this)
|
||||||
|
.around(thrown)
|
||||||
|
.around(new JenkinsReadYamlRule(this))
|
||||||
|
.around(stepRule)
|
||||||
|
.around(loggingRule)
|
||||||
|
.around(envRule)
|
||||||
|
.around(new JenkinsCredentialsRule(this)
|
||||||
|
.withCredentials('TMS_ServiceKey', serviceKeyContent))
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
tmsStub = mockTransportManagementService()
|
||||||
|
helper.registerAllowedMethod("unstash", [String.class], { s -> return [s] })
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
void tearDown() {
|
||||||
|
calledTmsMethodsWithArgs.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minimalConfig__isSuccessful() {
|
||||||
|
jenkinsUtilsStub = new JenkinsUtilsMock("Test User")
|
||||||
|
binding.workspace = "."
|
||||||
|
envRule.env.gitCommitId = "testCommitId"
|
||||||
|
|
||||||
|
stepRule.step.tmsUpload(
|
||||||
|
script: nullScript,
|
||||||
|
juStabUtils: utils,
|
||||||
|
jenkinsUtilsStub: jenkinsUtilsStub,
|
||||||
|
transportManagementService: tmsStub,
|
||||||
|
mtaPath: 'dummy.mtar',
|
||||||
|
nodeName: 'myNode',
|
||||||
|
credentialsId: 'TMS_ServiceKey'
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(calledTmsMethodsWithArgs[0], is("authentication('${uaaUrl}', '${oauthClientId}', '${oauthClientSecret}')"))
|
||||||
|
assertThat(calledTmsMethodsWithArgs[1], is("uploadFile('${uri}', 'myToken', './dummy.mtar', 'Test User')"))
|
||||||
|
assertThat(calledTmsMethodsWithArgs[2], is("uploadFileToNode('${uri}', 'myToken', 'myNode', '1234', 'Git CommitId: testCommitId')"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] File './dummy.mtar' successfully uploaded to Node 'myNode' (Id: '1000')."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Corresponding Transport Request: 'Git CommitId: testCommitId' (Id: '2000')"))
|
||||||
|
assertThat(loggingRule.log, not(containsString("[TransportManagementService] CredentialsId: 'TMS_ServiceKey'")))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verboseMode__yieldsMoreEchos() {
|
||||||
|
jenkinsUtilsStub = new JenkinsUtilsMock("Test User")
|
||||||
|
binding.workspace = "."
|
||||||
|
envRule.env.gitCommitId = "testCommitId"
|
||||||
|
|
||||||
|
stepRule.step.tmsUpload(
|
||||||
|
script: nullScript,
|
||||||
|
juStabUtils: utils,
|
||||||
|
jenkinsUtilsStub: jenkinsUtilsStub,
|
||||||
|
transportManagementService: tmsStub,
|
||||||
|
mtaPath: 'dummy.mtar',
|
||||||
|
nodeName: 'myNode',
|
||||||
|
credentialsId: 'TMS_ServiceKey',
|
||||||
|
verbose: true
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] CredentialsId: 'TMS_ServiceKey'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Node name: 'myNode'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] MTA path: 'dummy.mtar'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Named user: 'Test User'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] UAA URL: '${uaaUrl}'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] TMS URL: '${uri}'"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] ClientId: '${oauthClientId}'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noUserAvailableInCurrentBuild__usesDefaultUser() {
|
||||||
|
jenkinsUtilsStub = new JenkinsUtilsMock(null)
|
||||||
|
binding.workspace = "."
|
||||||
|
envRule.env.gitCommitId = "testCommitId"
|
||||||
|
|
||||||
|
stepRule.step.tmsUpload(
|
||||||
|
script: nullScript,
|
||||||
|
juStabUtils: utils,
|
||||||
|
jenkinsUtilsStub: jenkinsUtilsStub,
|
||||||
|
transportManagementService: tmsStub,
|
||||||
|
mtaPath: 'dummy.mtar',
|
||||||
|
nodeName: 'myNode',
|
||||||
|
credentialsId: 'TMS_ServiceKey'
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(calledTmsMethodsWithArgs[1], is("uploadFile('${uri}', 'myToken', './dummy.mtar', 'Piper-Pipeline')"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addCustomDescription__descriptionChanged() {
|
||||||
|
jenkinsUtilsStub = new JenkinsUtilsMock("Test User")
|
||||||
|
binding.workspace = "."
|
||||||
|
envRule.env.gitCommitId = "testCommitId"
|
||||||
|
|
||||||
|
stepRule.step.tmsUpload(
|
||||||
|
script: nullScript,
|
||||||
|
juStabUtils: utils,
|
||||||
|
jenkinsUtilsStub: jenkinsUtilsStub,
|
||||||
|
transportManagementService: tmsStub,
|
||||||
|
mtaPath: 'dummy.mtar',
|
||||||
|
nodeName: 'myNode',
|
||||||
|
credentialsId: 'TMS_ServiceKey',
|
||||||
|
customDescription: 'My custom description for testing.'
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(calledTmsMethodsWithArgs[2], is("uploadFileToNode('${uri}', 'myToken', 'myNode', '1234', 'My custom description for testing.')"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Corresponding Transport Request: 'My custom description for testing.' (Id: '2000')"))
|
||||||
|
}
|
||||||
|
|
||||||
|
def mockTransportManagementService() {
|
||||||
|
return new TransportManagementService(nullScript, [:]) {
|
||||||
|
def authentication(String uaaUrl, String oauthClientId, String oauthClientSecret) {
|
||||||
|
calledTmsMethodsWithArgs << "authentication('${uaaUrl}', '${oauthClientId}', '${oauthClientSecret}')"
|
||||||
|
return "myToken"
|
||||||
|
}
|
||||||
|
|
||||||
|
def uploadFile(String url, String token, String file, String namedUser) {
|
||||||
|
calledTmsMethodsWithArgs << "uploadFile('${url}', '${token}', '${file}', '${namedUser}')"
|
||||||
|
return [fileId: 1234, fileName: file]
|
||||||
|
}
|
||||||
|
|
||||||
|
def uploadFileToNode(String url, String token, String nodeName, int fileId, String description, String namedUser) {
|
||||||
|
calledTmsMethodsWithArgs << "uploadFileToNode('${url}', '${token}', '${nodeName}', '${fileId}', '${description}')"
|
||||||
|
return [transportRequestDescription: description, transportRequestId: 2000, queueEntries: [nodeName: 'myNode', nodeId: 1000]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.ExpectedException
|
|
||||||
import org.junit.rules.RuleChain
|
import org.junit.rules.RuleChain
|
||||||
import util.BasePiperTest
|
import util.BasePiperTest
|
||||||
import util.JenkinsLoggingRule
|
import util.JenkinsLoggingRule
|
||||||
@ -33,6 +32,8 @@ class JenkinsUtilsTest extends BasePiperTest {
|
|||||||
|
|
||||||
Map triggerCause
|
Map triggerCause
|
||||||
|
|
||||||
|
String userId
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
void init() throws Exception {
|
void init() throws Exception {
|
||||||
@ -60,7 +61,15 @@ class JenkinsUtilsTest extends BasePiperTest {
|
|||||||
return parentMock
|
return parentMock
|
||||||
}
|
}
|
||||||
def getCause(type) {
|
def getCause(type) {
|
||||||
return triggerCause
|
if (type == hudson.model.Cause.UserIdCause.class){
|
||||||
|
def userIdCause = new hudson.model.Cause.UserIdCause()
|
||||||
|
userIdCause.metaClass.getUserId = {
|
||||||
|
return userId
|
||||||
|
}
|
||||||
|
return userIdCause
|
||||||
|
} else {
|
||||||
|
return triggerCause
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -109,4 +118,16 @@ class JenkinsUtilsTest extends BasePiperTest {
|
|||||||
]
|
]
|
||||||
assertThat(jenkinsUtils.getIssueCommentTriggerAction(), isEmptyOrNullString())
|
assertThat(jenkinsUtils.getIssueCommentTriggerAction(), isEmptyOrNullString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserId() {
|
||||||
|
userId = 'Test User'
|
||||||
|
assertThat(jenkinsUtils.getJobStartedByUserId(), is('Test User'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserIdNoUser() {
|
||||||
|
userId = null
|
||||||
|
assertThat(jenkinsUtils.getJobStartedByUserId(), isEmptyOrNullString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,181 @@
|
|||||||
|
package com.sap.piper.integration
|
||||||
|
|
||||||
|
import hudson.AbortException
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.Ignore
|
||||||
|
import org.junit.rules.ExpectedException
|
||||||
|
import org.junit.rules.RuleChain
|
||||||
|
import util.*
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*
|
||||||
|
import static org.junit.Assert.assertThat
|
||||||
|
|
||||||
|
class TransportManagementServiceTest extends BasePiperTest {
|
||||||
|
private ExpectedException thrown = ExpectedException.none()
|
||||||
|
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
||||||
|
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public RuleChain rules = Rules
|
||||||
|
.getCommonRules(this)
|
||||||
|
.around(new JenkinsErrorRule(this))
|
||||||
|
.around(new JenkinsReadJsonRule(this))
|
||||||
|
.around(shellRule)
|
||||||
|
.around(loggingRule)
|
||||||
|
.around(new JenkinsReadFileRule(this, 'test/resources/TransportManagementService'))
|
||||||
|
.around(thrown)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void retrieveOAuthToken__successfully() {
|
||||||
|
Map requestParams
|
||||||
|
helper.registerAllowedMethod('httpRequest', [Map.class], { m ->
|
||||||
|
requestParams = m
|
||||||
|
return [content: '{ "access_token": "myOAuthToken" }']
|
||||||
|
})
|
||||||
|
|
||||||
|
def uaaUrl = 'http://dummy.com/oauth'
|
||||||
|
def clientId = 'myId'
|
||||||
|
def clientSecret = 'mySecret'
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [:])
|
||||||
|
def token = tms.authentication(uaaUrl, clientId, clientSecret)
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] OAuth Token retrieval started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] OAuth Token retrieved successfully."))
|
||||||
|
assertThat(token, is('myOAuthToken'))
|
||||||
|
assertThat(requestParams, hasEntry('url', "${uaaUrl}/oauth/token/?grant_type=client_credentials&response_type=token"))
|
||||||
|
assertThat(requestParams, hasEntry('requestBody', "grant_type=password&username=${clientId}&password=${clientSecret}".toString()))
|
||||||
|
assertThat(requestParams.customHeaders[1].value, is("Basic ${"${clientId}:${clientSecret}".bytes.encodeBase64()}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void retrieveOAuthToken__inVerboseMode__yieldsMoreEchos() {
|
||||||
|
Map requestParams
|
||||||
|
helper.registerAllowedMethod('httpRequest', [Map.class], { m ->
|
||||||
|
requestParams = m
|
||||||
|
return [content: '{ "access_token": "myOAuthToken" }', status: 200]
|
||||||
|
})
|
||||||
|
|
||||||
|
def uaaUrl = 'http://dummy.com/oauth'
|
||||||
|
def clientId = 'myId'
|
||||||
|
def clientSecret = 'mySecret'
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [verbose: true])
|
||||||
|
tms.authentication(uaaUrl, clientId, clientSecret)
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] OAuth Token retrieval started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] UAA-URL: '${uaaUrl}', ClientId: '${clientId}'"))
|
||||||
|
assertThat(loggingRule.log, containsString("Received response '{ \"access_token\": \"myOAuthToken\" }' with status 200."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] OAuth Token retrieved successfully."))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadFile__successfully() {
|
||||||
|
|
||||||
|
def url = 'http://dummy.com/oauth'
|
||||||
|
def token = 'myToken'
|
||||||
|
def file = 'myFile.mtar'
|
||||||
|
def namedUser = 'myUser'
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [:])
|
||||||
|
def responseDetails = tms.uploadFile(url, token, file, namedUser)
|
||||||
|
|
||||||
|
def oAuthShellCall = shellRule.shell[0]
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] File upload started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] File upload successful."))
|
||||||
|
assertThat(oAuthShellCall, startsWith("#!/bin/sh -e "))
|
||||||
|
assertThat(oAuthShellCall, endsWith("curl -H 'Authorization: Bearer ${token}' -F 'file=@${file}' -F 'namedUser=${namedUser}' -o responseFileUpload.txt --fail '${url}/v2/files/upload'"))
|
||||||
|
assertThat(responseDetails, hasEntry("fileId", 1234))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
void uploadFile__withHttpErrorResponse__throwsError() {
|
||||||
|
|
||||||
|
def url = 'http://dummy.com/oauth'
|
||||||
|
def token = 'myWrongToken'
|
||||||
|
def file = 'myFile.mtar'
|
||||||
|
def namedUser = 'myUser'
|
||||||
|
|
||||||
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, ".* curl .*", {throw new AbortException()})
|
||||||
|
|
||||||
|
thrown.expect(AbortException.class)
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [:])
|
||||||
|
tms.uploadFile(url, token, file, namedUser)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadFile__inVerboseMode__yieldsMoreEchos() {
|
||||||
|
|
||||||
|
def url = 'http://dummy.com/oauth'
|
||||||
|
def token = 'myToken'
|
||||||
|
def file = 'myFile.mtar'
|
||||||
|
def namedUser = 'myUser'
|
||||||
|
|
||||||
|
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, ".* curl .*", '200')
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [verbose: true])
|
||||||
|
tms.uploadFile(url, token, file, namedUser)
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] File upload started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] URL: '${url}', File: '${file}'"))
|
||||||
|
assertThat(loggingRule.log, containsString("\"fileId\": 1234"))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] File upload successful."))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadFileToNode__successfully() {
|
||||||
|
Map requestParams
|
||||||
|
helper.registerAllowedMethod('httpRequest', [Map.class], { m ->
|
||||||
|
requestParams = m
|
||||||
|
return [content: '{ "upload": "success" }']
|
||||||
|
})
|
||||||
|
|
||||||
|
def url = 'http://dummy.com/oauth'
|
||||||
|
def token = 'myToken'
|
||||||
|
def nodeName = 'myNode'
|
||||||
|
def fileId = 1234
|
||||||
|
def description = "My description."
|
||||||
|
def namedUser = 'myUser'
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [:])
|
||||||
|
def responseDetails = tms.uploadFileToNode(url, token, nodeName, fileId, description, namedUser)
|
||||||
|
|
||||||
|
def bodyRegEx = /^\{\s+"nodeName":\s+"myNode",\s+"contentType":\s+"MTA",\s+"description":\s+"My\s+description.",\s+"storageType":\s+"FILE",\s+"namedUser":\s+"myUser",\s+"entries":\s+\[\s+\{\s+"uri":\s+1234\s+}\s+]\s+}$/
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Node upload started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Node upload successful."))
|
||||||
|
assertThat(requestParams, hasEntry('url', "${url}/v2/nodes/upload"))
|
||||||
|
assert requestParams.requestBody ==~ bodyRegEx
|
||||||
|
assertThat(requestParams.customHeaders[0].value, is("Bearer ${token}"))
|
||||||
|
assertThat(responseDetails, hasEntry("upload", "success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadFileToNode__inVerboseMode__yieldsMoreEchos() {
|
||||||
|
Map requestParams
|
||||||
|
helper.registerAllowedMethod('httpRequest', [Map.class], { m ->
|
||||||
|
requestParams = m
|
||||||
|
return [content: '{ "upload": "success" }']
|
||||||
|
})
|
||||||
|
|
||||||
|
def url = 'http://dummy.com/oauth'
|
||||||
|
def token = 'myToken'
|
||||||
|
def nodeName = 'myNode'
|
||||||
|
def fileId = 1234
|
||||||
|
def description = "My description."
|
||||||
|
def namedUser = 'myUser'
|
||||||
|
|
||||||
|
def tms = new TransportManagementService(nullScript, [verbose: true])
|
||||||
|
tms.uploadFileToNode(url, token, nodeName, fileId, description, namedUser)
|
||||||
|
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Node upload started."))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] URL: '${url}', NodeName: '${nodeName}', FileId: '${fileId}'"))
|
||||||
|
assertThat(loggingRule.log, containsString("\"upload\": \"success\""))
|
||||||
|
assertThat(loggingRule.log, containsString("[TransportManagementService] Node upload successful."))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"access_token": "myOAuthToken"
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"fileId": 1234
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"upload": "success"
|
||||||
|
}
|
@ -15,6 +15,8 @@ import static com.sap.piper.Prerequisites.checkScript
|
|||||||
'healthExecuteCheck',
|
'healthExecuteCheck',
|
||||||
/** For Neo use-cases: Performs deployment to Neo landscape. */
|
/** For Neo use-cases: Performs deployment to Neo landscape. */
|
||||||
'neoDeploy',
|
'neoDeploy',
|
||||||
|
/** For TMS use-cases: Performs upload to Transport Management Service node*/
|
||||||
|
'tmsUpload',
|
||||||
/** Publishes release information to GitHub. */
|
/** Publishes release information to GitHub. */
|
||||||
'githubPublishRelease',
|
'githubPublishRelease',
|
||||||
]
|
]
|
||||||
@ -60,6 +62,12 @@ void call(Map parameters = [:]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.tmsUpload) {
|
||||||
|
durationMeasure(script: script, measurementName: 'upload_release_tms_duration') {
|
||||||
|
tmsUpload script: script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config.healthExecuteCheck) {
|
if (config.healthExecuteCheck) {
|
||||||
healthExecuteCheck script: script
|
healthExecuteCheck script: script
|
||||||
}
|
}
|
||||||
|
131
vars/tmsUpload.groovy
Normal file
131
vars/tmsUpload.groovy
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import com.sap.piper.ConfigurationHelper
|
||||||
|
import com.sap.piper.GenerateDocumentation
|
||||||
|
import com.sap.piper.JenkinsUtils
|
||||||
|
import com.sap.piper.JsonUtils
|
||||||
|
import com.sap.piper.Utils
|
||||||
|
import com.sap.piper.integration.TransportManagementService
|
||||||
|
import groovy.transform.Field
|
||||||
|
|
||||||
|
import static com.sap.piper.Prerequisites.checkScript
|
||||||
|
|
||||||
|
@Field String STEP_NAME = getClass().getName()
|
||||||
|
|
||||||
|
@Field Set GENERAL_CONFIG_KEYS = [
|
||||||
|
/**
|
||||||
|
* Print more detailed information into the log.
|
||||||
|
* @possibleValues `true`, `false`
|
||||||
|
*/
|
||||||
|
'verbose'
|
||||||
|
]
|
||||||
|
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
|
||||||
|
/**
|
||||||
|
* If specific stashes should be considered, their names need to be passed via the parameter `stashContent`.
|
||||||
|
*/
|
||||||
|
'stashContent',
|
||||||
|
/**
|
||||||
|
* Defines the path to *.mtar for the upload to the Transport Management Service.
|
||||||
|
*/
|
||||||
|
'mtaPath',
|
||||||
|
/**
|
||||||
|
* Defines the name of the node to which the *.mtar file should be uploaded.
|
||||||
|
*/
|
||||||
|
'nodeName',
|
||||||
|
/**
|
||||||
|
* Credentials to be used for the file and node uploads to the Transport Management Service.
|
||||||
|
*/
|
||||||
|
'credentialsId',
|
||||||
|
/**
|
||||||
|
* Can be used as the description of a transport request. Will overwrite the default. (Default: Corresponding Git Commit-ID)
|
||||||
|
*/
|
||||||
|
'customDescription'
|
||||||
|
])
|
||||||
|
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This step allows you to upload an MTA file (multi-target application archive) into a TMS (SAP Cloud Platform Transport Management Service) landscape for further TMS-controlled distribution through a TMS-configured landscape.
|
||||||
|
* TMS lets you manage transports between SAP Cloud Platform accounts in Neo and Cloud Foundry, such as from DEV to TEST and PROD accounts.
|
||||||
|
* For more information, see [official documentation of Transport Management Service](https://help.sap.com/viewer/p/TRANSPORT_MANAGEMENT_SERVICE)
|
||||||
|
*
|
||||||
|
* !!! note "Prerequisites"
|
||||||
|
* * You have subscribed to and set up TMS, as described in [Setup and Configuration of SAP Cloud Platform Transport Management](https://help.sap.com/viewer/7f7160ec0d8546c6b3eab72fb5ad6fd8/Cloud/en-US/66fd7283c62f48adb23c56fb48c84a60.html), which includes the configuration of a node to be used for uploading an MTA file.
|
||||||
|
* * A corresponding service key has been created, as described in [Set Up the Environment to Transport Content Archives directly in an Application](https://help.sap.com/viewer/7f7160ec0d8546c6b3eab72fb5ad6fd8/Cloud/en-US/8d9490792ed14f1bbf8a6ac08a6bca64.html). This service key (JSON) must be stored as a secret text within the Jenkins secure store.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GenerateDocumentation
|
||||||
|
void call(Map parameters = [:]) {
|
||||||
|
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
|
||||||
|
|
||||||
|
def script = checkScript(this, parameters) ?: this
|
||||||
|
def utils = parameters.juStabUtils ?: new Utils()
|
||||||
|
def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils()
|
||||||
|
|
||||||
|
// load default & individual configuration
|
||||||
|
Map config = ConfigurationHelper.newInstance(this)
|
||||||
|
.loadStepDefaults()
|
||||||
|
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
|
||||||
|
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
|
||||||
|
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)
|
||||||
|
.mixin(parameters, PARAMETER_KEYS)
|
||||||
|
//mandatory parameters
|
||||||
|
.withMandatoryProperty('mtaPath')
|
||||||
|
.withMandatoryProperty('nodeName')
|
||||||
|
.withMandatoryProperty('credentialsId')
|
||||||
|
.use()
|
||||||
|
|
||||||
|
// telemetry reporting
|
||||||
|
new Utils().pushToSWA([
|
||||||
|
step : STEP_NAME,
|
||||||
|
stepParamKey1: 'scriptMissing',
|
||||||
|
stepParam1 : parameters?.script == null
|
||||||
|
], config)
|
||||||
|
|
||||||
|
def jsonUtilsObject = new JsonUtils()
|
||||||
|
|
||||||
|
// 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 = []
|
||||||
|
|
||||||
|
def customDescription = config.customDescription ? "${config.customDescription}" : "Git CommitId: ${script.commonPipelineEnvironment.getGitCommitId()}"
|
||||||
|
def description = customDescription
|
||||||
|
|
||||||
|
def namedUser = jenkinsUtils.getJobStartedByUserId() ?: config.namedUser
|
||||||
|
|
||||||
|
def nodeName = config.nodeName
|
||||||
|
def mtaPath = config.mtaPath
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo "[TransportManagementService] CredentialsId: '${config.credentialsId}'"
|
||||||
|
echo "[TransportManagementService] Node name: '${nodeName}'"
|
||||||
|
echo "[TransportManagementService] MTA path: '${mtaPath}'"
|
||||||
|
echo "[TransportManagementService] Named user: '${namedUser}'"
|
||||||
|
}
|
||||||
|
|
||||||
|
def tms = parameters.transportManagementService ?: new TransportManagementService(script, config)
|
||||||
|
|
||||||
|
withCredentials([string(credentialsId: config.credentialsId, variable: 'tmsServiceKeyJSON')]) {
|
||||||
|
|
||||||
|
def tmsServiceKey = jsonUtilsObject.jsonStringToGroovyObject(tmsServiceKeyJSON)
|
||||||
|
|
||||||
|
def clientId = tmsServiceKey.uaa.clientid
|
||||||
|
def clientSecret = tmsServiceKey.uaa.clientsecret
|
||||||
|
def uaaUrl = tmsServiceKey.uaa.url
|
||||||
|
def uri = tmsServiceKey.uri
|
||||||
|
|
||||||
|
if (config.verbose) {
|
||||||
|
echo "[TransportManagementService] UAA URL: '${uaaUrl}'"
|
||||||
|
echo "[TransportManagementService] TMS URL: '${uri}'"
|
||||||
|
echo "[TransportManagementService] ClientId: '${clientId}'"
|
||||||
|
}
|
||||||
|
|
||||||
|
def token = tms.authentication(uaaUrl, clientId, clientSecret)
|
||||||
|
def fileUploadResponse = tms.uploadFile(uri, token, "${workspace}/${mtaPath}", namedUser)
|
||||||
|
def uploadFileToNodeResponse = tms.uploadFileToNode(uri, token, nodeName, fileUploadResponse.fileId, description, namedUser)
|
||||||
|
|
||||||
|
echo "[TransportManagementService] File '${fileUploadResponse.fileName}' successfully uploaded to Node '${uploadFileToNodeResponse.queueEntries.nodeName}' (Id: '${uploadFileToNodeResponse.queueEntries.nodeId}')."
|
||||||
|
echo "[TransportManagementService] Corresponding Transport Request: '${uploadFileToNodeResponse.transportRequestDescription}' (Id: '${uploadFileToNodeResponse.transportRequestId}')"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user