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 Map defaultValues
private DefaultValueCache(Map defaultValues){ private List customDefaults = []
private DefaultValueCache(Map defaultValues, List customDefaults){
this.defaultValues = defaultValues this.defaultValues = defaultValues
if(customDefaults) {
this.customDefaults.addAll(customDefaults)
}
} }
static getInstance(){ static getInstance(){
return instance return instance
} }
static createInstance(Map defaultValues){ static createInstance(Map defaultValues, List customDefaults = []){
instance = new DefaultValueCache(defaultValues) instance = new DefaultValueCache(defaultValues, customDefaults)
} }
Map getDefaultValues(){ Map getDefaultValues(){
@ -28,6 +33,12 @@ class DefaultValueCache implements Serializable {
instance = null instance = null
} }
List getCustomDefaults() {
def result = []
result.addAll(customDefaults)
return result
}
static void prepare(Script steps, Map parameters = [:]) { static void prepare(Script steps, Map parameters = [:]) {
if(parameters == null) parameters = [:] if(parameters == null) parameters = [:]
if(!DefaultValueCache.getInstance() || parameters.customDefaults) { if(!DefaultValueCache.getInstance() || parameters.customDefaults) {
@ -46,7 +57,7 @@ class DefaultValueCache implements Serializable {
MapUtils.pruneNulls(defaultValues), MapUtils.pruneNulls(defaultValues),
MapUtils.pruneNulls(configuration)) 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.allOf
import static org.hamcrest.Matchers.contains import static org.hamcrest.Matchers.contains
import static org.hamcrest.Matchers.containsInAnyOrder
import static org.hamcrest.Matchers.containsString import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.equalTo import static org.hamcrest.Matchers.equalTo
import static org.hamcrest.Matchers.is import static org.hamcrest.Matchers.is
@ -63,8 +64,8 @@ class XsDeployTest extends BasePiperTest {
@Before @Before
public void init() { public void init() {
helper.registerAllowedMethod('withEnv', [List, Closure], {l, c -> env = l; c()}) 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.*--contextConfig.*', '{"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)', '{"mode": "BG_DEPLOY", "action": "NONE", "apiUrl": "https://example.org/xs", "org": "myOrg", "space": "mySpace"}')
nullScript.commonPipelineEnvironment.xsDeploymentId = null nullScript.commonPipelineEnvironment.xsDeploymentId = null
} }
@ -88,7 +89,7 @@ class XsDeployTest extends BasePiperTest {
thrown.expect(IllegalArgumentException) thrown.expect(IllegalArgumentException)
thrown.expectMessage('No enum constant') 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( stepRule.step.xsDeploy(
script: nullScript, script: nullScript,
@ -114,7 +115,6 @@ class XsDeployTest extends BasePiperTest {
piperGoUtils: goUtils piperGoUtils: goUtils
) )
// nota bene: script and piperGoUtils are not contained in the json below. // 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"}')) 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(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.dockerImage, equalTo('xs'))
assertThat(dockerRule.dockerParams.dockerPullImage, equalTo(false)) assertThat(dockerRule.dockerParams.dockerPullImage, equalTo(false))
@ -158,7 +161,8 @@ class XsDeployTest extends BasePiperTest {
new CommandLineMatcher() new CommandLineMatcher()
.hasProlog('./piper version'), .hasProlog('./piper version'),
new CommandLineMatcher() new CommandLineMatcher()
.hasProlog('./piper getConfig --contextConfig --stepMetadata \'metadata/xsDeploy.yaml\''), .hasProlog('./piper getConfig')
.hasArgument('--contextConfig'),
new CommandLineMatcher() new CommandLineMatcher()
.hasProlog('./piper getConfig --stepMetadata \'metadata/xsDeploy.yaml\''), .hasProlog('./piper getConfig --stepMetadata \'metadata/xsDeploy.yaml\''),
new CommandLineMatcher() new CommandLineMatcher()
@ -176,7 +180,7 @@ class XsDeployTest extends BasePiperTest {
nullScript.commonPipelineEnvironment.xsDeploymentId = '1234' 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( stepRule.step.xsDeploy(
script: nullScript, script: nullScript,
@ -202,7 +206,7 @@ class XsDeployTest extends BasePiperTest {
containsString('No operationId provided'), containsString('No operationId provided'),
containsString('Was there a deployment before?'))) 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()) assertThat(nullScript.commonPipelineEnvironment.xsDeploymentId, nullValue())
@ -212,4 +216,61 @@ class XsDeployTest extends BasePiperTest {
failOnError: true, 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.ConfigurationLoader
import com.sap.piper.ConfigurationMerger import com.sap.piper.ConfigurationMerger
import com.sap.piper.DefaultValueCache
import com.sap.piper.analytics.InfluxData import com.sap.piper.analytics.InfluxData
class commonPipelineEnvironment implements Serializable { class commonPipelineEnvironment implements Serializable {
@ -143,4 +144,8 @@ class commonPipelineEnvironment implements Serializable {
config = ConfigurationMerger.merge(configuration.get('stages')?.get(stageName) ?: [:], null, config) config = ConfigurationMerger.merge(configuration.get('stages')?.get(stageName) ?: [:], null, config)
return config return config
} }
List getCustomDefaults() {
DefaultValueCache.getInstance().getCustomDefaults()
}
} }

View File

@ -1,5 +1,6 @@
import static com.sap.piper.Prerequisites.checkScript import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.DefaultValueCache
import com.sap.piper.JenkinsUtils import com.sap.piper.JenkinsUtils
import com.sap.piper.PiperGoUtils import com.sap.piper.PiperGoUtils
@ -10,6 +11,7 @@ import com.sap.piper.Utils
import groovy.transform.Field import groovy.transform.Field
@Field String METADATA_FILE = 'metadata/xsDeploy.yaml' @Field String METADATA_FILE = 'metadata/xsDeploy.yaml'
@Field String PIPER_DEFAULTS = 'default_pipeline_environment.yml'
@Field String STEP_NAME = getClass().getName() @Field String STEP_NAME = getClass().getName()
@ -76,8 +78,16 @@ void call(Map parameters = [:]) {
step: STEP_NAME, step: STEP_NAME,
], null) ], 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([ withEnv([
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(parameters)}", "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? // 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. // 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? // 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) { if(parameters.verbose) {
echo "[INFO] Context-Config: ${contextConfig}" echo "[INFO] Context-Config: ${contextConfig}"
@ -155,3 +165,11 @@ void call(Map parameters = [:]) {
String getLockIdentifier(Map config) { String getLockIdentifier(Map config) {
"$STEP_NAME:${config.apiUrl}:${config.org}:${config.space}" "$STEP_NAME:${config.apiUrl}:${config.org}:${config.space}"
} }
String joinAndQuote(List l) {
_l = []
for(def e : l) {
_l << '"' + e + '"'
}
_l.join(' ')
}