blob: e6d3070b3cd83ad8a3bf474e131560fe4c015c8f [file] [log] [blame]
chnydae80bb922017-05-29 17:48:40 +02001common = new com.mirantis.mk.Common()
chnydabc63c9a2017-05-30 15:37:54 +02002gerrit = new com.mirantis.mk.Gerrit()
chnydae80bb922017-05-29 17:48:40 +02003git = new com.mirantis.mk.Git()
4python = new com.mirantis.mk.Python()
chnydae80bb922017-05-29 17:48:40 +02005
azvyagintsev5c0313d2018-08-13 17:13:35 +03006gerritRef = env.GERRIT_REFSPEC ?: null
7slaveNode = (env.SLAVE_NODE ?: 'python&&docker')
8def alreadyMerged = false
9
Vasyl Saienko682043d2018-07-23 16:04:10 +030010def reclassVersion = 'v1.5.4'
Vasyl Saienko772e1232018-07-23 14:42:24 +030011if (common.validInputParam('RECLASS_VERSION')) {
azvyagintsev5c0313d2018-08-13 17:13:35 +030012 reclassVersion = RECLASS_VERSION
Vasyl Saienko772e1232018-07-23 14:42:24 +030013}
14
chnyda467f10f2017-05-30 17:25:07 +020015def generateSaltMaster(modEnv, clusterDomain, clusterName) {
azvyagintsev5c0313d2018-08-13 17:13:35 +030016 def nodeFile = "${modEnv}/nodes/cfg01.${clusterDomain}.yml"
17 def nodeString = """classes:
chnydae80bb922017-05-29 17:48:40 +020018- cluster.${clusterName}.infra.config
19parameters:
20 _param:
21 linux_system_codename: xenial
22 reclass_data_revision: master
23 linux:
24 system:
25 name: cfg01
26 domain: ${clusterDomain}
27"""
azvyagintsev5c0313d2018-08-13 17:13:35 +030028 sh "mkdir -p ${modEnv}/nodes/"
29 println "Create file ${nodeFile}"
30 writeFile(file: nodeFile, text: nodeString)
azvyagintsev87985532018-07-10 20:49:38 +030031}
32
azvyagintsev30bc82e2018-08-22 12:26:06 +030033/**
34 *
35 * @param contextFile - path to `contexts/XXX.yaml file`
36 * @param virtualenv - pyvenv with CC and dep's
37 * @param templateEnvDir - root of CookieCutter
38 * @return
39 */
40
azvyagintsev5c0313d2018-08-13 17:13:35 +030041def generateModel(contextFile, virtualenv, templateEnvDir) {
42 def modelEnv = "${templateEnvDir}/model"
43 def basename = common.GetBaseName(contextFile, '.yml')
44 def generatedModel = "${modelEnv}/${basename}"
45 def content = readFile(file: "${templateEnvDir}/contexts/${contextFile}")
46 def templateContext = readYaml text: content
47 def clusterDomain = templateContext.default_context.cluster_domain
48 def clusterName = templateContext.default_context.cluster_name
49 def outputDestination = "${generatedModel}/classes/cluster/${clusterName}"
50 def templateBaseDir = templateEnvDir
51 def templateDir = "${templateEnvDir}/dir"
52 def templateOutputDir = templateBaseDir
azvyagintsev30bc82e2018-08-22 12:26:06 +030053 dir(templateEnvDir) {
54 sh(script: "rm -rf ${generatedModel} || true")
55 common.infoMsg("Generating model from context ${contextFile}")
56 def productList = ["infra", "cicd", "opencontrail", "kubernetes", "openstack", "oss", "stacklight", "ceph"]
57 for (product in productList) {
chnydae80bb922017-05-29 17:48:40 +020058
azvyagintsev30bc82e2018-08-22 12:26:06 +030059 // get templateOutputDir and productDir
60 if (product.startsWith("stacklight")) {
61 templateOutputDir = "${templateEnvDir}/output/stacklight"
62 try {
63 productDir = "stacklight" + templateContext.default_context['stacklight_version']
64 } catch (Throwable e) {
65 productDir = "stacklight1"
66 }
67 } else {
68 templateOutputDir = "${templateEnvDir}/output/${product}"
69 productDir = product
azvyagintsev5c0313d2018-08-13 17:13:35 +030070 }
azvyagintsev30bc82e2018-08-22 12:26:06 +030071
72 if (product == "infra" || (templateContext.default_context["${product}_enabled"]
73 && templateContext.default_context["${product}_enabled"].toBoolean())) {
74
75 templateDir = "${templateEnvDir}/cluster_product/${productDir}"
76 common.infoMsg("Generating product " + product + " from " + templateDir + " to " + templateOutputDir)
77
78 sh "rm -rf ${templateOutputDir} || true"
79 sh "mkdir -p ${templateOutputDir}"
80 sh "mkdir -p ${outputDestination}"
81
82 python.buildCookiecutterTemplate(templateDir, content, templateOutputDir, virtualenv, templateBaseDir)
83 sh "mv -v ${templateOutputDir}/${clusterName}/* ${outputDestination}"
84 } else {
85 common.warningMsg("Product " + product + " is disabled")
86 }
azvyagintsev5c0313d2018-08-13 17:13:35 +030087 }
azvyagintsev30bc82e2018-08-22 12:26:06 +030088 generateSaltMaster(generatedModel, clusterDomain, clusterName)
chnydae80bb922017-05-29 17:48:40 +020089 }
chnydae80bb922017-05-29 17:48:40 +020090}
91
azvyagintsev87985532018-07-10 20:49:38 +030092
azvyagintsev5c0313d2018-08-13 17:13:35 +030093def testModel(modelFile, reclassVersion = 'v1.5.4') {
94 // modelFile - `modelfiname` from model/modelfiname/modelfiname.yaml
95 //* Grub all models and send it to check in paralell - by one in thread.
chnyda7dd8cd92017-12-18 10:19:25 +010096
azvyagintsev5c0313d2018-08-13 17:13:35 +030097 _values_string = """
azvyagintsev87985532018-07-10 20:49:38 +030098 ---
azvyagintsev5c0313d2018-08-13 17:13:35 +030099 MODELS_TARGZ: "${env.BUILD_URL}/artifact/patched_reclass.tar.gz"
azvyagintsev87985532018-07-10 20:49:38 +0300100 DockerCName: "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}_${modelFile.toLowerCase()}"
101 testReclassEnv: "model/${modelFile}/"
102 modelFile: "contexts/${modelFile}.yml"
103 DISTRIB_REVISION: "${DISTRIB_REVISION}"
104 EXTRA_FORMULAS: "${env.EXTRA_FORMULAS}"
105 reclassVersion: "${reclassVersion}"
106 """
azvyagintsev5c0313d2018-08-13 17:13:35 +0300107 build job: "test-mk-cookiecutter-templates-chunk", parameters: [
108 [$class: 'StringParameterValue', name: 'EXTRA_VARIABLES_YAML',
azvyagintsev30bc82e2018-08-22 12:26:06 +0300109 value : _values_string.stripIndent()],
azvyagintsev5c0313d2018-08-13 17:13:35 +0300110 ]
chnydabc63c9a2017-05-30 15:37:54 +0200111}
112
azvyagintsev5c0313d2018-08-13 17:13:35 +0300113def StepTestModel(basename) {
114 // We need to wrap what we return in a Groovy closure, or else it's invoked
115 // when this method is called, not when we pass it to parallel.
116 // To do this, you need to wrap the code below in { }, and either return
117 // that explicitly, or use { -> } syntax.
118 // return node object
119 return {
120 node(slaveNode) {
121 testModel(basename)
122 }
chnydae80bb922017-05-29 17:48:40 +0200123 }
azvyagintsev87985532018-07-10 20:49:38 +0300124}
125
azvyagintsev5c0313d2018-08-13 17:13:35 +0300126def StepPrepareCCenv(refchange, templateEnvFolder) {
127 // return git clone object
128 return {
129 // fetch needed sources
130 dir(templateEnvFolder) {
131 if (refchange) {
132 def gerritChange = gerrit.getGerritChange(GERRIT_NAME, GERRIT_HOST, GERRIT_CHANGE_NUMBER, CREDENTIALS_ID)
133 merged = gerritChange.status == "MERGED"
134 if (!merged) {
135 checkouted = gerrit.gerritPatchsetCheckout([
136 credentialsId: CREDENTIALS_ID
137 ])
138 } else {
139 // update global variable for success return from pipeline
140 //alreadyMerged = true
141 common.successMsg("Change ${GERRIT_CHANGE_NUMBER} is already merged, no need to gate them")
142 currentBuild.result = 'ABORTED'
143 throw new hudson.AbortException('change already merged')
azvyagintsev87985532018-07-10 20:49:38 +0300144 }
azvyagintsev5c0313d2018-08-13 17:13:35 +0300145 } else {
146 git.checkoutGitRepository(templateEnvFolder, COOKIECUTTER_TEMPLATE_URL, COOKIECUTTER_TEMPLATE_BRANCH, CREDENTIALS_ID)
azvyagintsev87985532018-07-10 20:49:38 +0300147 }
azvyagintsev5c0313d2018-08-13 17:13:35 +0300148 }
azvyagintsev87985532018-07-10 20:49:38 +0300149 }
azvyagintsev5c0313d2018-08-13 17:13:35 +0300150}
151
152def StepGenerateModels(_contextFileList, _virtualenv, _templateEnvDir) {
153 return {
154 for (contextFile in _contextFileList) {
155 generateModel(contextFile, _virtualenv, _templateEnvDir)
156 }
157 }
158}
159
160timeout(time: 1, unit: 'HOURS') {
161 node(slaveNode) {
azvyagintsev30bc82e2018-08-22 12:26:06 +0300162 def templateEnvHead = "${env.WORKSPACE}/EnvHead/"
163 def templateEnvPatched = "${env.WORKSPACE}/EnvPatched/"
azvyagintsev5c0313d2018-08-13 17:13:35 +0300164 def contextFileListHead = []
165 def contextFileListPatched = []
166 def vEnv = "${env.WORKSPACE}/venv"
167
168 try {
169 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
170 stage('Download and prepare CC env') {
171 // Prepare 2 env - for patchset, and for HEAD
172 paralellEnvs = [:]
173 paralellEnvs.failFast = true
174 paralellEnvs['downloadEnvHead'] = StepPrepareCCenv('', templateEnvHead)
175 paralellEnvs['downloadEnvPatched'] = StepPrepareCCenv(gerritRef, templateEnvPatched)
176 parallel paralellEnvs
177 }
178 stage("Check workflow_definition") {
179 // Check only for patchset
180 python.setupVirtualenv(vEnv, 'python2', [], "${templateEnvPatched}/requirements.txt")
181 common.infoMsg(python.runVirtualenvCommand(vEnv, "python ${templateEnvPatched}/workflow_definition_test.py"))
182 }
183
184 stage("generate models") {
185 dir("${templateEnvHead}/contexts") {
186 for (String x : findFiles(glob: "*.yml")) {
187 contextFileListHead.add(x)
188 }
189 }
190 dir("${templateEnvPatched}/contexts") {
191 for (String x : findFiles(glob: "*.yml")) {
192 contextFileListPatched.add(x)
193 }
194 }
195 // Generate over 2env's - for patchset, and for HEAD
196 paralellEnvs = [:]
197 paralellEnvs.failFast = true
azvyagintsev30bc82e2018-08-22 12:26:06 +0300198 paralellEnvs['GenerateEnvPatched'] = StepGenerateModels(contextFileListPatched, vEnv, templateEnvPatched)
199 paralellEnvs['GenerateEnvHead'] = StepGenerateModels(contextFileListHead, vEnv, templateEnvHead)
azvyagintsev5c0313d2018-08-13 17:13:35 +0300200 parallel paralellEnvs
201
202 // Collect artifacts
203 dir(templateEnvPatched) {
204 // Collect only models. For backward comparability - who know, probably someone use it..
205 sh(script: "tar -czf model.tar.gz -C model ../contexts .", returnStatus: true)
206 archiveArtifacts artifacts: "model.tar.gz"
207 }
208
209 // to be able share reclass for all subenvs
210 // Also, makes artifact test more solid - use one reclass for all of sub-models.
211 // Archive Structure will be:
212 // tar.gz
213 // ├── contexts
214 // │   └── ceph.yml
215 // ├── global_reclass <<< reclass system
216 // ├── model
217 // │   └── ceph <<< from `context basename`
218 // │   ├── classes
219 // │   │   ├── cluster
220 // │   │   └── system -> ../../../global_reclass
221 // │   └── nodes
222 // │   └── cfg01.ceph-cluster-domain.local.yml
223
224 if (SYSTEM_GIT_URL == "") {
225 git.checkoutGitRepository("${env.WORKSPACE}/global_reclass/", RECLASS_MODEL_URL, RECLASS_MODEL_BRANCH, CREDENTIALS_ID)
226 } else {
227 dir("${env.WORKSPACE}/global_reclass/") {
228 if (!gerrit.gerritPatchsetCheckout(SYSTEM_GIT_URL, SYSTEM_GIT_REF, "HEAD", CREDENTIALS_ID)) {
229 common.errorMsg("Failed to obtain system reclass with url: ${SYSTEM_GIT_URL} and ${SYSTEM_GIT_REF}")
230 throw new RuntimeException("Failed to obtain system reclass")
231 }
232 }
233 }
234 // link all models, to use one global reclass
235 // For HEAD
236 dir(templateEnvHead) {
237 for (String context : contextFileListHead) {
238 def basename = common.GetBaseName(context, '.yml')
239 dir("${templateEnvHead}/model/${basename}") {
240 sh(script: 'mkdir -p classes/; ln -sfv ../../../../global_reclass classes/system ')
241 }
242 }
243 // Save all models and all contexts. Warning! `h` flag must be used.
244 sh(script: "tar -chzf head_reclass.tar.gz --exclude='*@tmp' model contexts global_reclass", returnStatus: true)
245 archiveArtifacts artifacts: "head_reclass.tar.gz"
246 // move for "Compare Pillars" stage
247 sh(script: "mv -v head_reclass.tar.gz ${env.WORKSPACE}")
248 }
249 // For patched
250 dir(templateEnvPatched) {
251 for (String context : contextFileListPatched) {
252 def basename = common.GetBaseName(context, '.yml')
253 dir("${templateEnvPatched}/model/${basename}") {
254 sh(script: 'mkdir -p classes/; ln -sfv ../../../../global_reclass classes/system ')
255 }
256 }
257 // Save all models and all contexts. Warning! `h` flag must be used.
258 sh(script: "tar -chzf patched_reclass.tar.gz --exclude='*@tmp' model contexts global_reclass", returnStatus: true)
259 archiveArtifacts artifacts: "patched_reclass.tar.gz"
260 // move for "Compare Pillars" stage
261 sh(script: "mv -v patched_reclass.tar.gz ${env.WORKSPACE}")
262 }
263 }
264
265 stage("Compare Pillars") {
266 // Compare patched and HEAD reclass pillars
267 compareRoot = "${env.WORKSPACE}/test_compare/"
268 sh(script: """
269 mkdir -pv ${compareRoot}/new ${compareRoot}/old
270 tar -xzf patched_reclass.tar.gz --directory ${compareRoot}/new
271 tar -xzf head_reclass.tar.gz --directory ${compareRoot}/old
272 """)
273 common.warningMsg('infra/secrets.yml has been skipped from compare!')
azvyagintsev30bc82e2018-08-22 12:26:06 +0300274 rezult = common.comparePillars(compareRoot, env.BUILD_URL, "-Ev \'infra/secrets.yml\'")
azvyagintsev5c0313d2018-08-13 17:13:35 +0300275 currentBuild.description = rezult
276 }
277 stage("test-contexts") {
278 // Test contexts for patched only
279 stepsForParallel = [:]
280 common.infoMsg("Found: ${contextFileListPatched.size()} patched contexts to test.")
281 for (String context : contextFileListPatched) {
282 def basename = common.GetBaseName(context, '.yml')
283 stepsForParallel.put("ContextPatchTest:${basename}", StepTestModel(basename))
284 }
285 parallel stepsForParallel
286 common.infoMsg('All tests done')
287 }
288
289 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
290
291 } catch (Throwable e) {
292 currentBuild.result = "FAILURE"
293 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
294 throw e
295 } finally {
296 def dummy = "dummy"
297 //FAILING common.sendNotification(currentBuild.result,"",["slack"])
298 }
299 }
chnydae80bb922017-05-29 17:48:40 +0200300}