mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-18 05:18:24 +02:00
feat(newman): fetch xsuaa credentials in newman execution (#1325)
* fetch xsuaa credentials in newman execution * refactor CfUtils and moving to integration package * removing CfUtils from old package com.sap.piper * avoid config.verbose as "null" * variable renamed for consistent naming * handling NPE and other comments incorp * added test cases Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
This commit is contained in:
parent
41c1653a06
commit
702664645c
68
src/com/sap/piper/integration/CloudFoundry.groovy
Normal file
68
src/com/sap/piper/integration/CloudFoundry.groovy
Normal file
@ -0,0 +1,68 @@
|
||||
package com.sap.piper.integration
|
||||
|
||||
class CloudFoundry {
|
||||
final Script script
|
||||
|
||||
CloudFoundry(Script script) {
|
||||
this.script = script
|
||||
}
|
||||
|
||||
def getXsuaaCredentials(String apiEndpoint, String org, String space, String credentialsId, String appName, boolean verbose){
|
||||
def env = getAppEnvironment(apiEndpoint, org, space, credentialsId, appName, verbose)
|
||||
if (!env.system_env_json.VCAP_SERVICES.xsuaa[0]) throw new Exception("Cannot find xsuaa credentials")
|
||||
return env.system_env_json.VCAP_SERVICES.xsuaa[0].credentials
|
||||
}
|
||||
|
||||
def getAppEnvironment(String apiEndpoint, String org, String space, String credentialsId, String appName, boolean verbose){
|
||||
def authEndpoint = getAuthEndPoint(apiEndpoint, verbose)
|
||||
def bearerToken = getBearerToken(authEndpoint, credentialsId, verbose)
|
||||
def appUrl = getAppRefUrl(apiEndpoint, org, space, bearerToken, appName, verbose)
|
||||
def response = script.httpRequest url: "${appUrl}/env", quiet: !verbose,
|
||||
customHeaders:[[name: 'Authorization', value: "${bearerToken}"]]
|
||||
def envJson = script.readJSON text:"${response.content}"
|
||||
return envJson
|
||||
}
|
||||
|
||||
def getAuthEndPoint(String apiEndpoint, boolean verbose){
|
||||
def info = script.httpRequest url: "${apiEndpoint}/v2/info", quiet: !verbose
|
||||
def responseJson = script.readJSON text:"${info.content}"
|
||||
return responseJson.authorization_endpoint
|
||||
}
|
||||
|
||||
def getBearerToken(String authorizationEndpoint, String credentialsId, boolean verbose){
|
||||
script.withCredentials([script.usernamePassword(credentialsId: credentialsId, usernameVariable: 'usercf', passwordVariable: 'passwordcf')]) {
|
||||
def token = script.httpRequest url:"${authorizationEndpoint}/oauth/token", quiet: !verbose,
|
||||
httpMode:'POST',
|
||||
requestBody: "username=${script.usercf}&password=${script.passwordcf}&client_id=cf&grant_type=password&response_type=token",
|
||||
customHeaders: [[name: 'Content-Type', value: 'application/x-www-form-urlencoded'],[name: 'Authorization', value: 'Basic Y2Y6']]
|
||||
def responseJson = script.readJSON text:"${token.content}"
|
||||
return "Bearer ${responseJson.access_token.trim()}"
|
||||
}
|
||||
}
|
||||
|
||||
def getAppRefUrl(String apiEndpoint, String org, String space, String bearerToken, String appName, boolean verbose){
|
||||
def orgGuid = getOrgGuid(apiEndpoint, org, bearerToken, verbose)
|
||||
def spaceGuid = getSpaceGuid(apiEndpoint, orgGuid, space, bearerToken, verbose)
|
||||
def appInfo = script.httpRequest url:"${apiEndpoint}/v3/apps?names=${appName},${appName}_blue,${appName}_green,space_guids=${spaceGuid}", quiet: !verbose,
|
||||
customHeaders:[[name: 'Authorization', value: "${bearerToken}"]]
|
||||
def responseJson = script.readJSON text:"${appInfo.content}"
|
||||
if (!responseJson.resources[0]) throw new Exception("Cannot find Application")
|
||||
return responseJson.resources[0].links.self.href.trim()
|
||||
}
|
||||
|
||||
def getOrgGuid(String apiEndpoint, String org, String bearerToken, boolean verbose){
|
||||
def organizationInfo = script.httpRequest url: "${apiEndpoint}/v3/organizations?names=${org}", quiet: !verbose,
|
||||
customHeaders:[[name: 'Authorization', value: "${bearerToken}"]]
|
||||
def responseJson = script.readJSON text:"${organizationInfo.content}"
|
||||
if (!responseJson.resources[0]) throw new Exception("Cannot find Organization")
|
||||
return responseJson.resources[0].guid
|
||||
}
|
||||
|
||||
def getSpaceGuid(String apiEndpoint, String orgGuid, String space, String bearerToken, boolean verbose){
|
||||
def spaceInfo = script.httpRequest url: "${apiEndpoint}/v3/spaces?names=${space},organization_guids=${orgGuid}", quiet: !verbose,
|
||||
customHeaders:[[name: 'Authorization', value: "${bearerToken}"]]
|
||||
def responseJson = script.readJSON text:"${spaceInfo.content}"
|
||||
if (!responseJson.resources[0]) throw new Exception("Cannot find Space")
|
||||
return responseJson.resources[0].guid
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import static org.hamcrest.Matchers.is
|
||||
import static org.hamcrest.Matchers.containsString
|
||||
import static org.hamcrest.Matchers.endsWith
|
||||
import static org.hamcrest.Matchers.startsWith
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
import static org.junit.Assert.assertThat
|
||||
|
||||
@ -19,6 +20,7 @@ import util.JenkinsShellCallRule
|
||||
import util.JenkinsDockerExecuteRule
|
||||
import util.Rules
|
||||
import org.junit.rules.ExpectedException
|
||||
import util.JenkinsCredentialsRule
|
||||
|
||||
class NewmanExecuteTest extends BasePiperTest {
|
||||
private ExpectedException thrown = ExpectedException.none()
|
||||
@ -26,6 +28,7 @@ class NewmanExecuteTest extends BasePiperTest {
|
||||
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
|
||||
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
||||
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
|
||||
private JenkinsCredentialsRule jenkinsCredentialsRule = new JenkinsCredentialsRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules
|
||||
@ -34,6 +37,7 @@ class NewmanExecuteTest extends BasePiperTest {
|
||||
.around(thrown)
|
||||
.around(dockerExecuteRule)
|
||||
.around(shellRule)
|
||||
.around(jenkinsCredentialsRule)
|
||||
.around(loggingRule)
|
||||
.around(stepRule) // needs to be activated after dockerExecuteRule, otherwise executeDocker is not mocked
|
||||
|
||||
@ -162,4 +166,32 @@ class NewmanExecuteTest extends BasePiperTest {
|
||||
assertThat(shellRule.shell, hasItem(endsWith('newman run testCollectionsFolder'+File.separatorChar+'B.postman_collection.json --iteration-data testDataFile --reporters junit,html --reporter-junit-export target/newman/TEST-testCollectionsFolder_B.xml --reporter-html-export target/newman/TEST-testCollectionsFolder_B.html')))
|
||||
assertJobStatusSuccess()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExecuteNewmanCfAppsWithSecrets() throws Exception {
|
||||
def jsonResponse = '{ "system_env_json":{"VCAP_SERVICES":{"xsuaa":[{"credentials":{"clientid":"myclientid", "clientsecret":"myclientsecret"}}]}}, "authorization_endpoint": "myAuthEndPoint", "access_token": "myAccessToken", "resources":[{"guid":"myGuid", "links":{"self":{"href":"myAppUrl"}}}] }'
|
||||
jenkinsCredentialsRule.withCredentials('credentialsId', 'myuser', 'topsecret')
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: jsonResponse, status: 200]
|
||||
})
|
||||
helper.registerAllowedMethod('readJSON', [Map.class] , {
|
||||
return new JsonSlurper().parseText(jsonResponse)
|
||||
})
|
||||
|
||||
stepRule.step.newmanExecute(
|
||||
script: nullScript,
|
||||
juStabUtils: utils,
|
||||
newmanCollection: 'testCollection',
|
||||
newmanEnvironment: 'testEnvironment',
|
||||
newmanGlobals: 'testGlobals',
|
||||
dockerImage: 'testImage',
|
||||
testRepository: 'testRepo',
|
||||
failOnError: false,
|
||||
cloudFoundry: ["apiEndpoint": "http://fake.endpoint.com", "org":"myOrg", "space": "mySpace", "credentialsId": "credentialsId"],
|
||||
cfAppsWithSecrets: ['app1', 'app2']
|
||||
)
|
||||
// asserts
|
||||
assertThat(shellRule.shell, hasItem(endsWith('--env-var app1_clientid=myclientid --env-var app1_clientsecret=myclientsecret --env-var app2_clientid=myclientid --env-var app2_clientsecret=myclientsecret')))
|
||||
assertJobStatusSuccess()
|
||||
}
|
||||
}
|
||||
|
158
test/groovy/com/sap/piper/integration/CloudFoundryTest.groovy
Normal file
158
test/groovy/com/sap/piper/integration/CloudFoundryTest.groovy
Normal file
@ -0,0 +1,158 @@
|
||||
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 groovy.json.JsonSlurper
|
||||
import static org.hamcrest.Matchers.*
|
||||
import static org.junit.Assert.assertThat
|
||||
import util.JenkinsCredentialsRule
|
||||
|
||||
class CloudFoundryTest extends BasePiperTest {
|
||||
public ExpectedException exception = ExpectedException.none()
|
||||
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
||||
private JenkinsCredentialsRule jenkinsCredentialsRule = new JenkinsCredentialsRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules
|
||||
.getCommonRules(this)
|
||||
.around(exception)
|
||||
.around(new JenkinsErrorRule(this))
|
||||
.around(new JenkinsReadJsonRule(this))
|
||||
.around(shellRule)
|
||||
.around(jenkinsCredentialsRule)
|
||||
|
||||
@Test
|
||||
void getAuthEndPoint_test() {
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "authorization_endpoint": "myAuthEndPoint" }', status: 200]
|
||||
})
|
||||
|
||||
String apiEndPoint = 'http://dummy.sap.com'
|
||||
boolean verbose = true
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def endPoint = cf.getAuthEndPoint(apiEndPoint, verbose)
|
||||
|
||||
assertThat(endPoint, is('myAuthEndPoint'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBearerToken_test(){
|
||||
String authorizationEndpoint = 'http://dummy.sap.com'
|
||||
String credentialsId = 'credentialsId'
|
||||
boolean verbose = true
|
||||
|
||||
jenkinsCredentialsRule.withCredentials(credentialsId, 'myuser', 'topsecret')
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "access_token": "myAccessToken" }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def token = cf.getBearerToken(authorizationEndpoint, credentialsId, verbose)
|
||||
|
||||
assertThat(token.toString(), is('Bearer myAccessToken'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAppRefUrl_test(){
|
||||
String apiEndpoint = 'http://dummy.sap.com'
|
||||
String org = 'myOrg'
|
||||
String space = 'mySpace'
|
||||
String bearerToken = 'myAccessToken'
|
||||
String appName = 'myAppName'
|
||||
boolean verbose = true
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "resources":[{"guid":"myGuid", "links":{"self":{"href":"myAppUrl"}}}] }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def appUrl = cf.getAppRefUrl(apiEndpoint, org, space, bearerToken, appName, verbose)
|
||||
|
||||
assertThat(appUrl.toString(), is('myAppUrl'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOrgGuid_test(){
|
||||
String apiEndpoint = 'http://dummy.sap.com'
|
||||
String org = 'myOrg'
|
||||
String bearerToken = 'myToken'
|
||||
boolean verbose = true
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "resources":[{"guid":"myOrgGuid"}] }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def orgGuid = cf.getOrgGuid(apiEndpoint, org, bearerToken, verbose)
|
||||
|
||||
assertThat(orgGuid.toString(), is('myOrgGuid'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSpaceGuid_test(){
|
||||
String apiEndpoint ='http://dummy.sap.com'
|
||||
String orgGuid = 'myOrgGuid'
|
||||
String space = 'mySpace'
|
||||
String bearerToken = 'myToken'
|
||||
boolean verbose = true
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "resources":[{"guid":"mySpaceGuid"}] }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def spaceGuid = cf.getSpaceGuid(apiEndpoint, orgGuid, space, bearerToken, verbose)
|
||||
|
||||
assertThat(spaceGuid.toString(), is('mySpaceGuid'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAppEnvironment_test(){
|
||||
String apiEndpoint = 'http://dummy.sap.com'
|
||||
String org = 'myOrg'
|
||||
String space = 'mySpace'
|
||||
String credentialsId = 'credentialsId'
|
||||
String appName = 'myAppName'
|
||||
boolean verbose = true
|
||||
|
||||
jenkinsCredentialsRule.withCredentials(credentialsId, 'myuser', 'topsecret')
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "authorization_endpoint": "myAuthEndPoint", "access_token": "myAccessToken", "resources":[{"guid":"myGuid", "links":{"self":{"href":"myAppUrl"}}}] }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def appEnv = cf.getAppEnvironment(apiEndpoint, org, space, credentialsId, appName, verbose)
|
||||
|
||||
assertThat(appEnv.toString(), is('{authorization_endpoint=myAuthEndPoint, access_token=myAccessToken, resources=[{guid=myGuid, links={self={href=myAppUrl}}}]}'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void getXsuaaCredentials_test(){
|
||||
String apiEndpoint = 'http://dummy.sap.com'
|
||||
String org = 'myOrg'
|
||||
String space = 'mySpace'
|
||||
String credentialsId = 'credentialsId'
|
||||
String appName = 'myAppName'
|
||||
boolean verbose = true
|
||||
|
||||
jenkinsCredentialsRule.withCredentials(credentialsId, 'myuser', 'topsecret')
|
||||
|
||||
helper.registerAllowedMethod('httpRequest', [Map.class] , {
|
||||
return [content: '{ "system_env_json":{"VCAP_SERVICES":{"xsuaa":[{"credentials":"myCredentials"}]}}, "authorization_endpoint": "myAuthEndPoint", "access_token": "myAccessToken", "resources":[{"guid":"myGuid", "links":{"self":{"href":"myAppUrl"}}}] }', status: 200]
|
||||
})
|
||||
|
||||
def cf = new CloudFoundry(nullScript)
|
||||
def xsuaaCred = cf.getXsuaaCredentials(apiEndpoint, org, space, credentialsId, appName, verbose)
|
||||
|
||||
assertThat(xsuaaCred.toString(), is('myCredentials'))
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ import com.sap.piper.ConfigurationHelper
|
||||
import com.sap.piper.GenerateDocumentation
|
||||
import com.sap.piper.GitUtils
|
||||
import com.sap.piper.Utils
|
||||
import com.sap.piper.integration.CloudFoundry
|
||||
|
||||
import groovy.text.GStringTemplateEngine
|
||||
import groovy.transform.Field
|
||||
|
||||
@ -64,9 +66,43 @@ import groovy.transform.Field
|
||||
* Define an additional repository where the test implementation is located.
|
||||
* For protected repositories the `testRepository` needs to contain the ssh git url.
|
||||
*/
|
||||
'testRepository'
|
||||
'testRepository',
|
||||
/**
|
||||
* Define name array of cloud foundry apps deployed for which secrets (clientid and clientsecret) will be appended
|
||||
* to the newman command that overrides the environment json entries
|
||||
* (--env-var <appName_clientid>=${clientid} & --env-var <appName_clientsecret>=${clientsecret})
|
||||
*/
|
||||
'cfAppsWithSecrets',
|
||||
'cloudFoundry',
|
||||
/**
|
||||
* Cloud Foundry API endpoint.
|
||||
* @parentConfigKey cloudFoundry
|
||||
*/
|
||||
'apiEndpoint',
|
||||
/**
|
||||
* Credentials to be used for deployment.
|
||||
* @parentConfigKey cloudFoundry
|
||||
*/
|
||||
'credentialsId',
|
||||
/**
|
||||
* Cloud Foundry target organization.
|
||||
* @parentConfigKey cloudFoundry
|
||||
*/
|
||||
'org',
|
||||
/**
|
||||
* Cloud Foundry target space.
|
||||
* @parentConfigKey cloudFoundry
|
||||
*/
|
||||
'space',
|
||||
/**
|
||||
* Print more detailed information into the log.
|
||||
* @possibleValues `true`, `false`
|
||||
*/
|
||||
'verbose'
|
||||
]
|
||||
|
||||
@Field Map CONFIG_KEY_COMPATIBILITY = [cloudFoundry: [apiEndpoint: 'cfApiEndpoint', credentialsId: 'cfCredentialsId', org: 'cfOrg', space: 'cfSpace']]
|
||||
|
||||
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
|
||||
|
||||
/**
|
||||
@ -86,7 +122,7 @@ void call(Map parameters = [:]) {
|
||||
.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)
|
||||
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
|
||||
.use()
|
||||
|
||||
new Utils().pushToSWA([
|
||||
@ -128,10 +164,35 @@ void call(Map parameters = [:]) {
|
||||
config: config.plus([newmanCollection: collection]),
|
||||
collectionDisplayName: collectionDisplayName
|
||||
]).toString()
|
||||
def command_secrets = ''
|
||||
if(config.cfAppsWithSecrets){
|
||||
CloudFoundry cfUtils = new CloudFoundry(script);
|
||||
config.cfAppsWithSecrets.each { appName ->
|
||||
def xsuaaCredentials = cfUtils.getXsuaaCredentials(config.cloudFoundry.apiEndpoint,
|
||||
config.cloudFoundry.org,
|
||||
config.cloudFoundry.space,
|
||||
config.cloudFoundry.credentialsId,
|
||||
appName,
|
||||
config.verbose ? true : false ) //to avoid config.verbose as "null" if undefined in yaml and since function parameter boolean
|
||||
command_secrets += " --env-var ${appName}_clientid=${xsuaaCredentials.clientid} --env-var ${appName}_clientsecret=${xsuaaCredentials.clientsecret}"
|
||||
echo "Exposing client id and secret for ${appName}: as ${appName}_clientid and ${appName}_clientsecret to newman"
|
||||
}
|
||||
}
|
||||
|
||||
if(!config.failOnError) command += ' --suppress-exit-code'
|
||||
try {
|
||||
sh "PATH=\$PATH:~/.npm-global/bin newman ${command}"
|
||||
} catch (e) {
|
||||
if (config.cfAppsWithSecrets && command_secrets){
|
||||
echo "PATH=\$PATH:~/.npm-global/bin newman ${command} **env/secrets**"
|
||||
sh """
|
||||
set +x
|
||||
PATH=\$PATH:~/.npm-global/bin newman ${command} ${command_secrets}
|
||||
"""
|
||||
}
|
||||
else{
|
||||
sh "PATH=\$PATH:~/.npm-global/bin newman ${command}"
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
error "[${STEP_NAME}] ERROR: The execution of the newman tests failed, see the log for details."
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user