You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-11-06 09:09:19 +02:00
Add step spinnakerTriggerPipeline (#793)
This commit is contained in:
@@ -512,6 +512,10 @@ steps:
|
||||
options: []
|
||||
pullRequestProvider: 'GitHub'
|
||||
sonarScannerDownloadUrl: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip'
|
||||
spinnakerTriggerPipeline:
|
||||
certFileCredentialsId: 'spinnaker-client-certificate'
|
||||
keyFileCredentialsId: 'spinnaker-client-key'
|
||||
timeout: 60
|
||||
testsPublishResults:
|
||||
failOnError: false
|
||||
junit:
|
||||
|
||||
202
test/groovy/SpinnakerTriggerPipelineTest.groovy
Normal file
202
test/groovy/SpinnakerTriggerPipelineTest.groovy
Normal file
@@ -0,0 +1,202 @@
|
||||
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.*
|
||||
import static org.junit.Assert.assertThat
|
||||
|
||||
class SpinnakerTriggerPipelineTest extends BasePiperTest {
|
||||
private ExpectedException exception = new ExpectedException().none()
|
||||
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
|
||||
private JenkinsLoggingRule logginRule = new JenkinsLoggingRule(this)
|
||||
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
|
||||
private JenkinsReadJsonRule readJsonRule = new JenkinsReadJsonRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules
|
||||
.getCommonRules(this)
|
||||
.around(new JenkinsReadYamlRule(this))
|
||||
.around(exception)
|
||||
.around(shellRule)
|
||||
.around(logginRule)
|
||||
.around(readJsonRule)
|
||||
.around(stepRule)
|
||||
|
||||
class EnvMock {
|
||||
def STAGE_NAME = 'testStage'
|
||||
Map getEnvironment() {
|
||||
return [key1: 'value1', key2: 'value2']
|
||||
}
|
||||
}
|
||||
|
||||
def credentialFileList = []
|
||||
def timeout = 0
|
||||
|
||||
@Before
|
||||
void init() {
|
||||
binding.setVariable('env', new EnvMock())
|
||||
|
||||
credentialFileList = []
|
||||
helper.registerAllowedMethod('file', [Map], { m ->
|
||||
credentialFileList.add(m)
|
||||
return m
|
||||
})
|
||||
|
||||
Map credentialFileNames = [
|
||||
'spinnaker-client-certificate': 'clientCert.file',
|
||||
'spinnaker-client-key': 'clientKey.file'
|
||||
|
||||
]
|
||||
|
||||
helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c ->
|
||||
l.each { fileCredentials ->
|
||||
binding.setProperty(fileCredentials.variable, credentialFileNames[fileCredentials.credentialsId])
|
||||
}
|
||||
try {
|
||||
c()
|
||||
} finally {
|
||||
l.each { fileCredentials ->
|
||||
binding.setProperty(fileCredentials.variable, null)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
helper.registerAllowedMethod('timeout', [Integer.class, Closure.class] , {i, body ->
|
||||
timeout = i
|
||||
return body()
|
||||
})
|
||||
|
||||
//not sure where this comes from!
|
||||
helper.registerAllowedMethod('waitUntil', [Closure.class], {body ->
|
||||
List responseStatus = ['RUNNING', 'PAUSED', 'NOT_STARTED']
|
||||
while (!body()) {
|
||||
//take another round with a different response status
|
||||
responseStatus.each {status ->
|
||||
shellRule.setReturnValue('curl -X GET https://spinnakerTest.url/testRef --silent --cert $clientCertificate --key $clientKey', "{\"status\": \"${status}\"}")
|
||||
shellRule.setReturnValue('curl -X GET https://spinnakerTest.url/testRef --verbose --cert $clientCertificate --key $clientKey', "{\"status\": \"${status}\"}")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
shellRule.setReturnValue('curl -H \'Content-Type: application/json\' -X POST -d \'{"parameters":{"param1":"val1"}}\' --silent --cert $clientCertificate --key $clientKey https://spinnakerTest.url/pipelines/spinnakerTestApp/spinnakerTestPipeline', '{"ref": "/testRef"}')
|
||||
shellRule.setReturnValue('curl -H \'Content-Type: application/json\' -X POST -d \'{"parameters":{"param1":"val1"}}\' --verbose --cert $clientCertificate --key $clientKey https://spinnakerTest.url/pipelines/spinnakerTestApp/spinnakerTestPipeline', '{"ref": "/testRef"}')
|
||||
shellRule.setReturnValue('curl -X GET https://spinnakerTest.url/testRef --silent --cert $clientCertificate --key $clientKey', '{"status": "SUCCEEDED"}')
|
||||
shellRule.setReturnValue('curl -X GET https://spinnakerTest.url/testRef --verbose --cert $clientCertificate --key $clientKey', '{"status": "SUCCEEDED"}')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaults() {
|
||||
nullScript.commonPipelineEnvironment.configuration = [
|
||||
general: [
|
||||
spinnakerGateUrl: 'https://spinnakerTest.url',
|
||||
spinnakerApplication: 'spinnakerTestApp',
|
||||
verbose: true
|
||||
],
|
||||
stages: [
|
||||
testStage: [
|
||||
spinnakerPipeline: 'spinnakerTestPipeline',
|
||||
pipelineParameters: [param1: 'val1']
|
||||
]
|
||||
]
|
||||
]
|
||||
stepRule.step.spinnakerTriggerPipeline(
|
||||
script: nullScript
|
||||
)
|
||||
|
||||
assertThat(timeout, is(60))
|
||||
|
||||
assertThat(logginRule.log, containsString('Triggering Spinnaker pipeline with parameters:'))
|
||||
assertThat(logginRule.log, containsString('Spinnaker pipeline /testRef triggered, waiting for the pipeline to finish'))
|
||||
assertThat(credentialFileList,
|
||||
hasItem(
|
||||
allOf(
|
||||
hasEntry('credentialsId', 'spinnaker-client-key'),
|
||||
hasEntry('variable', 'clientKey')
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(credentialFileList,
|
||||
hasItem(
|
||||
allOf(
|
||||
hasEntry('credentialsId', 'spinnaker-client-certificate'),
|
||||
hasEntry('variable', 'clientCertificate')
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisabledPipelineCheck() {
|
||||
nullScript.commonPipelineEnvironment.configuration = [
|
||||
general: [
|
||||
spinnaker: [
|
||||
gateUrl: 'https://spinnakerTest.url',
|
||||
application: 'spinnakerTestApp'
|
||||
]
|
||||
],
|
||||
stages: [
|
||||
testStage: [
|
||||
pipelineNameOrId: 'spinnakerTestPipeline',
|
||||
pipelineParameters: [param1: 'val1']
|
||||
]
|
||||
]
|
||||
]
|
||||
stepRule.step.spinnakerTriggerPipeline(
|
||||
script: nullScript,
|
||||
timeout: 0
|
||||
)
|
||||
|
||||
assertThat(logginRule.log, containsString('Exiting without waiting for Spinnaker pipeline result.'))
|
||||
assertThat(timeout, is(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTriggerFailure() {
|
||||
|
||||
nullScript.commonPipelineEnvironment.configuration = [
|
||||
general: [
|
||||
spinnakerGateUrl: 'https://spinnakerTest.url',
|
||||
spinnakerApplication: 'spinnakerTestApp'
|
||||
],
|
||||
stages: [
|
||||
testStage: [
|
||||
spinnakerPipeline: 'spinnakerTestPipeline'
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
shellRule.setReturnValue('curl -H \'Content-Type: application/json\' -X POST --silent --cert $clientCertificate --key $clientKey https://spinnakerTest.url/pipelines/spinnakerTestApp/spinnakerTestPipeline', '{}')
|
||||
|
||||
exception.expectMessage('Failed to trigger Spinnaker pipeline')
|
||||
stepRule.step.spinnakerTriggerPipeline(
|
||||
script: nullScript,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipelineFailure() {
|
||||
|
||||
nullScript.commonPipelineEnvironment.configuration = [
|
||||
general: [
|
||||
spinnakerGateUrl: 'https://spinnakerTest.url',
|
||||
spinnakerApplication: 'spinnakerTestApp'
|
||||
],
|
||||
stages: [
|
||||
testStage: [
|
||||
spinnakerPipeline: 'spinnakerTestPipeline',
|
||||
pipelineParameters: [param1: 'val1']
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
shellRule.setReturnValue('curl -X GET https://spinnakerTest.url/testRef --silent --cert $clientCertificate --key $clientKey', '{"status": "FAILED"}')
|
||||
|
||||
exception.expectMessage('Spinnaker pipeline failed with FAILED')
|
||||
stepRule.step.spinnakerTriggerPipeline(
|
||||
script: nullScript,
|
||||
)
|
||||
}
|
||||
}
|
||||
174
vars/spinnakerTriggerPipeline.groovy
Normal file
174
vars/spinnakerTriggerPipeline.groovy
Normal file
@@ -0,0 +1,174 @@
|
||||
import com.cloudbees.groovy.cps.NonCPS
|
||||
import com.sap.piper.JsonUtils
|
||||
import groovy.text.GStringTemplateEngine
|
||||
|
||||
import static com.sap.piper.Prerequisites.checkScript
|
||||
import groovy.json.JsonOutput
|
||||
import org.apache.commons.lang3.text.StrSubstitutor
|
||||
|
||||
|
||||
import com.sap.piper.GenerateDocumentation
|
||||
import com.sap.piper.ConfigurationHelper
|
||||
import com.sap.piper.Utils
|
||||
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field def STEP_NAME = getClass().getName()
|
||||
|
||||
@Field Set GENERAL_CONFIG_KEYS = [
|
||||
'spinnaker',
|
||||
/**
|
||||
* Whether verbose output should be produced.
|
||||
* @possibleValues `true`, `false`
|
||||
*/
|
||||
'verbose'
|
||||
]
|
||||
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
|
||||
/**
|
||||
* Defines the id of the file credentials in your Jenkins credentials store which contain the client certificate file for Spinnaker authentication.
|
||||
* @parentConfigKey spinnaker
|
||||
*/
|
||||
'certFileCredentialsId',
|
||||
/**
|
||||
* Defines the url of the Spinnaker Gateway Service as API endpoint for communication with Spinnaker.
|
||||
* @parentConfigKey spinnaker
|
||||
*/
|
||||
'gateUrl',
|
||||
/**
|
||||
* Defines the id of the file credentials in your Jenkins credentials store which contain the private key file for Spinnaker authentication.
|
||||
* @parentConfigKey spinnaker
|
||||
*/
|
||||
'keyFileCredentialsId',
|
||||
/**
|
||||
* Defines the name/id of the Spinnaker pipeline.
|
||||
* @parentConfigKey spinnaker
|
||||
*/
|
||||
'pipelineNameOrId',
|
||||
/**
|
||||
* Parameter map containing Spinnaker pipeline parameters.
|
||||
* @parentConfigKey spinnaker
|
||||
*/
|
||||
'pipelineParameters',
|
||||
/**
|
||||
* Defines the timeout in minutes for checking the Spinnaker pipeline result.
|
||||
* By setting to `0` the check can be de-activated.
|
||||
*/
|
||||
'timeout'
|
||||
|
||||
])
|
||||
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
|
||||
|
||||
@Field Map CONFIG_KEY_COMPATIBILITY = [
|
||||
application: 'spinnakerApplication',
|
||||
certFileCredentialsId: 'certCredentialId',
|
||||
gateUrl: 'spinnakerGateUrl',
|
||||
keyFileCredentialsId: 'keyCredentialId',
|
||||
pipelineNameOrId: 'spinnakerPipeline',
|
||||
pipelineParameters: 'pipelineParameters',
|
||||
spinnaker: [
|
||||
application: 'application',
|
||||
certFileCredentialsId: 'certFileCredentialsId',
|
||||
keyFileCredentialsId: 'keyFileCredentialsId',
|
||||
gateUrl: 'gateUrl',
|
||||
pipelineParameters: 'pipelineParameters',
|
||||
pipelineNameOrId: 'pipelineNameOrId'
|
||||
]
|
||||
]
|
||||
|
||||
/**
|
||||
* Triggers a [Spinnaker](https://spinnaker.io) pipeline from a Jenkins pipeline.
|
||||
* Spinnaker is for example used for Continuos Deployment scenarios to various Clouds.
|
||||
*/
|
||||
@GenerateDocumentation
|
||||
void call(Map parameters = [:]) {
|
||||
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
|
||||
|
||||
final script = checkScript(this, parameters) ?: this
|
||||
|
||||
// load default & individual configuration
|
||||
Map config = ConfigurationHelper.newInstance(this)
|
||||
.loadStepDefaults(CONFIG_KEY_COMPATIBILITY)
|
||||
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
|
||||
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
|
||||
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
|
||||
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
|
||||
.withMandatoryProperty('spinnaker/gateUrl')
|
||||
.withMandatoryProperty('spinnaker/application')
|
||||
.withMandatoryProperty('spinnaker/pipelineNameOrId')
|
||||
.use()
|
||||
|
||||
// telemetry reporting
|
||||
new Utils().pushToSWA([
|
||||
step: STEP_NAME
|
||||
], config)
|
||||
|
||||
String paramsString = ""
|
||||
if (config.spinnaker.pipelineParameters) {
|
||||
def pipelineParameters = [parameters: config.spinnaker.pipelineParameters]
|
||||
|
||||
paramsString = "-d '${new GStringTemplateEngine().createTemplate(JsonOutput.toJson(pipelineParameters)).make([config: config, env: env]).toString()}'"
|
||||
|
||||
if (config.verbose) {
|
||||
echo "[${STEP_NAME}] Triggering Spinnaker pipeline with parameters: ${paramsString}"
|
||||
}
|
||||
}
|
||||
|
||||
def pipelineTriggerResponse
|
||||
|
||||
//ToDO: support userId/pwd authentication or token authentication!
|
||||
|
||||
def curlVerbosity = (config.verbose) ? '--verbose ' : '--silent '
|
||||
|
||||
withCredentials([
|
||||
file(credentialsId: config.spinnaker.keyFileCredentialsId, variable: 'clientKey'),
|
||||
file(credentialsId: config.spinnaker.certFileCredentialsId, variable: 'clientCertificate')
|
||||
]) {
|
||||
// Trigger a pipeline execution by calling invokePipelineConfigUsingPOST1 (see https://www.spinnaker.io/reference/api/docs.html)
|
||||
pipelineTriggerResponse = sh(returnStdout: true, script: "curl -H 'Content-Type: application/json' -X POST ${paramsString} ${curlVerbosity} --cert \$clientCertificate --key \$clientKey ${config.spinnaker.gateUrl}/pipelines/${config.spinnaker.application}/${config.spinnaker.pipelineNameOrId}").trim()
|
||||
}
|
||||
if (config.verbose) {
|
||||
echo "[${STEP_NAME}] Spinnaker pipeline trigger response = ${pipelineTriggerResponse}"
|
||||
}
|
||||
|
||||
def pipelineTriggerResponseObj = readJSON text: pipelineTriggerResponse
|
||||
if (!pipelineTriggerResponseObj.ref) {
|
||||
error "[${STEP_NAME}] Failed to trigger Spinnaker pipeline"
|
||||
}
|
||||
|
||||
if (config.timeout == 0) {
|
||||
echo "[${STEP_NAME}] Exiting without waiting for Spinnaker pipeline result."
|
||||
return
|
||||
}
|
||||
|
||||
echo "[${STEP_NAME}] Spinnaker pipeline ${pipelineTriggerResponseObj.ref} triggered, waiting for the pipeline to finish"
|
||||
|
||||
def pipelineStatusResponseObj
|
||||
timeout(config.timeout) {
|
||||
waitUntil {
|
||||
def pipelineStatusResponse
|
||||
sleep 10
|
||||
withCredentials([
|
||||
file(credentialsId: config.spinnaker.keyFileCredentialsId, variable: 'clientKey'),
|
||||
file(credentialsId: config.spinnaker.certFileCredentialsId, variable: 'clientCertificate')
|
||||
]) {
|
||||
pipelineStatusResponse = sh returnStdout: true, script: "curl -X GET ${config.spinnaker.gateUrl}${pipelineTriggerResponseObj.ref} ${curlVerbosity} --cert \$clientCertificate --key \$clientKey"
|
||||
}
|
||||
pipelineStatusResponseObj = readJSON text: pipelineStatusResponse
|
||||
echo "[${STEP_NAME}] Spinnaker pipeline ${pipelineTriggerResponseObj.ref} status: ${pipelineStatusResponseObj.status}"
|
||||
|
||||
if (pipelineStatusResponseObj.status in ['RUNNING', 'PAUSED', 'NOT_STARTED']) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pipelineStatusResponseObj.status != 'SUCCEEDED') {
|
||||
if (config.verbose) {
|
||||
echo "[${STEP_NAME}] Full Spinnaker response = ${new JsonUtils().groovyObjectToPrettyJsonString(pipelineStatusResponse)}"
|
||||
}
|
||||
error "[${STEP_NAME}] Spinnaker pipeline failed with ${pipelineStatusResponseObj.status}"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user