blob: 37833312c836081f58b1690c9fc59df033fa91dc [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 ],
46 'image': 'docker-prod-local.artifactory.mirantis.com/mirantis/cicd/jnlp-slave',
47 'runCommands' : [
48 '001_prepare_generate_auto_reqs': {
49 sh('''
50 pip install tox
51 ''')
52 },
53 // user & group can be different on host and in docker
54 '002_set_jenkins_id': {
55 sh("""
56 usermod -u ${jenkinsUID} jenkins
57 groupmod -g ${jenkinsUID} jenkins
58 """)
59 },
60 '003_run_generate_auto': {
61 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
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000133 common.infoMsg("Using context:\n" + context)
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000134 print prettyPrint(toJson(context))
azvyagintsev866b19a2018-11-20 18:21:43 +0200135 return context
136
137}
azvyagintsevf252b592018-08-13 18:39:14 +0300138
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000139timeout(time: 1, unit: 'HOURS') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000140 node(slaveNode) {
azvyagintsev866b19a2018-11-20 18:21:43 +0200141 def context = globalVariatorsUpdate()
azvyagintsev6d678da2018-11-28 21:19:06 +0200142 def RequesterEmail = context.get('email_address', '')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000143 def templateEnv = "${env.WORKSPACE}/template"
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200144 // modelEnv - this is reclass root, aka /srv/salt/reclass
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000145 def modelEnv = "${env.WORKSPACE}/model"
146 def testEnv = "${env.WORKSPACE}/test"
147 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +0200148
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000149 try {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000150 //
151 def cutterEnv = "${env.WORKSPACE}/cutter"
152 def systemEnv = "${modelEnv}/classes/system"
153 def testResult = false
154 def user
155 wrap([$class: 'BuildUser']) {
156 user = env.BUILD_USER_ID
157 }
azvyagintsev6d678da2018-11-28 21:19:06 +0200158 currentBuild.description = "${context['cluster_name']} ${RequesterEmail}"
azvyagintsev866b19a2018-11-20 18:21:43 +0200159
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000160 stage('Download Cookiecutter template') {
161 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
162 checkout([
163 $class : 'GitSCM',
164 branches : [[name: 'FETCH_HEAD'],],
165 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
166 userRemoteConfigs: [[url: context['cookiecutter_template_url'], refspec: context['cookiecutter_template_branch'], credentialsId: gerritCredentials],],
167 ])
168 }
169 stage('Create empty reclass model') {
170 dir(path: modelEnv) {
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200171 sh 'rm -rfv .git; git init'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000172 sshagent(credentials: [gerritCredentials]) {
173 sh "git submodule add ${context['shared_reclass_url']} 'classes/system'"
174 }
175 }
176 checkout([
177 $class : 'GitSCM',
178 branches : [[name: 'FETCH_HEAD'],],
179 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
180 userRemoteConfigs: [[url: context['shared_reclass_url'], refspec: context['shared_reclass_branch'], credentialsId: gerritCredentials],],
181 ])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200182 git.commitGitChanges(modelEnv, 'Added new shared reclass submodule', "${user}@localhost", "${user}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000183 }
184
185 stage('Generate model') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000186 // GNUPGHOME environment variable is required for all gpg commands
187 // and for python.generateModel execution
Denis Egorenkoce93af32019-05-20 16:57:33 +0400188 def envOpts = ["GNUPGHOME=${env.WORKSPACE}/gpghome"]
189 withEnv(envOpts) {
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300190 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000191 sh "mkdir gpghome; chmod 700 gpghome"
Dmitry Pyzhovb5c74c72018-12-17 22:08:50 +0300192 def secretKeyID = RequesterEmail ?: "salt@${context['cluster_domain']}".toString()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300193 if (!context.get('secrets_encryption_private_key')) {
194 def batchData = """
Denis Egorenko131de5f2019-05-16 14:25:40 +0400195 %echo Generating a basic OpenPGP key for Salt-Master
196 %no-protection
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300197 Key-Type: 1
198 Key-Length: 4096
199 Expire-Date: 0
200 Name-Real: ${context['salt_master_hostname']}.${context['cluster_domain']}
201 Name-Email: ${secretKeyID}
Denis Egorenko131de5f2019-05-16 14:25:40 +0400202 %commit
203 %echo done
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300204 """.stripIndent()
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200205 writeFile file: 'gpg-batch.txt', text: batchData
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300206 sh "gpg --gen-key --batch < gpg-batch.txt"
207 sh "gpg --export-secret-key -a ${secretKeyID} > gpgkey.asc"
208 } else {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200209 writeFile file: 'gpgkey.asc', text: context['secrets_encryption_private_key']
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300210 sh "gpg --import gpgkey.asc"
Denis Egorenko131de5f2019-05-16 14:25:40 +0400211 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 +0300212 }
213 context['secrets_encryption_key_id'] = secretKeyID
214 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400215 if (context.get('cfg_failsafe_ssh_public_key')) {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200216 writeFile file: 'failsafe-ssh-key.pub', text: context['cfg_failsafe_ssh_public_key']
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400217 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200218 if (!fileExists(new File(templateEnv, 'tox.ini').toString())) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200219 reqs = new File(templateEnv, 'requirements.txt').toString()
220 if (fileExists(reqs)) {
221 python.setupVirtualenv(cutterEnv, 'python2', [], reqs)
222 } else {
223 python.setupCookiecutterVirtualenv(cutterEnv)
224 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200225 python.generateModel(common2.dumpYAML(['default_context': context]), 'default_context', context['salt_master_hostname'], cutterEnv, modelEnv, templateEnv, false)
226 } else {
227 // tox-based CC generated structure of reclass,from the root. Otherwise for bw compat, modelEnv
228 // still expect only lower lvl of project, aka model/classes/cluster/XXX/. So,lets dump result into
229 // temp dir, and then copy it over initial structure.
230 reclassTempRootDir = sh(script: "mktemp -d -p ${env.WORKSPACE}", returnStdout: true).trim()
Denis Egorenkoce93af32019-05-20 16:57:33 +0400231 GenerateModelToxDocker(['context': common2.dumpYAML(['default_context': context]),
232 'ccRoot' : templateEnv,
233 'outDir' : reclassTempRootDir,
234 'envOpts': envOpts])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200235 dir(modelEnv) {
236 common.warningMsg('Forming reclass-root structure...')
237 sh("cp -ra ${reclassTempRootDir}/reclass/* .")
238 }
239 }
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300240 git.commitGitChanges(modelEnv, "Create model ${context['cluster_name']}", "${user}@localhost", "${user}")
241 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000242 }
243
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000244 stage("Test") {
azvyagintsev6d678da2018-11-28 21:19:06 +0200245 if (runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000246 sh("cp -r ${modelEnv} ${testEnv}")
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200247 if (fileExists('gpgkey.asc')) {
248 common.infoMsg('gpgkey.asc found!Copy it into reclass folder for tests..')
249 sh("cp -v gpgkey.asc ${testEnv}/salt_master_pillar.asc")
250 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000251 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
azvyagintsev5b7ec892019-05-15 16:37:34 +0300252 common.warningMsg("Attempt to run test against:\n" +
253 "DISTRIB_REVISION from ${distribRevision}\n" +
254 "updateSaltFormulasDuringTest = ${updateSaltFormulasDuringTest}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000255 try {
256 def config = [
Denis Egorenko926bc7d2019-02-19 14:27:34 +0400257 'dockerHostname' : "${context['salt_master_hostname']}",
258 'domain' : "${context['cluster_domain']}",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000259 'reclassEnv' : testEnv,
260 'distribRevision' : distribRevision,
261 'dockerContainerName': DockerCName,
Denis Egorenkod87490e2019-02-26 20:00:41 +0400262 'testContext' : 'salt-model-node',
azvyagintsev5b7ec892019-05-15 16:37:34 +0300263 'dockerExtraOpts' : ['--memory=3g'],
264 'updateSaltFormulas' : updateSaltFormulasDuringTest
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000265 ]
266 testResult = saltModelTesting.testNode(config)
267 common.infoMsg("Test finished: SUCCESS")
268 } catch (Exception ex) {
269 common.warningMsg("Test finished: FAILED")
270 testResult = false
271 }
272 } else {
273 common.warningMsg("Test stage has been skipped!")
274 }
275 }
276 stage("Generate config drives") {
277 // apt package genisoimage is required for this stage
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000278 // download create-config-drive
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000279 def commonScriptsRepoUrl = context['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
280 checkout([
281 $class : 'GitSCM',
282 branches : [[name: 'FETCH_HEAD'],],
283 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
azvyagintsev866b19a2018-11-20 18:21:43 +0200284 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: context['mcp_common_scripts_branch'], credentialsId: gerritCredentials],],
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000285 ])
286
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400287 def outdateGeneration = false
288 if (fileExists('mcp-common-scripts/config-drive/create_config_drive.py')) {
289 sh 'cp mcp-common-scripts/config-drive/create_config_drive.py create-config-drive.py'
290 } else {
291 outdateGeneration = true
292 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
293 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000294 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'
295
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000296 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
297 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400298 args = [
azvyagintsev1385db92019-03-22 16:05:10 +0200299 "--user-data user_data", "--model ${modelEnv}",
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400300 "--mk-pipelines ${pipelineEnv}/mk-pipelines/", "--pipeline-library ${pipelineEnv}/pipeline-library/"
301 ]
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300302 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400303 args.add('--gpg-key gpgkey.asc')
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300304 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400305 if (context.get('cfg_failsafe_ssh_public_key')) {
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400306 if (outdateGeneration) {
307 args.add('--ssh-key failsafe-ssh-key.pub')
308 } else {
Denis Egorenko85ddf0d2019-03-18 18:11:19 +0400309 if (context.get('cfg_failsafe_user')) {
310 args.add('--ssh-keys failsafe-ssh-key.pub')
311 args.add("--cloud-user-name ${context.get('cfg_failsafe_user')}")
312 }
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400313 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400314 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000315 // load data from model
316 def smc = [:]
317 smc['SALT_MASTER_MINION_ID'] = "${context['salt_master_hostname']}.${context['cluster_domain']}"
318 smc['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400319 if (outdateGeneration) {
320 smc['DEPLOY_NETWORK_GW'] = context['deploy_network_gateway']
321 smc['DEPLOY_NETWORK_NETMASK'] = context['deploy_network_netmask']
322 if (context.get('deploy_network_mtu')) {
323 smc['DEPLOY_NETWORK_MTU'] = context['deploy_network_mtu']
324 }
325 smc['DNS_SERVERS'] = context['dns_server01']
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000326 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000327 smc['MCP_VERSION'] = "${context['mcp_version']}"
328 if (context['local_repositories'] == 'True') {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400329 def localRepoIP = ''
Denis Egorenkof97aec22019-02-05 14:57:33 +0400330 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 +0400331 localRepoIP = context['local_repo_url']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400332 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
333 } else {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400334 localRepoIP = context['aptly_server_deploy_address']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400335 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}"
336 }
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400337 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000338 smc['PIPELINES_FROM_ISO'] = 'false'
339 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
340 smc['LOCAL_REPOS'] = 'true'
341 }
342 if (context['upstream_proxy_enabled'] == 'True') {
343 if (context['upstream_proxy_auth_enabled'] == 'True') {
344 smc['http_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
345 smc['https_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
346 } else {
347 smc['http_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
348 smc['https_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
349 }
350 }
351
352 for (i in common.entries(smc)) {
353 sh "sed -i 's,${i[0]}=.*,${i[0]}=${i[1]},' user_data"
354 }
355
Denis Egorenko032a2b72019-04-03 14:56:52 +0400356 // calculate netmask
Denis Egorenko1f131b72019-04-05 14:42:48 +0400357 def deployNetworkSubnet = ''
358 if (context.get('deploy_network_subnet')) {
359 def subnet = new SubnetUtils(context['deploy_network_subnet'])
360 deployNetworkSubnet = subnet.getInfo().getNetmask()
361 } else if (context.get('deploy_network_netmask')) { // case for 2018.4.0
362 deployNetworkSubnet = context['deploy_network_netmask']
363 } else {
364 error('Neither context parameter deploy_network_subnet or deploy_network_netmask should be set!')
365 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000366 // create cfg config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400367 if (outdateGeneration) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200368 args += ["--hostname ${context['salt_master_hostname']}", "${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"]
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400369 sh "./create-config-drive ${args.join(' ')}"
370 } else {
371 args += [
372 "--name ${context['salt_master_hostname']}", "--hostname ${context['salt_master_hostname']}.${context['cluster_domain']}", "--clean-up",
Denis Egorenko032a2b72019-04-03 14:56:52 +0400373 "--ip ${context['salt_master_management_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400374 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400375 ]
azvyagintsevc4c047a2019-04-04 17:42:48 +0300376 sh "chmod 0755 create-config-drive.py ; ./create-config-drive.py ${args.join(' ')}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400377 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000378 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 +0000379
380 // save cfg iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000381 archiveArtifacts artifacts: "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000382
383 if (context['local_repositories'] == 'True') {
384 def aptlyServerHostname = context.aptly_server_hostname
385 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"
386
387 def smc_apt = [:]
388 smc_apt['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400389 if (outdateGeneration) {
390 smc_apt['APTLY_DEPLOY_IP'] = context['aptly_server_deploy_address']
391 smc_apt['APTLY_DEPLOY_NETMASK'] = context['deploy_network_netmask']
392 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000393 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${context['cluster_domain']}"
394
395 for (i in common.entries(smc_apt)) {
396 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
397 }
398
399 // create apt config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400400 if (outdateGeneration) {
401 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
402 } else {
403 args = [
Denis Egorenko032a2b72019-04-03 14:56:52 +0400404 "--ip ${context['aptly_server_deploy_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400405 "--user-data mirror_config", "--hostname ${aptlyServerHostname}.${context['cluster_domain']}", "--name ${aptlyServerHostname}", "--clean-up",
406 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400407 ]
408 sh "python ./create-config-drive.py ${args.join(' ')}"
409 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000410 sh("mv ${aptlyServerHostname}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000411
412 // save apt iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000413 archiveArtifacts artifacts: "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000414 }
415 }
416
417 stage('Save changes reclass model') {
Denis Egorenkod9865252019-04-24 15:41:57 +0400418 sh(returnStatus: true, script: "tar -czf ${context['cluster_name']}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
419 archiveArtifacts artifacts: "${context['cluster_name']}.tar.gz"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000420
azvyagintsev5400d1d2018-12-11 13:19:29 +0200421 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
azvyagintsev6d678da2018-11-28 21:19:06 +0200422 emailext(to: RequesterEmail,
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000423 attachmentsPattern: "output-${context['cluster_name']}/*",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000424 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and attached to this email.\nEnjoy!\n\nMirantis",
425 subject: "Your Salt model ${context['cluster_name']}")
426 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000427 dir("output-${context['cluster_name']}") {
428 deleteDir()
429 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000430 }
431
432 // Fail, but leave possibility to get failed artifacts
azvyagintsev6d678da2018-11-28 21:19:06 +0200433 if (!testResult && runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000434 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
435 error('Test stage finished: FAILURE')
436 }
437
438 } catch (Throwable e) {
439 currentBuild.result = "FAILURE"
440 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
441 throw e
442 } finally {
443 stage('Clean workspace directories') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000444 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000445 }
446 // common.sendNotification(currentBuild.result,"",["slack"])
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400447 stage('Save artifacts to Artifactory') {
448 def artifactory = new com.mirantis.mcp.MCPArtifactory()
azvyagintsev06dfe402019-03-22 17:58:53 +0200449 def buildProps = ["context=${context['cluster_name']}"]
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400450 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
451 buildProps.add("emailTo=${RequesterEmail}")
452 }
453 def artifactoryLink = artifactory.uploadJobArtifactsToArtifactory([
azvyagintsev06dfe402019-03-22 17:58:53 +0200454 'artifactory' : 'mcp-ci',
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400455 'artifactoryRepo': "drivetrain-local/${JOB_NAME}/${context['cluster_name']}-${BUILD_NUMBER}",
azvyagintsev06dfe402019-03-22 17:58:53 +0200456 'buildProps' : buildProps,
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400457 ])
458 currentBuild.description += "<br/>${artifactoryLink}"
459 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400460 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200461 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400462}