blob: 83591033877b44ec52f0f357e388f528d597011a [file] [log] [blame]
Tomáš Kukrál7ded3642017-03-27 15:52:51 +02001/**
2 * Generate cookiecutter cluster by individual products
3 *
4 * Expected parameters:
Tomáš Kukrál7ded3642017-03-27 15:52:51 +02005 * COOKIECUTTER_TEMPLATE_CONTEXT Context parameters for the template generation.
Sergey Galkin8b87f6e2018-10-24 18:40:13 +04006 * CREDENTIALS_ID Credentials id for git
azvyagintsev6d678da2018-11-28 21:19:06 +02007 * TEST_MODEL Run syntax tests for model
azvyagintsev3ed704f2018-07-09 15:49:27 +03008 **/
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +00009import static groovy.json.JsonOutput.toJson
10import static groovy.json.JsonOutput.prettyPrint
Denis Egorenko032a2b72019-04-03 14:56:52 +040011import org.apache.commons.net.util.SubnetUtils
Tomáš Kukrál7ded3642017-03-27 15:52:51 +020012
13common = new com.mirantis.mk.Common()
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +000014common2 = new com.mirantis.mcp.Common()
Tomáš Kukrál7ded3642017-03-27 15:52:51 +020015git = new com.mirantis.mk.Git()
16python = new com.mirantis.mk.Python()
chnyda89191012017-05-29 15:38:35 +020017saltModelTesting = new com.mirantis.mk.SaltModelTesting()
azvyagintsev5b7ec892019-05-15 16:37:34 +030018updateSaltFormulasDuringTest = true
Tomáš Kukrál7ded3642017-03-27 15:52:51 +020019
Aleksey Zvyagintsev4a804212019-02-07 13:02:31 +000020slaveNode = env.getProperty('SLAVE_NODE') ?: 'virtual'
azvyagintsev6d678da2018-11-28 21:19:06 +020021gerritCredentials = env.getProperty('CREDENTIALS_ID') ?: 'gerrit'
22runTestModel = (env.getProperty('TEST_MODEL') ?: true).toBoolean()
azvyagintsev866b19a2018-11-20 18:21:43 +020023distribRevision = 'proposed'
24gitGuessedVersion = false
25
Denis Egorenkoce93af32019-05-20 16:57:33 +040026def GenerateModelToxDocker(Map params) {
27 def ccRoot = params['ccRoot']
28 def context = params['context']
29 def outDir = params['outDir']
30 def envOpts = params['envOpts']
31 def tempContextFile = new File(ccRoot, 'tempContext.yaml_' + UUID.randomUUID().toString()).toString()
32 writeFile file: tempContextFile, text: context
33 // Get Jenkins user UID and GID
34 def jenkinsUID = sh(script: 'id -u', returnStdout: true).trim()
35 def jenkinsGID = sh(script: 'id -g', returnStdout: true).trim()
36 /*
37 by default, process in image operates via root user
38 Otherwise, gpg key for model and all files managed by jenkins user
39 To make it compatible, install rrequirementfrom user, but generate model via jenkins
40 for build use upstream Ubuntu Bionic image
41 */
42 def configRun = ['distribRevision': 'nightly',
43 'envOpts' : envOpts + ["CONFIG_FILE=$tempContextFile",
44 "OUTPUT_DIR=${outDir}"
45 ],
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +040046 'image' : 'docker-prod-local.artifactory.mirantis.com/mirantis/cicd/jnlp-slave',
Denis Egorenkoce93af32019-05-20 16:57:33 +040047 'runCommands' : [
48 '001_prepare_generate_auto_reqs': {
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +040049 sh('''
Denis Egorenkoce93af32019-05-20 16:57:33 +040050 pip install tox
51 ''')
52 },
53 // user & group can be different on host and in docker
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +040054 '002_set_jenkins_id' : {
55 sh("""
Denis Egorenkoce93af32019-05-20 16:57:33 +040056 usermod -u ${jenkinsUID} jenkins
57 groupmod -g ${jenkinsUID} jenkins
58 """)
59 },
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +040060 '003_run_generate_auto' : {
Denis Egorenkoce93af32019-05-20 16:57:33 +040061 print('[Cookiecutter build] Result:\n' +
62 sh(returnStdout: true, script: 'cd ' + ccRoot + '; su jenkins -c "tox -ve generate_auto" '))
63 }
64 ]
65 ]
66
67 saltModelTesting.setupDockerAndTest(configRun)
68}
69
azvyagintsev866b19a2018-11-20 18:21:43 +020070def globalVariatorsUpdate() {
71 def templateContext = readYaml text: env.COOKIECUTTER_TEMPLATE_CONTEXT
72 def context = templateContext['default_context']
73 // TODO add more check's for critical var's
74 // Since we can't pin to any '_branch' variable from context, to identify 'default git revision' -
75 // because each of them, might be 'refs/' variable, we need to add some tricky trigger of using
76 // 'release/XXX' logic. This is totall guess - so,if even those one failed, to definitely must pass
77 // correct variable finally!
78 [context.get('cookiecutter_template_branch'), context.get('shared_reclass_branch'), context.get('mcp_common_scripts_branch')].any { branch ->
79 if (branch.toString().startsWith('release/')) {
80 gitGuessedVersion = branch
81 return true
82 }
83 }
azvyagintsev866b19a2018-11-20 18:21:43 +020084 // Use mcpVersion git tag if not specified branch for cookiecutter-templates
85 if (!context.get('cookiecutter_template_branch')) {
86 context['cookiecutter_template_branch'] = gitGuessedVersion ?: context['mcp_version']
87 }
88 // Don't have n/t/s for cookiecutter-templates repo, therefore use master
89 if (["nightly", "testing", "stable"].contains(context['cookiecutter_template_branch'])) {
90 context['cookiecutter_template_branch'] = 'master'
91 }
92 if (!context.get('shared_reclass_branch')) {
93 context['shared_reclass_branch'] = gitGuessedVersion ?: context['mcp_version']
94 }
95 // Don't have nightly/testing for reclass-system repo, therefore use master
96 if (["nightly", "testing", "stable"].contains(context['shared_reclass_branch'])) {
97 context['shared_reclass_branch'] = 'master'
98 }
99 if (!context.get('mcp_common_scripts_branch')) {
100 // Pin exactly to CC branch, since it might use 'release/XXX' format
101 context['mcp_common_scripts_branch'] = gitGuessedVersion ?: context['mcp_version']
102 }
103 // Don't have n/t/s for mcp-common-scripts repo, therefore use master
104 if (["nightly", "testing", "stable"].contains(context['mcp_common_scripts_branch'])) {
105 context['mcp_common_scripts_branch'] = 'master'
106 }
107 //
108 distribRevision = context['mcp_version']
109 if (['master'].contains(context['mcp_version'])) {
110 distribRevision = 'nightly'
111 }
112 if (distribRevision.contains('/')) {
113 distribRevision = distribRevision.split('/')[-1]
114 }
115 // Check if we are going to test bleeding-edge release, which doesn't have binary release yet
azvyagintsev8e081642019-02-03 19:15:22 +0200116 // After 2018q4 releases, need to also check 'static' repo, for example ubuntu.
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200117 def binTest = common.checkRemoteBinary(['mcp_version': distribRevision])
azvyagintsev8e081642019-02-03 19:15:22 +0200118 if (!binTest.linux_system_repo_url || !binTest.linux_system_repo_ubuntu_url) {
119 common.errorMsg("Binary release: ${distribRevision} not exist or not full. Fallback to 'proposed'! ")
azvyagintsev866b19a2018-11-20 18:21:43 +0200120 distribRevision = 'proposed'
121 }
azvyagintsev8e081642019-02-03 19:15:22 +0200122
azvyagintsev5400d1d2018-12-11 13:19:29 +0200123 // (azvyagintsev) WA for PROD-25732
124 if (context.cookiecutter_template_url.contains('gerrit.mcp.mirantis.com/mk/cookiecutter-templates')) {
125 common.warningMsg('Apply WA for PROD-25732')
126 context.cookiecutter_template_url = 'ssh://gerrit.mcp.mirantis.com:29418/mk/cookiecutter-templates.git'
127 }
azvyagintsev5b7ec892019-05-15 16:37:34 +0300128 // check, if we are going to test clear release version, w\o any updates and patches
129 if (!gitGuessedVersion && (distribRevision == context.mcp_version)) {
130 updateSaltFormulasDuringTest = false
131 }
132
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +0400133 if (gitGuessedVersion == 'release/proposed/2019.2.0') {
134 // CFG node in 2019.2.X update has to be bootstrapped with update/proposed repository for salt formulas
135 context['cloudinit_master_config']['MCP_SALT_REPO_UPDATES'] =
136 'deb [arch=amd64] http://mirror.mirantis.com/update/proposed/salt-formulas/xenial xenial main'
137 }
138
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000139 common.infoMsg("Using context:\n" + context)
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000140 print prettyPrint(toJson(context))
azvyagintsev866b19a2018-11-20 18:21:43 +0200141 return context
142
143}
azvyagintsevf252b592018-08-13 18:39:14 +0300144
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000145timeout(time: 1, unit: 'HOURS') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000146 node(slaveNode) {
azvyagintsev866b19a2018-11-20 18:21:43 +0200147 def context = globalVariatorsUpdate()
azvyagintsev6d678da2018-11-28 21:19:06 +0200148 def RequesterEmail = context.get('email_address', '')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000149 def templateEnv = "${env.WORKSPACE}/template"
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200150 // modelEnv - this is reclass root, aka /srv/salt/reclass
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000151 def modelEnv = "${env.WORKSPACE}/model"
152 def testEnv = "${env.WORKSPACE}/test"
153 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +0200154
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000155 try {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000156 //
157 def cutterEnv = "${env.WORKSPACE}/cutter"
158 def systemEnv = "${modelEnv}/classes/system"
159 def testResult = false
160 def user
161 wrap([$class: 'BuildUser']) {
162 user = env.BUILD_USER_ID
163 }
azvyagintsev6d678da2018-11-28 21:19:06 +0200164 currentBuild.description = "${context['cluster_name']} ${RequesterEmail}"
azvyagintsev866b19a2018-11-20 18:21:43 +0200165
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000166 stage('Download Cookiecutter template') {
167 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
168 checkout([
169 $class : 'GitSCM',
170 branches : [[name: 'FETCH_HEAD'],],
171 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
172 userRemoteConfigs: [[url: context['cookiecutter_template_url'], refspec: context['cookiecutter_template_branch'], credentialsId: gerritCredentials],],
173 ])
174 }
175 stage('Create empty reclass model') {
176 dir(path: modelEnv) {
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200177 sh 'rm -rfv .git; git init'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000178 sshagent(credentials: [gerritCredentials]) {
179 sh "git submodule add ${context['shared_reclass_url']} 'classes/system'"
180 }
181 }
182 checkout([
183 $class : 'GitSCM',
184 branches : [[name: 'FETCH_HEAD'],],
185 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
186 userRemoteConfigs: [[url: context['shared_reclass_url'], refspec: context['shared_reclass_branch'], credentialsId: gerritCredentials],],
187 ])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200188 git.commitGitChanges(modelEnv, 'Added new shared reclass submodule', "${user}@localhost", "${user}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000189 }
190
191 stage('Generate model') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000192 // GNUPGHOME environment variable is required for all gpg commands
193 // and for python.generateModel execution
Denis Egorenkoce93af32019-05-20 16:57:33 +0400194 def envOpts = ["GNUPGHOME=${env.WORKSPACE}/gpghome"]
195 withEnv(envOpts) {
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300196 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000197 sh "mkdir gpghome; chmod 700 gpghome"
Dmitry Pyzhovb5c74c72018-12-17 22:08:50 +0300198 def secretKeyID = RequesterEmail ?: "salt@${context['cluster_domain']}".toString()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300199 if (!context.get('secrets_encryption_private_key')) {
200 def batchData = """
Denis Egorenko131de5f2019-05-16 14:25:40 +0400201 %echo Generating a basic OpenPGP key for Salt-Master
202 %no-protection
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300203 Key-Type: 1
204 Key-Length: 4096
205 Expire-Date: 0
206 Name-Real: ${context['salt_master_hostname']}.${context['cluster_domain']}
207 Name-Email: ${secretKeyID}
Denis Egorenko131de5f2019-05-16 14:25:40 +0400208 %commit
209 %echo done
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300210 """.stripIndent()
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200211 writeFile file: 'gpg-batch.txt', text: batchData
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300212 sh "gpg --gen-key --batch < gpg-batch.txt"
213 sh "gpg --export-secret-key -a ${secretKeyID} > gpgkey.asc"
214 } else {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200215 writeFile file: 'gpgkey.asc', text: context['secrets_encryption_private_key']
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300216 sh "gpg --import gpgkey.asc"
Denis Egorenko131de5f2019-05-16 14:25:40 +0400217 secretKeyID = sh(returnStdout: true, script: 'gpg --list-secret-keys --with-colons | grep -E "^sec" | awk -F: \'{print \$5}\'').trim()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300218 }
219 context['secrets_encryption_key_id'] = secretKeyID
220 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400221 if (context.get('cfg_failsafe_ssh_public_key')) {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200222 writeFile file: 'failsafe-ssh-key.pub', text: context['cfg_failsafe_ssh_public_key']
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400223 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200224 if (!fileExists(new File(templateEnv, 'tox.ini').toString())) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200225 reqs = new File(templateEnv, 'requirements.txt').toString()
226 if (fileExists(reqs)) {
227 python.setupVirtualenv(cutterEnv, 'python2', [], reqs)
228 } else {
229 python.setupCookiecutterVirtualenv(cutterEnv)
230 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200231 python.generateModel(common2.dumpYAML(['default_context': context]), 'default_context', context['salt_master_hostname'], cutterEnv, modelEnv, templateEnv, false)
232 } else {
233 // tox-based CC generated structure of reclass,from the root. Otherwise for bw compat, modelEnv
234 // still expect only lower lvl of project, aka model/classes/cluster/XXX/. So,lets dump result into
235 // temp dir, and then copy it over initial structure.
236 reclassTempRootDir = sh(script: "mktemp -d -p ${env.WORKSPACE}", returnStdout: true).trim()
Denis Egorenkoce93af32019-05-20 16:57:33 +0400237 GenerateModelToxDocker(['context': common2.dumpYAML(['default_context': context]),
238 'ccRoot' : templateEnv,
239 'outDir' : reclassTempRootDir,
240 'envOpts': envOpts])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200241 dir(modelEnv) {
242 common.warningMsg('Forming reclass-root structure...')
243 sh("cp -ra ${reclassTempRootDir}/reclass/* .")
244 }
245 }
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300246 git.commitGitChanges(modelEnv, "Create model ${context['cluster_name']}", "${user}@localhost", "${user}")
247 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000248 }
249
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000250 stage("Test") {
azvyagintsev6d678da2018-11-28 21:19:06 +0200251 if (runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000252 sh("cp -r ${modelEnv} ${testEnv}")
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200253 if (fileExists('gpgkey.asc')) {
254 common.infoMsg('gpgkey.asc found!Copy it into reclass folder for tests..')
255 sh("cp -v gpgkey.asc ${testEnv}/salt_master_pillar.asc")
256 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000257 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
azvyagintsev5b7ec892019-05-15 16:37:34 +0300258 common.warningMsg("Attempt to run test against:\n" +
259 "DISTRIB_REVISION from ${distribRevision}\n" +
260 "updateSaltFormulasDuringTest = ${updateSaltFormulasDuringTest}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000261 try {
262 def config = [
Denis Egorenko926bc7d2019-02-19 14:27:34 +0400263 'dockerHostname' : "${context['salt_master_hostname']}",
264 'domain' : "${context['cluster_domain']}",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000265 'reclassEnv' : testEnv,
266 'distribRevision' : distribRevision,
267 'dockerContainerName': DockerCName,
Denis Egorenkod87490e2019-02-26 20:00:41 +0400268 'testContext' : 'salt-model-node',
azvyagintsev5b7ec892019-05-15 16:37:34 +0300269 'dockerExtraOpts' : ['--memory=3g'],
270 'updateSaltFormulas' : updateSaltFormulasDuringTest
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000271 ]
272 testResult = saltModelTesting.testNode(config)
273 common.infoMsg("Test finished: SUCCESS")
274 } catch (Exception ex) {
275 common.warningMsg("Test finished: FAILED")
276 testResult = false
277 }
278 } else {
279 common.warningMsg("Test stage has been skipped!")
280 }
281 }
282 stage("Generate config drives") {
283 // apt package genisoimage is required for this stage
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000284 // download create-config-drive
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000285 def commonScriptsRepoUrl = context['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
286 checkout([
287 $class : 'GitSCM',
288 branches : [[name: 'FETCH_HEAD'],],
289 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
azvyagintsev866b19a2018-11-20 18:21:43 +0200290 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: context['mcp_common_scripts_branch'], credentialsId: gerritCredentials],],
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000291 ])
292
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400293 def outdateGeneration = false
294 if (fileExists('mcp-common-scripts/config-drive/create_config_drive.py')) {
295 sh 'cp mcp-common-scripts/config-drive/create_config_drive.py create-config-drive.py'
296 } else {
297 outdateGeneration = true
298 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
299 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000300 sh '[ -f mcp-common-scripts/config-drive/master_config.sh ] && cp mcp-common-scripts/config-drive/master_config.sh user_data || cp mcp-common-scripts/config-drive/master_config.yaml user_data'
301
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000302 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
303 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400304 args = [
azvyagintsev1385db92019-03-22 16:05:10 +0200305 "--user-data user_data", "--model ${modelEnv}",
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400306 "--mk-pipelines ${pipelineEnv}/mk-pipelines/", "--pipeline-library ${pipelineEnv}/pipeline-library/"
307 ]
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300308 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400309 args.add('--gpg-key gpgkey.asc')
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300310 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400311 if (context.get('cfg_failsafe_ssh_public_key')) {
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400312 if (outdateGeneration) {
313 args.add('--ssh-key failsafe-ssh-key.pub')
314 } else {
Denis Egorenko85ddf0d2019-03-18 18:11:19 +0400315 if (context.get('cfg_failsafe_user')) {
316 args.add('--ssh-keys failsafe-ssh-key.pub')
317 args.add("--cloud-user-name ${context.get('cfg_failsafe_user')}")
318 }
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400319 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400320 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000321 // load data from model
322 def smc = [:]
323 smc['SALT_MASTER_MINION_ID'] = "${context['salt_master_hostname']}.${context['cluster_domain']}"
324 smc['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Ivan Berezovskiy1cb295e2019-05-24 17:30:47 +0400325 if (context.get('cloudinit_master_config', false)) {
326 context['cloudinit_master_config'].each { k, v ->
327 smc[k] = v
328 }
329 }
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400330 if (outdateGeneration) {
331 smc['DEPLOY_NETWORK_GW'] = context['deploy_network_gateway']
332 smc['DEPLOY_NETWORK_NETMASK'] = context['deploy_network_netmask']
333 if (context.get('deploy_network_mtu')) {
334 smc['DEPLOY_NETWORK_MTU'] = context['deploy_network_mtu']
335 }
336 smc['DNS_SERVERS'] = context['dns_server01']
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000337 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000338 smc['MCP_VERSION'] = "${context['mcp_version']}"
339 if (context['local_repositories'] == 'True') {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400340 def localRepoIP = ''
Denis Egorenkof97aec22019-02-05 14:57:33 +0400341 if (context['mcp_version'] in ['2018.4.0', '2018.8.0', '2018.8.0-milestone1', '2018.11.0']) {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400342 localRepoIP = context['local_repo_url']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400343 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
344 } else {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400345 localRepoIP = context['aptly_server_deploy_address']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400346 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}"
347 }
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400348 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000349 smc['PIPELINES_FROM_ISO'] = 'false'
350 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
351 smc['LOCAL_REPOS'] = 'true'
352 }
353 if (context['upstream_proxy_enabled'] == 'True') {
354 if (context['upstream_proxy_auth_enabled'] == 'True') {
355 smc['http_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
356 smc['https_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
357 } else {
358 smc['http_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
359 smc['https_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
360 }
361 }
362
363 for (i in common.entries(smc)) {
364 sh "sed -i 's,${i[0]}=.*,${i[0]}=${i[1]},' user_data"
365 }
366
Denis Egorenko032a2b72019-04-03 14:56:52 +0400367 // calculate netmask
Denis Egorenko1f131b72019-04-05 14:42:48 +0400368 def deployNetworkSubnet = ''
369 if (context.get('deploy_network_subnet')) {
370 def subnet = new SubnetUtils(context['deploy_network_subnet'])
371 deployNetworkSubnet = subnet.getInfo().getNetmask()
372 } else if (context.get('deploy_network_netmask')) { // case for 2018.4.0
373 deployNetworkSubnet = context['deploy_network_netmask']
374 } else {
375 error('Neither context parameter deploy_network_subnet or deploy_network_netmask should be set!')
376 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000377 // create cfg config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400378 if (outdateGeneration) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200379 args += ["--hostname ${context['salt_master_hostname']}", "${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"]
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400380 sh "./create-config-drive ${args.join(' ')}"
381 } else {
382 args += [
383 "--name ${context['salt_master_hostname']}", "--hostname ${context['salt_master_hostname']}.${context['cluster_domain']}", "--clean-up",
Denis Egorenko032a2b72019-04-03 14:56:52 +0400384 "--ip ${context['salt_master_management_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400385 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400386 ]
azvyagintsevc4c047a2019-04-04 17:42:48 +0300387 sh "chmod 0755 create-config-drive.py ; ./create-config-drive.py ${args.join(' ')}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400388 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000389 sh("mkdir output-${context['cluster_name']} && mv ${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000390
391 // save cfg iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000392 archiveArtifacts artifacts: "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000393
394 if (context['local_repositories'] == 'True') {
395 def aptlyServerHostname = context.aptly_server_hostname
396 sh "[ -f mcp-common-scripts/config-drive/mirror_config.yaml ] && cp mcp-common-scripts/config-drive/mirror_config.yaml mirror_config || cp mcp-common-scripts/config-drive/mirror_config.sh mirror_config"
397
398 def smc_apt = [:]
399 smc_apt['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400400 if (outdateGeneration) {
401 smc_apt['APTLY_DEPLOY_IP'] = context['aptly_server_deploy_address']
402 smc_apt['APTLY_DEPLOY_NETMASK'] = context['deploy_network_netmask']
403 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000404 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${context['cluster_domain']}"
405
406 for (i in common.entries(smc_apt)) {
407 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
408 }
409
410 // create apt config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400411 if (outdateGeneration) {
412 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
413 } else {
414 args = [
Denis Egorenko032a2b72019-04-03 14:56:52 +0400415 "--ip ${context['aptly_server_deploy_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400416 "--user-data mirror_config", "--hostname ${aptlyServerHostname}.${context['cluster_domain']}", "--name ${aptlyServerHostname}", "--clean-up",
417 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400418 ]
419 sh "python ./create-config-drive.py ${args.join(' ')}"
420 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000421 sh("mv ${aptlyServerHostname}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000422
423 // save apt iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000424 archiveArtifacts artifacts: "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000425 }
426 }
427
428 stage('Save changes reclass model') {
Denis Egorenkod9865252019-04-24 15:41:57 +0400429 sh(returnStatus: true, script: "tar -czf ${context['cluster_name']}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
430 archiveArtifacts artifacts: "${context['cluster_name']}.tar.gz"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000431
azvyagintsev5400d1d2018-12-11 13:19:29 +0200432 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
azvyagintsev6d678da2018-11-28 21:19:06 +0200433 emailext(to: RequesterEmail,
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000434 attachmentsPattern: "output-${context['cluster_name']}/*",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000435 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and attached to this email.\nEnjoy!\n\nMirantis",
436 subject: "Your Salt model ${context['cluster_name']}")
437 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000438 dir("output-${context['cluster_name']}") {
439 deleteDir()
440 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000441 }
442
443 // Fail, but leave possibility to get failed artifacts
azvyagintsev6d678da2018-11-28 21:19:06 +0200444 if (!testResult && runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000445 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
446 error('Test stage finished: FAILURE')
447 }
448
449 } catch (Throwable e) {
450 currentBuild.result = "FAILURE"
451 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
452 throw e
453 } finally {
454 stage('Clean workspace directories') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000455 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000456 }
457 // common.sendNotification(currentBuild.result,"",["slack"])
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400458 stage('Save artifacts to Artifactory') {
459 def artifactory = new com.mirantis.mcp.MCPArtifactory()
azvyagintsev06dfe402019-03-22 17:58:53 +0200460 def buildProps = ["context=${context['cluster_name']}"]
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400461 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
462 buildProps.add("emailTo=${RequesterEmail}")
463 }
464 def artifactoryLink = artifactory.uploadJobArtifactsToArtifactory([
azvyagintsev06dfe402019-03-22 17:58:53 +0200465 'artifactory' : 'mcp-ci',
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400466 'artifactoryRepo': "drivetrain-local/${JOB_NAME}/${context['cluster_name']}-${BUILD_NUMBER}",
azvyagintsev06dfe402019-03-22 17:58:53 +0200467 'buildProps' : buildProps,
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400468 ])
469 currentBuild.description += "<br/>${artifactoryLink}"
470 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400471 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200472 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400473}