blob: 4eb511c33023548d99b30075067fd050a1a0e216 [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()
Hanna Arhipova5132ed42021-09-08 12:30:37 +030023saveToArtifactory = (env.getProperty('SAVE_TO_ARTIFACTORY') ?: true).toBoolean()
azvyagintsev866b19a2018-11-20 18:21:43 +020024distribRevision = 'proposed'
25gitGuessedVersion = false
Ivan Berezovskiyf590bc62020-02-28 17:29:17 +040026aptlyServerHostname = ''
azvyagintsev866b19a2018-11-20 18:21:43 +020027
Denis Egorenkoce93af32019-05-20 16:57:33 +040028def GenerateModelToxDocker(Map params) {
29 def ccRoot = params['ccRoot']
30 def context = params['context']
31 def outDir = params['outDir']
32 def envOpts = params['envOpts']
33 def tempContextFile = new File(ccRoot, 'tempContext.yaml_' + UUID.randomUUID().toString()).toString()
34 writeFile file: tempContextFile, text: context
35 // Get Jenkins user UID and GID
36 def jenkinsUID = sh(script: 'id -u', returnStdout: true).trim()
37 def jenkinsGID = sh(script: 'id -g', returnStdout: true).trim()
38 /*
39 by default, process in image operates via root user
40 Otherwise, gpg key for model and all files managed by jenkins user
41 To make it compatible, install rrequirementfrom user, but generate model via jenkins
42 for build use upstream Ubuntu Bionic image
43 */
44 def configRun = ['distribRevision': 'nightly',
45 'envOpts' : envOpts + ["CONFIG_FILE=$tempContextFile",
46 "OUTPUT_DIR=${outDir}"
47 ],
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000048 'image': 'docker-prod-local.artifactory.mirantis.com/mirantis/cicd/jnlp-slave',
Denis Egorenkoce93af32019-05-20 16:57:33 +040049 'runCommands' : [
50 '001_prepare_generate_auto_reqs': {
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000051 sh('''
Denis Egorenkoce93af32019-05-20 16:57:33 +040052 pip install tox
53 ''')
54 },
55 // user & group can be different on host and in docker
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000056 '002_set_jenkins_id': {
57 sh("""
Denis Egorenkoce93af32019-05-20 16:57:33 +040058 usermod -u ${jenkinsUID} jenkins
Ivan Berezovskiy6c6118a2019-12-10 17:21:46 +040059 groupmod -g ${jenkinsGID} jenkins
Denis Egorenkoce93af32019-05-20 16:57:33 +040060 """)
61 },
Aleksey Zvyagintsev1cbd4d72019-05-24 14:08:19 +000062 '003_run_generate_auto': {
Denis Egorenkoce93af32019-05-20 16:57:33 +040063 print('[Cookiecutter build] Result:\n' +
64 sh(returnStdout: true, script: 'cd ' + ccRoot + '; su jenkins -c "tox -ve generate_auto" '))
65 }
66 ]
67 ]
68
69 saltModelTesting.setupDockerAndTest(configRun)
70}
71
azvyagintsev866b19a2018-11-20 18:21:43 +020072def globalVariatorsUpdate() {
73 def templateContext = readYaml text: env.COOKIECUTTER_TEMPLATE_CONTEXT
74 def context = templateContext['default_context']
75 // TODO add more check's for critical var's
76 // Since we can't pin to any '_branch' variable from context, to identify 'default git revision' -
77 // because each of them, might be 'refs/' variable, we need to add some tricky trigger of using
78 // 'release/XXX' logic. This is totall guess - so,if even those one failed, to definitely must pass
79 // correct variable finally!
Denis Egorenkof0d213d2019-06-03 19:05:40 +040080 [ context.get('cookiecutter_template_branch'), context.get('shared_reclass_branch'), context.get('mcp_common_scripts_branch') ].any { branch ->
azvyagintsev866b19a2018-11-20 18:21:43 +020081 if (branch.toString().startsWith('release/')) {
82 gitGuessedVersion = branch
83 return true
84 }
85 }
Denis Egorenkof0d213d2019-06-03 19:05:40 +040086
87 [ 'cookiecutter_template_branch', 'shared_reclass_branch', 'mcp_common_scripts_branch' ].each { repoName ->
Denis Egorenko0b283902019-06-04 13:09:15 +040088 if (context['mcp_version'] in [ "nightly", "testing", "stable" ] && ! context.get(repoName)) {
Denis Egorenkof0d213d2019-06-03 19:05:40 +040089 context[repoName] = 'master'
90 } else if (! context.get(repoName)) {
91 context[repoName] = gitGuessedVersion ?: "release/${context['mcp_version']}".toString()
92 }
azvyagintsev866b19a2018-11-20 18:21:43 +020093 }
94 //
95 distribRevision = context['mcp_version']
96 if (['master'].contains(context['mcp_version'])) {
97 distribRevision = 'nightly'
98 }
99 if (distribRevision.contains('/')) {
100 distribRevision = distribRevision.split('/')[-1]
101 }
102 // Check if we are going to test bleeding-edge release, which doesn't have binary release yet
azvyagintsev8e081642019-02-03 19:15:22 +0200103 // After 2018q4 releases, need to also check 'static' repo, for example ubuntu.
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200104 def binTest = common.checkRemoteBinary(['mcp_version': distribRevision])
azvyagintsev8e081642019-02-03 19:15:22 +0200105 if (!binTest.linux_system_repo_url || !binTest.linux_system_repo_ubuntu_url) {
106 common.errorMsg("Binary release: ${distribRevision} not exist or not full. Fallback to 'proposed'! ")
azvyagintsev866b19a2018-11-20 18:21:43 +0200107 distribRevision = 'proposed'
108 }
azvyagintsev8e081642019-02-03 19:15:22 +0200109
azvyagintsev5400d1d2018-12-11 13:19:29 +0200110 // (azvyagintsev) WA for PROD-25732
111 if (context.cookiecutter_template_url.contains('gerrit.mcp.mirantis.com/mk/cookiecutter-templates')) {
112 common.warningMsg('Apply WA for PROD-25732')
113 context.cookiecutter_template_url = 'ssh://gerrit.mcp.mirantis.com:29418/mk/cookiecutter-templates.git'
114 }
Ivan Berezovskiy6c6118a2019-12-10 17:21:46 +0400115 // check, if we are going to test clear release version, w\o any updates and patches.
116 // All 2019.2.X starting from 2019.2.1 and above have update repo which has to be used for salt-formulas.
117 // update/2019.2.0 repo can't be used b/c it points to latest 2019.2.X
118 if (!gitGuessedVersion && (distribRevision == context.mcp_version) && !(distribRevision ==~ /2019\.2\.[1-9]\d*/)) {
azvyagintsev5b7ec892019-05-15 16:37:34 +0300119 updateSaltFormulasDuringTest = false
120 }
121
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400122 if (gitGuessedVersion == 'release/proposed/2019.2.0') {
Denis Egorenko83dfe212019-09-11 16:19:01 +0400123 def mcpSaltRepoUpdateVar = 'deb [arch=amd64] http://mirror.mirantis.com/update/proposed/salt-formulas/xenial xenial main'
124 if (context.get('offline_deployment', 'False').toBoolean()) {
Denis Egorenkoa43c66e2019-09-11 16:40:56 +0400125 mcpSaltRepoUpdateVar = "deb [arch=amd64] http://${context.get('aptly_server_deploy_address')}/update/proposed/salt-formulas/xenial xenial main".toString()
Denis Egorenko83dfe212019-09-11 16:19:01 +0400126 }
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400127 // CFG node in 2019.2.X update has to be bootstrapped with update/proposed repository for salt formulas
128 context['cloudinit_master_config'] = context.get('cloudinit_master_config', false) ?: [:]
Denis Egorenko83dfe212019-09-11 16:19:01 +0400129 context['cloudinit_master_config']['MCP_SALT_REPO_UPDATES'] = context['cloudinit_master_config'].get('MCP_SALT_REPO_UPDATES', false) ?: mcpSaltRepoUpdateVar
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400130 }
131
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000132 common.infoMsg("Using context:\n" + context)
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000133 print prettyPrint(toJson(context))
azvyagintsev866b19a2018-11-20 18:21:43 +0200134 return context
135
136}
azvyagintsevf252b592018-08-13 18:39:14 +0300137
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000138timeout(time: 1, unit: 'HOURS') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000139 node(slaveNode) {
azvyagintsev866b19a2018-11-20 18:21:43 +0200140 def context = globalVariatorsUpdate()
azvyagintsev6d678da2018-11-28 21:19:06 +0200141 def RequesterEmail = context.get('email_address', '')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000142 def templateEnv = "${env.WORKSPACE}/template"
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200143 // modelEnv - this is reclass root, aka /srv/salt/reclass
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000144 def modelEnv = "${env.WORKSPACE}/model"
145 def testEnv = "${env.WORKSPACE}/test"
146 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +0200147
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000148 try {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000149 //
150 def cutterEnv = "${env.WORKSPACE}/cutter"
151 def systemEnv = "${modelEnv}/classes/system"
152 def testResult = false
153 def user
154 wrap([$class: 'BuildUser']) {
155 user = env.BUILD_USER_ID
156 }
azvyagintsev6d678da2018-11-28 21:19:06 +0200157 currentBuild.description = "${context['cluster_name']} ${RequesterEmail}"
azvyagintsev866b19a2018-11-20 18:21:43 +0200158
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000159 stage('Download Cookiecutter template') {
160 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
161 checkout([
162 $class : 'GitSCM',
163 branches : [[name: 'FETCH_HEAD'],],
164 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
165 userRemoteConfigs: [[url: context['cookiecutter_template_url'], refspec: context['cookiecutter_template_branch'], credentialsId: gerritCredentials],],
166 ])
167 }
168 stage('Create empty reclass model') {
169 dir(path: modelEnv) {
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200170 sh 'rm -rfv .git; git init'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000171 sshagent(credentials: [gerritCredentials]) {
172 sh "git submodule add ${context['shared_reclass_url']} 'classes/system'"
173 }
174 }
175 checkout([
176 $class : 'GitSCM',
177 branches : [[name: 'FETCH_HEAD'],],
178 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
179 userRemoteConfigs: [[url: context['shared_reclass_url'], refspec: context['shared_reclass_branch'], credentialsId: gerritCredentials],],
180 ])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200181 git.commitGitChanges(modelEnv, 'Added new shared reclass submodule', "${user}@localhost", "${user}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000182 }
183
184 stage('Generate model') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000185 // GNUPGHOME environment variable is required for all gpg commands
186 // and for python.generateModel execution
Denis Egorenkoce93af32019-05-20 16:57:33 +0400187 def envOpts = ["GNUPGHOME=${env.WORKSPACE}/gpghome"]
188 withEnv(envOpts) {
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300189 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000190 sh "mkdir gpghome; chmod 700 gpghome"
Dmitry Pyzhovb5c74c72018-12-17 22:08:50 +0300191 def secretKeyID = RequesterEmail ?: "salt@${context['cluster_domain']}".toString()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300192 if (!context.get('secrets_encryption_private_key')) {
193 def batchData = """
Denis Egorenko131de5f2019-05-16 14:25:40 +0400194 %echo Generating a basic OpenPGP key for Salt-Master
195 %no-protection
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300196 Key-Type: 1
197 Key-Length: 4096
198 Expire-Date: 0
199 Name-Real: ${context['salt_master_hostname']}.${context['cluster_domain']}
200 Name-Email: ${secretKeyID}
Denis Egorenko131de5f2019-05-16 14:25:40 +0400201 %commit
202 %echo done
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300203 """.stripIndent()
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200204 writeFile file: 'gpg-batch.txt', text: batchData
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300205 sh "gpg --gen-key --batch < gpg-batch.txt"
206 sh "gpg --export-secret-key -a ${secretKeyID} > gpgkey.asc"
207 } else {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200208 writeFile file: 'gpgkey.asc', text: context['secrets_encryption_private_key']
Vladimir Khlyunev90072962021-01-25 19:19:48 +0400209 sh "gpg --batch --import gpgkey.asc"
Denis Egorenko131de5f2019-05-16 14:25:40 +0400210 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 +0300211 }
212 context['secrets_encryption_key_id'] = secretKeyID
213 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400214 if (context.get('cfg_failsafe_ssh_public_key')) {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200215 writeFile file: 'failsafe-ssh-key.pub', text: context['cfg_failsafe_ssh_public_key']
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400216 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200217 if (!fileExists(new File(templateEnv, 'tox.ini').toString())) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200218 reqs = new File(templateEnv, 'requirements.txt').toString()
219 if (fileExists(reqs)) {
220 python.setupVirtualenv(cutterEnv, 'python2', [], reqs)
221 } else {
222 python.setupCookiecutterVirtualenv(cutterEnv)
223 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200224 python.generateModel(common2.dumpYAML(['default_context': context]), 'default_context', context['salt_master_hostname'], cutterEnv, modelEnv, templateEnv, false)
225 } else {
226 // tox-based CC generated structure of reclass,from the root. Otherwise for bw compat, modelEnv
227 // still expect only lower lvl of project, aka model/classes/cluster/XXX/. So,lets dump result into
228 // temp dir, and then copy it over initial structure.
229 reclassTempRootDir = sh(script: "mktemp -d -p ${env.WORKSPACE}", returnStdout: true).trim()
Denis Egorenkoce93af32019-05-20 16:57:33 +0400230 GenerateModelToxDocker(['context': common2.dumpYAML(['default_context': context]),
231 'ccRoot' : templateEnv,
232 'outDir' : reclassTempRootDir,
233 'envOpts': envOpts])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200234 dir(modelEnv) {
235 common.warningMsg('Forming reclass-root structure...')
236 sh("cp -ra ${reclassTempRootDir}/reclass/* .")
237 }
238 }
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300239 git.commitGitChanges(modelEnv, "Create model ${context['cluster_name']}", "${user}@localhost", "${user}")
240 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000241 }
242
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000243 stage("Test") {
azvyagintsev6d678da2018-11-28 21:19:06 +0200244 if (runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000245 sh("cp -r ${modelEnv} ${testEnv}")
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200246 if (fileExists('gpgkey.asc')) {
247 common.infoMsg('gpgkey.asc found!Copy it into reclass folder for tests..')
248 sh("cp -v gpgkey.asc ${testEnv}/salt_master_pillar.asc")
249 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000250 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
azvyagintsev5b7ec892019-05-15 16:37:34 +0300251 common.warningMsg("Attempt to run test against:\n" +
252 "DISTRIB_REVISION from ${distribRevision}\n" +
253 "updateSaltFormulasDuringTest = ${updateSaltFormulasDuringTest}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000254 try {
255 def config = [
Denis Egorenko926bc7d2019-02-19 14:27:34 +0400256 'dockerHostname' : "${context['salt_master_hostname']}",
257 'domain' : "${context['cluster_domain']}",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000258 'reclassEnv' : testEnv,
259 'distribRevision' : distribRevision,
260 'dockerContainerName': DockerCName,
Denis Egorenkod87490e2019-02-26 20:00:41 +0400261 'testContext' : 'salt-model-node',
azvyagintsev5b7ec892019-05-15 16:37:34 +0300262 'dockerExtraOpts' : ['--memory=3g'],
263 'updateSaltFormulas' : updateSaltFormulasDuringTest
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000264 ]
Denis Egorenko19a793d2019-11-18 20:50:59 +0400265 if (gitGuessedVersion == 'release/proposed/2019.2.0') {
266 config['updateSaltFormulasRev'] = 'proposed'
267 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000268 testResult = saltModelTesting.testNode(config)
269 common.infoMsg("Test finished: SUCCESS")
270 } catch (Exception ex) {
271 common.warningMsg("Test finished: FAILED")
272 testResult = false
273 }
274 } else {
275 common.warningMsg("Test stage has been skipped!")
276 }
277 }
278 stage("Generate config drives") {
279 // apt package genisoimage is required for this stage
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000280 // download create-config-drive
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000281 def commonScriptsRepoUrl = context['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
282 checkout([
283 $class : 'GitSCM',
284 branches : [[name: 'FETCH_HEAD'],],
285 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
azvyagintsev866b19a2018-11-20 18:21:43 +0200286 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: context['mcp_common_scripts_branch'], credentialsId: gerritCredentials],],
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000287 ])
288
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400289 def outdateGeneration = false
290 if (fileExists('mcp-common-scripts/config-drive/create_config_drive.py')) {
291 sh 'cp mcp-common-scripts/config-drive/create_config_drive.py create-config-drive.py'
292 } else {
293 outdateGeneration = true
294 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
295 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000296 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'
297
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000298 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
299 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400300 args = [
azvyagintsev1385db92019-03-22 16:05:10 +0200301 "--user-data user_data", "--model ${modelEnv}",
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400302 "--mk-pipelines ${pipelineEnv}/mk-pipelines/", "--pipeline-library ${pipelineEnv}/pipeline-library/"
303 ]
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300304 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400305 args.add('--gpg-key gpgkey.asc')
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300306 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400307 if (context.get('cfg_failsafe_ssh_public_key')) {
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400308 if (outdateGeneration) {
309 args.add('--ssh-key failsafe-ssh-key.pub')
310 } else {
Denis Egorenko85ddf0d2019-03-18 18:11:19 +0400311 if (context.get('cfg_failsafe_user')) {
312 args.add('--ssh-keys failsafe-ssh-key.pub')
313 args.add("--cloud-user-name ${context.get('cfg_failsafe_user')}")
314 }
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400315 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400316 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000317 // load data from model
318 def smc = [:]
319 smc['SALT_MASTER_MINION_ID'] = "${context['salt_master_hostname']}.${context['cluster_domain']}"
320 smc['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400321 if (context.get('cloudinit_master_config', false)) {
322 context['cloudinit_master_config'].each { k, v ->
323 smc[k] = v
324 }
325 }
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400326 if (outdateGeneration) {
327 smc['DEPLOY_NETWORK_GW'] = context['deploy_network_gateway']
328 smc['DEPLOY_NETWORK_NETMASK'] = context['deploy_network_netmask']
329 if (context.get('deploy_network_mtu')) {
330 smc['DEPLOY_NETWORK_MTU'] = context['deploy_network_mtu']
331 }
332 smc['DNS_SERVERS'] = context['dns_server01']
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000333 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000334 smc['MCP_VERSION'] = "${context['mcp_version']}"
335 if (context['local_repositories'] == 'True') {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400336 def localRepoIP = ''
Denis Egorenkof97aec22019-02-05 14:57:33 +0400337 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 +0400338 localRepoIP = context['local_repo_url']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400339 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
340 } else {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400341 localRepoIP = context['aptly_server_deploy_address']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400342 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}"
343 }
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400344 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
Denis Egorenko5f1c0d92019-09-26 09:03:57 +0000345 smc['PIPELINES_FROM_ISO'] = 'false'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000346 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
347 smc['LOCAL_REPOS'] = 'true'
348 }
349 if (context['upstream_proxy_enabled'] == 'True') {
350 if (context['upstream_proxy_auth_enabled'] == 'True') {
351 smc['http_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
352 smc['https_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
353 } else {
354 smc['http_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
355 smc['https_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
356 }
357 }
358
359 for (i in common.entries(smc)) {
Denis Egorenko815d5022019-07-15 16:01:04 +0400360 sh "sed -i 's,export ${i[0]}=.*,export ${i[0]}=\${${i[0]}:-\"${i[1]}\"},' user_data"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000361 }
362
Denis Egorenko032a2b72019-04-03 14:56:52 +0400363 // calculate netmask
Denis Egorenko1f131b72019-04-05 14:42:48 +0400364 def deployNetworkSubnet = ''
365 if (context.get('deploy_network_subnet')) {
366 def subnet = new SubnetUtils(context['deploy_network_subnet'])
367 deployNetworkSubnet = subnet.getInfo().getNetmask()
368 } else if (context.get('deploy_network_netmask')) { // case for 2018.4.0
369 deployNetworkSubnet = context['deploy_network_netmask']
370 } else {
371 error('Neither context parameter deploy_network_subnet or deploy_network_netmask should be set!')
372 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000373 // create cfg config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400374 if (outdateGeneration) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200375 args += ["--hostname ${context['salt_master_hostname']}", "${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"]
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400376 sh "./create-config-drive ${args.join(' ')}"
377 } else {
378 args += [
379 "--name ${context['salt_master_hostname']}", "--hostname ${context['salt_master_hostname']}.${context['cluster_domain']}", "--clean-up",
Denis Egorenko032a2b72019-04-03 14:56:52 +0400380 "--ip ${context['salt_master_management_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400381 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400382 ]
azvyagintsevc4c047a2019-04-04 17:42:48 +0300383 sh "chmod 0755 create-config-drive.py ; ./create-config-drive.py ${args.join(' ')}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400384 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000385 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 +0000386
387 // save cfg iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000388 archiveArtifacts artifacts: "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000389
390 if (context['local_repositories'] == 'True') {
Ivan Berezovskiyf590bc62020-02-28 17:29:17 +0400391 aptlyServerHostname = context.aptly_server_hostname
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000392 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"
393
394 def smc_apt = [:]
395 smc_apt['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400396 if (outdateGeneration) {
397 smc_apt['APTLY_DEPLOY_IP'] = context['aptly_server_deploy_address']
398 smc_apt['APTLY_DEPLOY_NETMASK'] = context['deploy_network_netmask']
399 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000400 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${context['cluster_domain']}"
401
402 for (i in common.entries(smc_apt)) {
403 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
404 }
405
406 // create apt config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400407 if (outdateGeneration) {
408 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
409 } else {
410 args = [
Denis Egorenko032a2b72019-04-03 14:56:52 +0400411 "--ip ${context['aptly_server_deploy_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400412 "--user-data mirror_config", "--hostname ${aptlyServerHostname}.${context['cluster_domain']}", "--name ${aptlyServerHostname}", "--clean-up",
413 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400414 ]
415 sh "python ./create-config-drive.py ${args.join(' ')}"
416 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000417 sh("mv ${aptlyServerHostname}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000418
419 // save apt iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000420 archiveArtifacts artifacts: "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000421 }
422 }
423
424 stage('Save changes reclass model') {
Denis Egorenkod9865252019-04-24 15:41:57 +0400425 sh(returnStatus: true, script: "tar -czf ${context['cluster_name']}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
426 archiveArtifacts artifacts: "${context['cluster_name']}.tar.gz"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000427
azvyagintsev5400d1d2018-12-11 13:19:29 +0200428 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
Denis Egorenkof9729842019-07-08 15:53:01 +0400429 def mailSubject = "Your Salt model ${context['cluster_name']}"
430 if (context.get('send_method') == 'gcs') {
431 def gcs = new com.mirantis.mk.GoogleCloudStorage()
432 def uploadIsos = [ "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso" ]
433 if (context['local_repositories'] == 'True') {
434 uploadIsos << "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
435 }
436 // generate random hash to have uniq and unpredictable link to file
437 def randHash = common.generateRandomHashString(64)
438 def config = [
439 'creds': context['gcs_creds'],
440 'project': context['gcs_project'],
441 'dest': "gs://${context['gcs_bucket']}/${randHash}",
442 'sources': uploadIsos
443 ]
444 def fileURLs = gcs.uploadArtifactToGoogleStorageBucket(config).join(' ').replace('gs://', 'https://storage.googleapis.com/')
445 emailext(to: RequesterEmail,
446 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and available to download via next URL: ${fileURLs} within 7 days.\nEnjoy!\n\nMirantis",
447 subject: mailSubject)
448 } else {
449 emailext(to: RequesterEmail,
450 attachmentsPattern: "output-${context['cluster_name']}/*",
451 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and attached to this email.\nEnjoy!\n\nMirantis",
452 subject: mailSubject)
453 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000454 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000455 dir("output-${context['cluster_name']}") {
456 deleteDir()
457 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000458 }
459
460 // Fail, but leave possibility to get failed artifacts
azvyagintsev6d678da2018-11-28 21:19:06 +0200461 if (!testResult && runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000462 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
463 error('Test stage finished: FAILURE')
464 }
465
466 } catch (Throwable e) {
467 currentBuild.result = "FAILURE"
468 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
469 throw e
470 } finally {
471 stage('Clean workspace directories') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000472 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000473 }
474 // common.sendNotification(currentBuild.result,"",["slack"])
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400475 stage('Save artifacts to Artifactory') {
Hanna Arhipova5132ed42021-09-08 12:30:37 +0300476 if (saveToArtifactory) {
477 def artifactory = new com.mirantis.mcp.MCPArtifactory()
478 def buildProps = ["context=${context['cluster_name']}"]
479 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
480 buildProps.add("emailTo=${RequesterEmail}")
481 }
482 def artifactoryLink = artifactory.uploadJobArtifactsToArtifactory([
483 'artifactory' : 'mcp-ci',
484 'artifactoryRepo': "artifactory/drivetrain-local/${JOB_NAME}/${context['cluster_name']}-${BUILD_NUMBER}",
485 'buildProps' : buildProps,
486 ])
487 currentBuild.description += "<br/>${artifactoryLink}"
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400488 }
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400489 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400490 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200491 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400492}