blob: 9ec74132f5fde896d4c7fb3447eacac6d7b7281b [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 ],
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000046 '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': {
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000049 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
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000054 '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 },
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000060 '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')) {
Denis Egorenkoeffa15d2019-06-03 17:45:36 +040086 context['cookiecutter_template_branch'] = gitGuessedVersion ?: "release/${context['mcp_version']}".toString()
azvyagintsev866b19a2018-11-20 18:21:43 +020087 }
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')) {
Denis Egorenkoeffa15d2019-06-03 17:45:36 +040093 context['shared_reclass_branch'] = gitGuessedVersion ?: "release/${context['mcp_version']}".toString()
azvyagintsev866b19a2018-11-20 18:21:43 +020094 }
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
Denis Egorenkoeffa15d2019-06-03 17:45:36 +0400101 context['mcp_common_scripts_branch'] = gitGuessedVersion ?: "release/${context['mcp_version']}".toString()
azvyagintsev866b19a2018-11-20 18:21:43 +0200102 }
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 Berezovskiy1e66f502019-05-24 18:11:17 +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'] = context.get('cloudinit_master_config', false) ?: [:]
136 context['cloudinit_master_config']['MCP_SALT_REPO_UPDATES'] = context['cloudinit_master_config'].get('MCP_SALT_REPO_UPDATES', false) ?:
137 'deb [arch=amd64] http://mirror.mirantis.com/update/proposed/salt-formulas/xenial xenial main'
138 }
139
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000140 common.infoMsg("Using context:\n" + context)
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000141 print prettyPrint(toJson(context))
azvyagintsev866b19a2018-11-20 18:21:43 +0200142 return context
143
144}
azvyagintsevf252b592018-08-13 18:39:14 +0300145
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000146timeout(time: 1, unit: 'HOURS') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000147 node(slaveNode) {
azvyagintsev866b19a2018-11-20 18:21:43 +0200148 def context = globalVariatorsUpdate()
azvyagintsev6d678da2018-11-28 21:19:06 +0200149 def RequesterEmail = context.get('email_address', '')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000150 def templateEnv = "${env.WORKSPACE}/template"
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200151 // modelEnv - this is reclass root, aka /srv/salt/reclass
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000152 def modelEnv = "${env.WORKSPACE}/model"
153 def testEnv = "${env.WORKSPACE}/test"
154 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +0200155
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000156 try {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000157 //
158 def cutterEnv = "${env.WORKSPACE}/cutter"
159 def systemEnv = "${modelEnv}/classes/system"
160 def testResult = false
161 def user
162 wrap([$class: 'BuildUser']) {
163 user = env.BUILD_USER_ID
164 }
azvyagintsev6d678da2018-11-28 21:19:06 +0200165 currentBuild.description = "${context['cluster_name']} ${RequesterEmail}"
azvyagintsev866b19a2018-11-20 18:21:43 +0200166
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000167 stage('Download Cookiecutter template') {
168 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
169 checkout([
170 $class : 'GitSCM',
171 branches : [[name: 'FETCH_HEAD'],],
172 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
173 userRemoteConfigs: [[url: context['cookiecutter_template_url'], refspec: context['cookiecutter_template_branch'], credentialsId: gerritCredentials],],
174 ])
175 }
176 stage('Create empty reclass model') {
177 dir(path: modelEnv) {
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200178 sh 'rm -rfv .git; git init'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000179 sshagent(credentials: [gerritCredentials]) {
180 sh "git submodule add ${context['shared_reclass_url']} 'classes/system'"
181 }
182 }
183 checkout([
184 $class : 'GitSCM',
185 branches : [[name: 'FETCH_HEAD'],],
186 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
187 userRemoteConfigs: [[url: context['shared_reclass_url'], refspec: context['shared_reclass_branch'], credentialsId: gerritCredentials],],
188 ])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200189 git.commitGitChanges(modelEnv, 'Added new shared reclass submodule', "${user}@localhost", "${user}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000190 }
191
192 stage('Generate model') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000193 // GNUPGHOME environment variable is required for all gpg commands
194 // and for python.generateModel execution
Denis Egorenkoce93af32019-05-20 16:57:33 +0400195 def envOpts = ["GNUPGHOME=${env.WORKSPACE}/gpghome"]
196 withEnv(envOpts) {
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300197 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000198 sh "mkdir gpghome; chmod 700 gpghome"
Dmitry Pyzhovb5c74c72018-12-17 22:08:50 +0300199 def secretKeyID = RequesterEmail ?: "salt@${context['cluster_domain']}".toString()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300200 if (!context.get('secrets_encryption_private_key')) {
201 def batchData = """
Denis Egorenko131de5f2019-05-16 14:25:40 +0400202 %echo Generating a basic OpenPGP key for Salt-Master
203 %no-protection
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300204 Key-Type: 1
205 Key-Length: 4096
206 Expire-Date: 0
207 Name-Real: ${context['salt_master_hostname']}.${context['cluster_domain']}
208 Name-Email: ${secretKeyID}
Denis Egorenko131de5f2019-05-16 14:25:40 +0400209 %commit
210 %echo done
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300211 """.stripIndent()
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200212 writeFile file: 'gpg-batch.txt', text: batchData
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300213 sh "gpg --gen-key --batch < gpg-batch.txt"
214 sh "gpg --export-secret-key -a ${secretKeyID} > gpgkey.asc"
215 } else {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200216 writeFile file: 'gpgkey.asc', text: context['secrets_encryption_private_key']
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300217 sh "gpg --import gpgkey.asc"
Denis Egorenko131de5f2019-05-16 14:25:40 +0400218 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 +0300219 }
220 context['secrets_encryption_key_id'] = secretKeyID
221 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400222 if (context.get('cfg_failsafe_ssh_public_key')) {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200223 writeFile file: 'failsafe-ssh-key.pub', text: context['cfg_failsafe_ssh_public_key']
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400224 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200225 if (!fileExists(new File(templateEnv, 'tox.ini').toString())) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200226 reqs = new File(templateEnv, 'requirements.txt').toString()
227 if (fileExists(reqs)) {
228 python.setupVirtualenv(cutterEnv, 'python2', [], reqs)
229 } else {
230 python.setupCookiecutterVirtualenv(cutterEnv)
231 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200232 python.generateModel(common2.dumpYAML(['default_context': context]), 'default_context', context['salt_master_hostname'], cutterEnv, modelEnv, templateEnv, false)
233 } else {
234 // tox-based CC generated structure of reclass,from the root. Otherwise for bw compat, modelEnv
235 // still expect only lower lvl of project, aka model/classes/cluster/XXX/. So,lets dump result into
236 // temp dir, and then copy it over initial structure.
237 reclassTempRootDir = sh(script: "mktemp -d -p ${env.WORKSPACE}", returnStdout: true).trim()
Denis Egorenkoce93af32019-05-20 16:57:33 +0400238 GenerateModelToxDocker(['context': common2.dumpYAML(['default_context': context]),
239 'ccRoot' : templateEnv,
240 'outDir' : reclassTempRootDir,
241 'envOpts': envOpts])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200242 dir(modelEnv) {
243 common.warningMsg('Forming reclass-root structure...')
244 sh("cp -ra ${reclassTempRootDir}/reclass/* .")
245 }
246 }
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300247 git.commitGitChanges(modelEnv, "Create model ${context['cluster_name']}", "${user}@localhost", "${user}")
248 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000249 }
250
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000251 stage("Test") {
azvyagintsev6d678da2018-11-28 21:19:06 +0200252 if (runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000253 sh("cp -r ${modelEnv} ${testEnv}")
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200254 if (fileExists('gpgkey.asc')) {
255 common.infoMsg('gpgkey.asc found!Copy it into reclass folder for tests..')
256 sh("cp -v gpgkey.asc ${testEnv}/salt_master_pillar.asc")
257 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000258 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
azvyagintsev5b7ec892019-05-15 16:37:34 +0300259 common.warningMsg("Attempt to run test against:\n" +
260 "DISTRIB_REVISION from ${distribRevision}\n" +
261 "updateSaltFormulasDuringTest = ${updateSaltFormulasDuringTest}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000262 try {
263 def config = [
Denis Egorenko926bc7d2019-02-19 14:27:34 +0400264 'dockerHostname' : "${context['salt_master_hostname']}",
265 'domain' : "${context['cluster_domain']}",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000266 'reclassEnv' : testEnv,
267 'distribRevision' : distribRevision,
268 'dockerContainerName': DockerCName,
Denis Egorenkod87490e2019-02-26 20:00:41 +0400269 'testContext' : 'salt-model-node',
azvyagintsev5b7ec892019-05-15 16:37:34 +0300270 'dockerExtraOpts' : ['--memory=3g'],
271 'updateSaltFormulas' : updateSaltFormulasDuringTest
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000272 ]
273 testResult = saltModelTesting.testNode(config)
274 common.infoMsg("Test finished: SUCCESS")
275 } catch (Exception ex) {
276 common.warningMsg("Test finished: FAILED")
277 testResult = false
278 }
279 } else {
280 common.warningMsg("Test stage has been skipped!")
281 }
282 }
283 stage("Generate config drives") {
284 // apt package genisoimage is required for this stage
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000285 // download create-config-drive
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000286 def commonScriptsRepoUrl = context['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
287 checkout([
288 $class : 'GitSCM',
289 branches : [[name: 'FETCH_HEAD'],],
290 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
azvyagintsev866b19a2018-11-20 18:21:43 +0200291 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: context['mcp_common_scripts_branch'], credentialsId: gerritCredentials],],
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000292 ])
293
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400294 def outdateGeneration = false
295 if (fileExists('mcp-common-scripts/config-drive/create_config_drive.py')) {
296 sh 'cp mcp-common-scripts/config-drive/create_config_drive.py create-config-drive.py'
297 } else {
298 outdateGeneration = true
299 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
300 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000301 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'
302
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000303 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
304 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400305 args = [
azvyagintsev1385db92019-03-22 16:05:10 +0200306 "--user-data user_data", "--model ${modelEnv}",
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400307 "--mk-pipelines ${pipelineEnv}/mk-pipelines/", "--pipeline-library ${pipelineEnv}/pipeline-library/"
308 ]
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300309 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400310 args.add('--gpg-key gpgkey.asc')
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300311 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400312 if (context.get('cfg_failsafe_ssh_public_key')) {
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400313 if (outdateGeneration) {
314 args.add('--ssh-key failsafe-ssh-key.pub')
315 } else {
Denis Egorenko85ddf0d2019-03-18 18:11:19 +0400316 if (context.get('cfg_failsafe_user')) {
317 args.add('--ssh-keys failsafe-ssh-key.pub')
318 args.add("--cloud-user-name ${context.get('cfg_failsafe_user')}")
319 }
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400320 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400321 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000322 // load data from model
323 def smc = [:]
324 smc['SALT_MASTER_MINION_ID'] = "${context['salt_master_hostname']}.${context['cluster_domain']}"
325 smc['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400326 if (context.get('cloudinit_master_config', false)) {
327 context['cloudinit_master_config'].each { k, v ->
328 smc[k] = v
329 }
330 }
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400331 if (outdateGeneration) {
332 smc['DEPLOY_NETWORK_GW'] = context['deploy_network_gateway']
333 smc['DEPLOY_NETWORK_NETMASK'] = context['deploy_network_netmask']
334 if (context.get('deploy_network_mtu')) {
335 smc['DEPLOY_NETWORK_MTU'] = context['deploy_network_mtu']
336 }
337 smc['DNS_SERVERS'] = context['dns_server01']
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000338 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000339 smc['MCP_VERSION'] = "${context['mcp_version']}"
340 if (context['local_repositories'] == 'True') {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400341 def localRepoIP = ''
Denis Egorenkof97aec22019-02-05 14:57:33 +0400342 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 +0400343 localRepoIP = context['local_repo_url']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400344 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
345 } else {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400346 localRepoIP = context['aptly_server_deploy_address']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400347 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}"
348 }
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400349 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000350 smc['PIPELINES_FROM_ISO'] = 'false'
351 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
352 smc['LOCAL_REPOS'] = 'true'
353 }
354 if (context['upstream_proxy_enabled'] == 'True') {
355 if (context['upstream_proxy_auth_enabled'] == 'True') {
356 smc['http_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
357 smc['https_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
358 } else {
359 smc['http_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
360 smc['https_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
361 }
362 }
363
364 for (i in common.entries(smc)) {
Ivan Berezovskiyd4818752019-05-24 19:14:17 +0400365 sh "sed -i 's,export ${i[0]}=.*,export ${i[0]}=\"${i[1]}\",' user_data"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000366 }
367
Denis Egorenko032a2b72019-04-03 14:56:52 +0400368 // calculate netmask
Denis Egorenko1f131b72019-04-05 14:42:48 +0400369 def deployNetworkSubnet = ''
370 if (context.get('deploy_network_subnet')) {
371 def subnet = new SubnetUtils(context['deploy_network_subnet'])
372 deployNetworkSubnet = subnet.getInfo().getNetmask()
373 } else if (context.get('deploy_network_netmask')) { // case for 2018.4.0
374 deployNetworkSubnet = context['deploy_network_netmask']
375 } else {
376 error('Neither context parameter deploy_network_subnet or deploy_network_netmask should be set!')
377 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000378 // create cfg config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400379 if (outdateGeneration) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200380 args += ["--hostname ${context['salt_master_hostname']}", "${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"]
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400381 sh "./create-config-drive ${args.join(' ')}"
382 } else {
383 args += [
384 "--name ${context['salt_master_hostname']}", "--hostname ${context['salt_master_hostname']}.${context['cluster_domain']}", "--clean-up",
Denis Egorenko032a2b72019-04-03 14:56:52 +0400385 "--ip ${context['salt_master_management_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400386 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400387 ]
azvyagintsevc4c047a2019-04-04 17:42:48 +0300388 sh "chmod 0755 create-config-drive.py ; ./create-config-drive.py ${args.join(' ')}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400389 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000390 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 +0000391
392 // save cfg iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000393 archiveArtifacts artifacts: "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000394
395 if (context['local_repositories'] == 'True') {
396 def aptlyServerHostname = context.aptly_server_hostname
397 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"
398
399 def smc_apt = [:]
400 smc_apt['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400401 if (outdateGeneration) {
402 smc_apt['APTLY_DEPLOY_IP'] = context['aptly_server_deploy_address']
403 smc_apt['APTLY_DEPLOY_NETMASK'] = context['deploy_network_netmask']
404 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000405 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${context['cluster_domain']}"
406
407 for (i in common.entries(smc_apt)) {
408 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
409 }
410
411 // create apt config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400412 if (outdateGeneration) {
413 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
414 } else {
415 args = [
Denis Egorenko032a2b72019-04-03 14:56:52 +0400416 "--ip ${context['aptly_server_deploy_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400417 "--user-data mirror_config", "--hostname ${aptlyServerHostname}.${context['cluster_domain']}", "--name ${aptlyServerHostname}", "--clean-up",
418 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400419 ]
420 sh "python ./create-config-drive.py ${args.join(' ')}"
421 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000422 sh("mv ${aptlyServerHostname}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000423
424 // save apt iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000425 archiveArtifacts artifacts: "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000426 }
427 }
428
429 stage('Save changes reclass model') {
Denis Egorenkod9865252019-04-24 15:41:57 +0400430 sh(returnStatus: true, script: "tar -czf ${context['cluster_name']}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
431 archiveArtifacts artifacts: "${context['cluster_name']}.tar.gz"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000432
azvyagintsev5400d1d2018-12-11 13:19:29 +0200433 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
azvyagintsev6d678da2018-11-28 21:19:06 +0200434 emailext(to: RequesterEmail,
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000435 attachmentsPattern: "output-${context['cluster_name']}/*",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000436 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and attached to this email.\nEnjoy!\n\nMirantis",
437 subject: "Your Salt model ${context['cluster_name']}")
438 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000439 dir("output-${context['cluster_name']}") {
440 deleteDir()
441 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000442 }
443
444 // Fail, but leave possibility to get failed artifacts
azvyagintsev6d678da2018-11-28 21:19:06 +0200445 if (!testResult && runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000446 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
447 error('Test stage finished: FAILURE')
448 }
449
450 } catch (Throwable e) {
451 currentBuild.result = "FAILURE"
452 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
453 throw e
454 } finally {
455 stage('Clean workspace directories') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000456 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000457 }
458 // common.sendNotification(currentBuild.result,"",["slack"])
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400459 stage('Save artifacts to Artifactory') {
460 def artifactory = new com.mirantis.mcp.MCPArtifactory()
azvyagintsev06dfe402019-03-22 17:58:53 +0200461 def buildProps = ["context=${context['cluster_name']}"]
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400462 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
463 buildProps.add("emailTo=${RequesterEmail}")
464 }
465 def artifactoryLink = artifactory.uploadJobArtifactsToArtifactory([
azvyagintsev06dfe402019-03-22 17:58:53 +0200466 'artifactory' : 'mcp-ci',
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400467 'artifactoryRepo': "drivetrain-local/${JOB_NAME}/${context['cluster_name']}-${BUILD_NUMBER}",
azvyagintsev06dfe402019-03-22 17:58:53 +0200468 'buildProps' : buildProps,
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400469 ])
470 currentBuild.description += "<br/>${artifactoryLink}"
471 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400472 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200473 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400474}