1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00

Provide support for additional customer config layers.

This commit is contained in:
Marcus Holl 2019-12-16 10:40:44 +01:00
parent 264e783833
commit e6b00fa601
4 changed files with 113 additions and 18 deletions

View File

@ -8,16 +8,21 @@ class DefaultValueCache implements Serializable {
private Map defaultValues
private DefaultValueCache(Map defaultValues){
private List customDefaults = []
private DefaultValueCache(Map defaultValues, List customDefaults){
this.defaultValues = defaultValues
if(customDefaults) {
this.customDefaults.addAll(customDefaults)
}
}
static getInstance(){
return instance
}
static createInstance(Map defaultValues){
instance = new DefaultValueCache(defaultValues)
static createInstance(Map defaultValues, List customDefaults = []){
instance = new DefaultValueCache(defaultValues, customDefaults)
}
Map getDefaultValues(){
@ -28,6 +33,12 @@ class DefaultValueCache implements Serializable {
instance = null
}
List getCustomDefaults() {
def result = []
result.addAll(customDefaults)
return result
}
static void prepare(Script steps, Map parameters = [:]) {
if(parameters == null) parameters = [:]
if(!DefaultValueCache.getInstance() || parameters.customDefaults) {
@ -46,7 +57,7 @@ class DefaultValueCache implements Serializable {
MapUtils.pruneNulls(defaultValues),
MapUtils.pruneNulls(configuration))
}
DefaultValueCache.createInstance(defaultValues)
DefaultValueCache.createInstance(defaultValues, customDefaults)
}
}
}

View File

@ -1,5 +1,6 @@
import static org.hamcrest.Matchers.allOf
import static org.hamcrest.Matchers.contains
import static org.hamcrest.Matchers.containsInAnyOrder
import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.equalTo
import static org.hamcrest.Matchers.is
@ -63,8 +64,8 @@ class XsDeployTest extends BasePiperTest {
@Before
public void init() {
helper.registerAllowedMethod('withEnv', [List, Closure], {l, c -> env = l; c()})
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig --contextConfig --stepMetadata.*', '{"dockerImage": "xs", "credentialsId":"myCreds"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig --stepMetadata.*', '{"mode": "BG_DEPLOY", "action": "NONE", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig.*--contextConfig.*', '{"dockerImage": "xs", "credentialsId":"myCreds"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'getConfig.* (?!--contextConfig)', '{"mode": "BG_DEPLOY", "action": "NONE", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
nullScript.commonPipelineEnvironment.xsDeploymentId = null
}
@ -88,7 +89,7 @@ class XsDeployTest extends BasePiperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('No enum constant')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig --stepMetadata.*', '{"mode": "DOES_NOT_EXIST", "action": "NONE", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'getConfig.* (?!--contextConfig)', '{"mode": "DOES_NOT_EXIST", "action": "NONE", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
stepRule.step.xsDeploy(
script: nullScript,
@ -114,7 +115,6 @@ class XsDeployTest extends BasePiperTest {
piperGoUtils: goUtils
)
// nota bene: script and piperGoUtils are not contained in the json below.
assertThat(env*.toString(), contains('PIPER_parametersJSON={"apiUrl":"https://example.org/xs","org":"myOrg","space":"mySpace","credentialsId":"myCreds","deployOpts":"-t 60","mtaPath":"myApp.mta","mode":"DEPLOY","action":"NONE"}'))
}
@ -148,7 +148,10 @@ class XsDeployTest extends BasePiperTest {
assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, is('1234'))
assertThat(writeFileRule.files.keySet(), contains('metadata/xsDeploy.yaml'))
assertThat(writeFileRule.files.keySet(), containsInAnyOrder(
'.pipeline/additionalConfigs/default_pipeline_environment.yml',
'metadata/xsDeploy.yaml',
))
assertThat(dockerRule.dockerParams.dockerImage, equalTo('xs'))
assertThat(dockerRule.dockerParams.dockerPullImage, equalTo(false))
@ -158,7 +161,8 @@ class XsDeployTest extends BasePiperTest {
new CommandLineMatcher()
.hasProlog('./piper version'),
new CommandLineMatcher()
.hasProlog('./piper getConfig --contextConfig --stepMetadata \'metadata/xsDeploy.yaml\''),
.hasProlog('./piper getConfig')
.hasArgument('--contextConfig'),
new CommandLineMatcher()
.hasProlog('./piper getConfig --stepMetadata \'metadata/xsDeploy.yaml\''),
new CommandLineMatcher()
@ -176,7 +180,7 @@ class XsDeployTest extends BasePiperTest {
nullScript.commonPipelineEnvironment.xsDeploymentId = '1234'
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig --stepMetadata.*', '{"mode": "BG_DEPLOY", "action": "RESUME", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'getConfig.* (?!--contextConfig)', '{"mode": "BG_DEPLOY", "action": "RESUME", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
stepRule.step.xsDeploy(
script: nullScript,
@ -202,7 +206,7 @@ class XsDeployTest extends BasePiperTest {
containsString('No operationId provided'),
containsString('Was there a deployment before?')))
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*getConfig --stepMetadata.*', '{"mode": "BG_DEPLOY", "action": "RESUME", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'getConfig.* (?!--contextConfig)', '{"mode": "BG_DEPLOY", "action": "RESUME", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, nullValue())
@ -212,4 +216,61 @@ class XsDeployTest extends BasePiperTest {
failOnError: true,
)
}
@Test
public void testAdditionalCustomConfigLayers() {
def resources = ['a.yml': '- x: y}', 'b.yml' : '- a: b}']
helper.registerAllowedMethod('libraryResource', [String], {
r ->
def resource = resources[r]
if(resource) return resource
File res = new File(new File('resources'), r)
if (res.exists()) {
return res.getText()
}
throw new RuntimeException("Resource '${r}' not found.")
})
assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, nullValue())
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*xsDeploy .*', '{"operationId": "1234"}')
nullScript.commonPipelineEnvironment = ['reset': {}, 'getCustomDefaults': {['a.yml', 'b.yml']}]
goUtils = new PiperGoUtils(null) {
void unstashPiperBin() {
}
}
stepRule.step.xsDeploy(
script: nullScript,
piperGoUtils: goUtils
)
assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, is('1234'))
assertThat(writeFileRule.files.keySet(), containsInAnyOrder(
'.pipeline/additionalConfigs/a.yml',
'.pipeline/additionalConfigs/b.yml',
'.pipeline/additionalConfigs/default_pipeline_environment.yml',
'metadata/xsDeploy.yaml',
))
assertThat(shellRule.shell,
allOf(
new CommandLineMatcher()
.hasProlog('./piper getConfig')
.hasArgument('--contextConfig')
.hasArgument('--defaultConfig "b.yml" "a.yml" "default_pipeline_environment.yml"'),
new CommandLineMatcher()
.hasProlog('./piper getConfig --stepMetadata \'metadata/xsDeploy.yaml\''),
)
)
}
}

View File

@ -1,5 +1,6 @@
import com.sap.piper.ConfigurationLoader
import com.sap.piper.ConfigurationMerger
import com.sap.piper.DefaultValueCache
import com.sap.piper.analytics.InfluxData
class commonPipelineEnvironment implements Serializable {
@ -143,4 +144,8 @@ class commonPipelineEnvironment implements Serializable {
config = ConfigurationMerger.merge(configuration.get('stages')?.get(stageName) ?: [:], null, config)
return config
}
List getCustomDefaults() {
DefaultValueCache.getInstance().getCustomDefaults()
}
}

View File

@ -1,5 +1,6 @@
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.DefaultValueCache
import com.sap.piper.JenkinsUtils
import com.sap.piper.PiperGoUtils
@ -10,6 +11,7 @@ import com.sap.piper.Utils
import groovy.transform.Field
@Field String METADATA_FILE = 'metadata/xsDeploy.yaml'
@Field String PIPER_DEFAULTS = 'default_pipeline_environment.yml'
@Field String STEP_NAME = getClass().getName()
@ -76,8 +78,16 @@ void call(Map parameters = [:]) {
step: STEP_NAME,
], null)
writeFile(file: METADATA_FILE, text: libraryResource(METADATA_FILE))
List configs = [PIPER_DEFAULTS]
configs.addAll(script.commonPipelineEnvironment.getCustomDefaults())
configs = configs.reverse()
for(def customDefault : configs) {
writeFile(file: ".pipeline/additionalConfigs/${customDefault}", text: libraryResource(customDefault))
}
writeFile(file: METADATA_FILE, text: libraryResource(METADATA_FILE))
withEnv([
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(parameters)}",
@ -87,9 +97,9 @@ void call(Map parameters = [:]) {
// context config gives us e.g. the docker image name. --> How does this work for customer maintained images?
// There is a name provided in the metadata file. But we do not provide a docker image for that.
// The user has to build that for her/his own. How do we expect to configure this?
Map contextConfig = readJSON (text: sh(returnStdout: true, script: "./piper getConfig --contextConfig --stepMetadata '${METADATA_FILE}'"))
Map contextConfig = readJSON (text: sh(returnStdout: true, script: "./piper ${parameters.verbose ? '--verbose' :''} getConfig --stepMetadata '${METADATA_FILE}' --defaultConfig ${joinAndQuote(configs)} --contextConfig"))
Map projectConfig = readJSON (text: sh(returnStdout: true, script: "./piper ${parameters.verbose ? '--verbose' :''} getConfig --stepMetadata '${METADATA_FILE}'"))
Map projectConfig = readJSON (text: sh(returnStdout: true, script: "./piper ${parameters.verbose ? '--verbose' :''} getConfig --stepMetadata '${METADATA_FILE}' --defaultConfig ${joinAndQuote(configs)}"))
if(parameters.verbose) {
echo "[INFO] Context-Config: ${contextConfig}"
@ -155,3 +165,11 @@ void call(Map parameters = [:]) {
String getLockIdentifier(Map config) {
"$STEP_NAME:${config.apiUrl}:${config.org}:${config.space}"
}
String joinAndQuote(List l) {
_l = []
for(def e : l) {
_l << '"' + e + '"'
}
_l.join(' ')
}