blob: 29f03fe1c24bbb99862cc653e3057b79ac10bff3 [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!
Denis Egorenkof0d213d2019-06-03 19:05:40 +040078 [ context.get('cookiecutter_template_branch'), context.get('shared_reclass_branch'), context.get('mcp_common_scripts_branch') ].any { branch ->
azvyagintsev866b19a2018-11-20 18:21:43 +020079 if (branch.toString().startsWith('release/')) {
80 gitGuessedVersion = branch
81 return true
82 }
83 }
Denis Egorenkof0d213d2019-06-03 19:05:40 +040084
85 [ 'cookiecutter_template_branch', 'shared_reclass_branch', 'mcp_common_scripts_branch' ].each { repoName ->
Denis Egorenko0b283902019-06-04 13:09:15 +040086 if (context['mcp_version'] in [ "nightly", "testing", "stable" ] && ! context.get(repoName)) {
Denis Egorenkof0d213d2019-06-03 19:05:40 +040087 context[repoName] = 'master'
88 } else if (! context.get(repoName)) {
89 context[repoName] = gitGuessedVersion ?: "release/${context['mcp_version']}".toString()
90 }
azvyagintsev866b19a2018-11-20 18:21:43 +020091 }
92 //
93 distribRevision = context['mcp_version']
94 if (['master'].contains(context['mcp_version'])) {
95 distribRevision = 'nightly'
96 }
97 if (distribRevision.contains('/')) {
98 distribRevision = distribRevision.split('/')[-1]
99 }
100 // Check if we are going to test bleeding-edge release, which doesn't have binary release yet
azvyagintsev8e081642019-02-03 19:15:22 +0200101 // After 2018q4 releases, need to also check 'static' repo, for example ubuntu.
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200102 def binTest = common.checkRemoteBinary(['mcp_version': distribRevision])
azvyagintsev8e081642019-02-03 19:15:22 +0200103 if (!binTest.linux_system_repo_url || !binTest.linux_system_repo_ubuntu_url) {
104 common.errorMsg("Binary release: ${distribRevision} not exist or not full. Fallback to 'proposed'! ")
azvyagintsev866b19a2018-11-20 18:21:43 +0200105 distribRevision = 'proposed'
106 }
azvyagintsev8e081642019-02-03 19:15:22 +0200107
azvyagintsev5400d1d2018-12-11 13:19:29 +0200108 // (azvyagintsev) WA for PROD-25732
109 if (context.cookiecutter_template_url.contains('gerrit.mcp.mirantis.com/mk/cookiecutter-templates')) {
110 common.warningMsg('Apply WA for PROD-25732')
111 context.cookiecutter_template_url = 'ssh://gerrit.mcp.mirantis.com:29418/mk/cookiecutter-templates.git'
112 }
azvyagintsev5b7ec892019-05-15 16:37:34 +0300113 // check, if we are going to test clear release version, w\o any updates and patches
114 if (!gitGuessedVersion && (distribRevision == context.mcp_version)) {
115 updateSaltFormulasDuringTest = false
116 }
117
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400118 if (gitGuessedVersion == 'release/proposed/2019.2.0') {
119 // CFG node in 2019.2.X update has to be bootstrapped with update/proposed repository for salt formulas
120 context['cloudinit_master_config'] = context.get('cloudinit_master_config', false) ?: [:]
121 context['cloudinit_master_config']['MCP_SALT_REPO_UPDATES'] = context['cloudinit_master_config'].get('MCP_SALT_REPO_UPDATES', false) ?:
122 'deb [arch=amd64] http://mirror.mirantis.com/update/proposed/salt-formulas/xenial xenial main'
123 }
124
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000125 common.infoMsg("Using context:\n" + context)
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000126 print prettyPrint(toJson(context))
azvyagintsev866b19a2018-11-20 18:21:43 +0200127 return context
128
129}
azvyagintsevf252b592018-08-13 18:39:14 +0300130
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000131timeout(time: 1, unit: 'HOURS') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000132 node(slaveNode) {
azvyagintsev866b19a2018-11-20 18:21:43 +0200133 def context = globalVariatorsUpdate()
azvyagintsev6d678da2018-11-28 21:19:06 +0200134 def RequesterEmail = context.get('email_address', '')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000135 def templateEnv = "${env.WORKSPACE}/template"
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200136 // modelEnv - this is reclass root, aka /srv/salt/reclass
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000137 def modelEnv = "${env.WORKSPACE}/model"
138 def testEnv = "${env.WORKSPACE}/test"
139 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +0200140
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000141 try {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000142 //
143 def cutterEnv = "${env.WORKSPACE}/cutter"
144 def systemEnv = "${modelEnv}/classes/system"
145 def testResult = false
146 def user
147 wrap([$class: 'BuildUser']) {
148 user = env.BUILD_USER_ID
149 }
azvyagintsev6d678da2018-11-28 21:19:06 +0200150 currentBuild.description = "${context['cluster_name']} ${RequesterEmail}"
azvyagintsev866b19a2018-11-20 18:21:43 +0200151
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000152 stage('Download Cookiecutter template') {
153 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
154 checkout([
155 $class : 'GitSCM',
156 branches : [[name: 'FETCH_HEAD'],],
157 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
158 userRemoteConfigs: [[url: context['cookiecutter_template_url'], refspec: context['cookiecutter_template_branch'], credentialsId: gerritCredentials],],
159 ])
160 }
161 stage('Create empty reclass model') {
162 dir(path: modelEnv) {
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200163 sh 'rm -rfv .git; git init'
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000164 sshagent(credentials: [gerritCredentials]) {
165 sh "git submodule add ${context['shared_reclass_url']} 'classes/system'"
166 }
167 }
168 checkout([
169 $class : 'GitSCM',
170 branches : [[name: 'FETCH_HEAD'],],
171 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
172 userRemoteConfigs: [[url: context['shared_reclass_url'], refspec: context['shared_reclass_branch'], credentialsId: gerritCredentials],],
173 ])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200174 git.commitGitChanges(modelEnv, 'Added new shared reclass submodule', "${user}@localhost", "${user}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000175 }
176
177 stage('Generate model') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000178 // GNUPGHOME environment variable is required for all gpg commands
179 // and for python.generateModel execution
Denis Egorenkoce93af32019-05-20 16:57:33 +0400180 def envOpts = ["GNUPGHOME=${env.WORKSPACE}/gpghome"]
181 withEnv(envOpts) {
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300182 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000183 sh "mkdir gpghome; chmod 700 gpghome"
Dmitry Pyzhovb5c74c72018-12-17 22:08:50 +0300184 def secretKeyID = RequesterEmail ?: "salt@${context['cluster_domain']}".toString()
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300185 if (!context.get('secrets_encryption_private_key')) {
186 def batchData = """
Denis Egorenko131de5f2019-05-16 14:25:40 +0400187 %echo Generating a basic OpenPGP key for Salt-Master
188 %no-protection
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300189 Key-Type: 1
190 Key-Length: 4096
191 Expire-Date: 0
192 Name-Real: ${context['salt_master_hostname']}.${context['cluster_domain']}
193 Name-Email: ${secretKeyID}
Denis Egorenko131de5f2019-05-16 14:25:40 +0400194 %commit
195 %echo done
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300196 """.stripIndent()
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200197 writeFile file: 'gpg-batch.txt', text: batchData
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300198 sh "gpg --gen-key --batch < gpg-batch.txt"
199 sh "gpg --export-secret-key -a ${secretKeyID} > gpgkey.asc"
200 } else {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200201 writeFile file: 'gpgkey.asc', text: context['secrets_encryption_private_key']
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300202 sh "gpg --import gpgkey.asc"
Denis Egorenko131de5f2019-05-16 14:25:40 +0400203 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 +0300204 }
205 context['secrets_encryption_key_id'] = secretKeyID
206 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400207 if (context.get('cfg_failsafe_ssh_public_key')) {
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200208 writeFile file: 'failsafe-ssh-key.pub', text: context['cfg_failsafe_ssh_public_key']
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400209 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200210 if (!fileExists(new File(templateEnv, 'tox.ini').toString())) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200211 reqs = new File(templateEnv, 'requirements.txt').toString()
212 if (fileExists(reqs)) {
213 python.setupVirtualenv(cutterEnv, 'python2', [], reqs)
214 } else {
215 python.setupCookiecutterVirtualenv(cutterEnv)
216 }
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200217 python.generateModel(common2.dumpYAML(['default_context': context]), 'default_context', context['salt_master_hostname'], cutterEnv, modelEnv, templateEnv, false)
218 } else {
219 // tox-based CC generated structure of reclass,from the root. Otherwise for bw compat, modelEnv
220 // still expect only lower lvl of project, aka model/classes/cluster/XXX/. So,lets dump result into
221 // temp dir, and then copy it over initial structure.
222 reclassTempRootDir = sh(script: "mktemp -d -p ${env.WORKSPACE}", returnStdout: true).trim()
Denis Egorenkoce93af32019-05-20 16:57:33 +0400223 GenerateModelToxDocker(['context': common2.dumpYAML(['default_context': context]),
224 'ccRoot' : templateEnv,
225 'outDir' : reclassTempRootDir,
226 'envOpts': envOpts])
azvyagintsev4f34f7a2019-02-26 18:47:37 +0200227 dir(modelEnv) {
228 common.warningMsg('Forming reclass-root structure...')
229 sh("cp -ra ${reclassTempRootDir}/reclass/* .")
230 }
231 }
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300232 git.commitGitChanges(modelEnv, "Create model ${context['cluster_name']}", "${user}@localhost", "${user}")
233 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000234 }
235
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000236 stage("Test") {
azvyagintsev6d678da2018-11-28 21:19:06 +0200237 if (runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000238 sh("cp -r ${modelEnv} ${testEnv}")
azvyagintsev7d6d46c2019-02-11 14:25:41 +0200239 if (fileExists('gpgkey.asc')) {
240 common.infoMsg('gpgkey.asc found!Copy it into reclass folder for tests..')
241 sh("cp -v gpgkey.asc ${testEnv}/salt_master_pillar.asc")
242 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000243 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
azvyagintsev5b7ec892019-05-15 16:37:34 +0300244 common.warningMsg("Attempt to run test against:\n" +
245 "DISTRIB_REVISION from ${distribRevision}\n" +
246 "updateSaltFormulasDuringTest = ${updateSaltFormulasDuringTest}")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000247 try {
248 def config = [
Denis Egorenko926bc7d2019-02-19 14:27:34 +0400249 'dockerHostname' : "${context['salt_master_hostname']}",
250 'domain' : "${context['cluster_domain']}",
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000251 'reclassEnv' : testEnv,
252 'distribRevision' : distribRevision,
253 'dockerContainerName': DockerCName,
Denis Egorenkod87490e2019-02-26 20:00:41 +0400254 'testContext' : 'salt-model-node',
azvyagintsev5b7ec892019-05-15 16:37:34 +0300255 'dockerExtraOpts' : ['--memory=3g'],
256 'updateSaltFormulas' : updateSaltFormulasDuringTest
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000257 ]
258 testResult = saltModelTesting.testNode(config)
259 common.infoMsg("Test finished: SUCCESS")
260 } catch (Exception ex) {
261 common.warningMsg("Test finished: FAILED")
262 testResult = false
263 }
264 } else {
265 common.warningMsg("Test stage has been skipped!")
266 }
267 }
268 stage("Generate config drives") {
269 // apt package genisoimage is required for this stage
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000270 // download create-config-drive
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000271 def commonScriptsRepoUrl = context['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
272 checkout([
273 $class : 'GitSCM',
274 branches : [[name: 'FETCH_HEAD'],],
275 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
azvyagintsev866b19a2018-11-20 18:21:43 +0200276 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: context['mcp_common_scripts_branch'], credentialsId: gerritCredentials],],
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000277 ])
278
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400279 def outdateGeneration = false
280 if (fileExists('mcp-common-scripts/config-drive/create_config_drive.py')) {
281 sh 'cp mcp-common-scripts/config-drive/create_config_drive.py create-config-drive.py'
282 } else {
283 outdateGeneration = true
284 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
285 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000286 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'
287
Denis Egorenkoc1e1d042019-04-26 08:55:27 +0000288 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
289 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400290 args = [
azvyagintsev1385db92019-03-22 16:05:10 +0200291 "--user-data user_data", "--model ${modelEnv}",
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400292 "--mk-pipelines ${pipelineEnv}/mk-pipelines/", "--pipeline-library ${pipelineEnv}/pipeline-library/"
293 ]
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300294 if (context['secrets_encryption_enabled'] == 'True') {
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400295 args.add('--gpg-key gpgkey.asc')
Dmitry Pyzhov089fb4f2018-12-11 16:58:00 +0300296 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400297 if (context.get('cfg_failsafe_ssh_public_key')) {
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400298 if (outdateGeneration) {
299 args.add('--ssh-key failsafe-ssh-key.pub')
300 } else {
Denis Egorenko85ddf0d2019-03-18 18:11:19 +0400301 if (context.get('cfg_failsafe_user')) {
302 args.add('--ssh-keys failsafe-ssh-key.pub')
303 args.add("--cloud-user-name ${context.get('cfg_failsafe_user')}")
304 }
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400305 }
Stanislav Riazanovda45ea02018-12-21 16:12:50 +0400306 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000307 // load data from model
308 def smc = [:]
309 smc['SALT_MASTER_MINION_ID'] = "${context['salt_master_hostname']}.${context['cluster_domain']}"
310 smc['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Ivan Berezovskiy1e66f502019-05-24 18:11:17 +0400311 if (context.get('cloudinit_master_config', false)) {
312 context['cloudinit_master_config'].each { k, v ->
313 smc[k] = v
314 }
315 }
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400316 if (outdateGeneration) {
317 smc['DEPLOY_NETWORK_GW'] = context['deploy_network_gateway']
318 smc['DEPLOY_NETWORK_NETMASK'] = context['deploy_network_netmask']
319 if (context.get('deploy_network_mtu')) {
320 smc['DEPLOY_NETWORK_MTU'] = context['deploy_network_mtu']
321 }
322 smc['DNS_SERVERS'] = context['dns_server01']
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000323 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000324 smc['MCP_VERSION'] = "${context['mcp_version']}"
325 if (context['local_repositories'] == 'True') {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400326 def localRepoIP = ''
Denis Egorenkof97aec22019-02-05 14:57:33 +0400327 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 +0400328 localRepoIP = context['local_repo_url']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400329 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
330 } else {
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400331 localRepoIP = context['aptly_server_deploy_address']
Denis Egorenkof97aec22019-02-05 14:57:33 +0400332 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}"
333 }
Denis Egorenko6bfa7552019-02-05 19:09:25 +0400334 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000335 smc['PIPELINES_FROM_ISO'] = 'false'
336 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
337 smc['LOCAL_REPOS'] = 'true'
338 }
339 if (context['upstream_proxy_enabled'] == 'True') {
340 if (context['upstream_proxy_auth_enabled'] == 'True') {
341 smc['http_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
342 smc['https_proxy'] = 'http://' + context['upstream_proxy_user'] + ':' + context['upstream_proxy_password'] + '@' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
343 } else {
344 smc['http_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
345 smc['https_proxy'] = 'http://' + context['upstream_proxy_address'] + ':' + context['upstream_proxy_port']
346 }
347 }
348
349 for (i in common.entries(smc)) {
Denis Egorenko815d5022019-07-15 16:01:04 +0400350 sh "sed -i 's,export ${i[0]}=.*,export ${i[0]}=\${${i[0]}:-\"${i[1]}\"},' user_data"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000351 }
352
Denis Egorenko032a2b72019-04-03 14:56:52 +0400353 // calculate netmask
Denis Egorenko1f131b72019-04-05 14:42:48 +0400354 def deployNetworkSubnet = ''
355 if (context.get('deploy_network_subnet')) {
356 def subnet = new SubnetUtils(context['deploy_network_subnet'])
357 deployNetworkSubnet = subnet.getInfo().getNetmask()
358 } else if (context.get('deploy_network_netmask')) { // case for 2018.4.0
359 deployNetworkSubnet = context['deploy_network_netmask']
360 } else {
361 error('Neither context parameter deploy_network_subnet or deploy_network_netmask should be set!')
362 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000363 // create cfg config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400364 if (outdateGeneration) {
azvyagintsev06dfe402019-03-22 17:58:53 +0200365 args += ["--hostname ${context['salt_master_hostname']}", "${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"]
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400366 sh "./create-config-drive ${args.join(' ')}"
367 } else {
368 args += [
369 "--name ${context['salt_master_hostname']}", "--hostname ${context['salt_master_hostname']}.${context['cluster_domain']}", "--clean-up",
Denis Egorenko032a2b72019-04-03 14:56:52 +0400370 "--ip ${context['salt_master_management_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400371 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400372 ]
azvyagintsevc4c047a2019-04-04 17:42:48 +0300373 sh "chmod 0755 create-config-drive.py ; ./create-config-drive.py ${args.join(' ')}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400374 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000375 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 +0000376
377 // save cfg iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000378 archiveArtifacts artifacts: "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000379
380 if (context['local_repositories'] == 'True') {
381 def aptlyServerHostname = context.aptly_server_hostname
382 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"
383
384 def smc_apt = [:]
385 smc_apt['SALT_MASTER_DEPLOY_IP'] = context['salt_master_management_address']
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400386 if (outdateGeneration) {
387 smc_apt['APTLY_DEPLOY_IP'] = context['aptly_server_deploy_address']
388 smc_apt['APTLY_DEPLOY_NETMASK'] = context['deploy_network_netmask']
389 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000390 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${context['cluster_domain']}"
391
392 for (i in common.entries(smc_apt)) {
393 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
394 }
395
396 // create apt config-drive
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400397 if (outdateGeneration) {
398 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
399 } else {
400 args = [
Denis Egorenko032a2b72019-04-03 14:56:52 +0400401 "--ip ${context['aptly_server_deploy_address']}", "--netmask ${deployNetworkSubnet}", "--gateway ${context['deploy_network_gateway']}",
Denis Egorenkoaaeda9b2019-02-28 20:55:59 +0400402 "--user-data mirror_config", "--hostname ${aptlyServerHostname}.${context['cluster_domain']}", "--name ${aptlyServerHostname}", "--clean-up",
403 "--dns-nameservers ${context['dns_server01']},${context['dns_server02']}"
Denis Egorenkoaeaa0132019-02-25 16:55:08 +0400404 ]
405 sh "python ./create-config-drive.py ${args.join(' ')}"
406 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000407 sh("mv ${aptlyServerHostname}.${context['cluster_domain']}-config.iso output-${context['cluster_name']}/")
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000408
409 // save apt iso to artifacts
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000410 archiveArtifacts artifacts: "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000411 }
412 }
413
414 stage('Save changes reclass model') {
Denis Egorenkod9865252019-04-24 15:41:57 +0400415 sh(returnStatus: true, script: "tar -czf ${context['cluster_name']}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
416 archiveArtifacts artifacts: "${context['cluster_name']}.tar.gz"
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000417
azvyagintsev5400d1d2018-12-11 13:19:29 +0200418 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
Denis Egorenkof9729842019-07-08 15:53:01 +0400419 def mailSubject = "Your Salt model ${context['cluster_name']}"
420 if (context.get('send_method') == 'gcs') {
421 def gcs = new com.mirantis.mk.GoogleCloudStorage()
422 def uploadIsos = [ "output-${context['cluster_name']}/${context['salt_master_hostname']}.${context['cluster_domain']}-config.iso" ]
423 if (context['local_repositories'] == 'True') {
424 uploadIsos << "output-${context['cluster_name']}/${aptlyServerHostname}.${context['cluster_domain']}-config.iso"
425 }
426 // generate random hash to have uniq and unpredictable link to file
427 def randHash = common.generateRandomHashString(64)
428 def config = [
429 'creds': context['gcs_creds'],
430 'project': context['gcs_project'],
431 'dest': "gs://${context['gcs_bucket']}/${randHash}",
432 'sources': uploadIsos
433 ]
434 def fileURLs = gcs.uploadArtifactToGoogleStorageBucket(config).join(' ').replace('gs://', 'https://storage.googleapis.com/')
435 emailext(to: RequesterEmail,
436 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",
437 subject: mailSubject)
438 } else {
439 emailext(to: RequesterEmail,
440 attachmentsPattern: "output-${context['cluster_name']}/*",
441 body: "Mirantis Jenkins\n\nRequested reclass model ${context['cluster_name']} has been created and attached to this email.\nEnjoy!\n\nMirantis",
442 subject: mailSubject)
443 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000444 }
Aleksey Zvyagintsevaa235f22019-04-24 12:46:42 +0000445 dir("output-${context['cluster_name']}") {
446 deleteDir()
447 }
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000448 }
449
450 // Fail, but leave possibility to get failed artifacts
azvyagintsev6d678da2018-11-28 21:19:06 +0200451 if (!testResult && runTestModel) {
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000452 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
453 error('Test stage finished: FAILURE')
454 }
455
456 } catch (Throwable e) {
457 currentBuild.result = "FAILURE"
458 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
459 throw e
460 } finally {
461 stage('Clean workspace directories') {
Aleksey Zvyagintsev7ca28d22019-02-25 11:05:49 +0000462 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
Aleksey Zvyagintsevb16902d2018-10-29 12:33:48 +0000463 }
464 // common.sendNotification(currentBuild.result,"",["slack"])
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400465 stage('Save artifacts to Artifactory') {
466 def artifactory = new com.mirantis.mcp.MCPArtifactory()
azvyagintsev06dfe402019-03-22 17:58:53 +0200467 def buildProps = ["context=${context['cluster_name']}"]
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400468 if (RequesterEmail != '' && !RequesterEmail.contains('example')) {
469 buildProps.add("emailTo=${RequesterEmail}")
470 }
471 def artifactoryLink = artifactory.uploadJobArtifactsToArtifactory([
azvyagintsev06dfe402019-03-22 17:58:53 +0200472 'artifactory' : 'mcp-ci',
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400473 'artifactoryRepo': "drivetrain-local/${JOB_NAME}/${context['cluster_name']}-${BUILD_NUMBER}",
azvyagintsev06dfe402019-03-22 17:58:53 +0200474 'buildProps' : buildProps,
Denis Egorenko34c4a3b2019-03-12 12:48:15 +0400475 ])
476 currentBuild.description += "<br/>${artifactoryLink}"
477 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400478 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200479 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400480}