blob: fb28837d3a8a0382c49946f3eaddbef385860641 [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.
Tomáš Kukrál91e49252017-05-09 14:40:26 +02006 * EMAIL_ADDRESS Email to send a created tar file
Sergey Galkin8b87f6e2018-10-24 18:40:13 +04007 * CREDENTIALS_ID Credentials id for git
azvyagintsev3ed704f2018-07-09 15:49:27 +03008 **/
Tomáš Kukrál7ded3642017-03-27 15:52:51 +02009
10common = new com.mirantis.mk.Common()
11git = new com.mirantis.mk.Git()
12python = new com.mirantis.mk.Python()
chnyda89191012017-05-29 15:38:35 +020013saltModelTesting = new com.mirantis.mk.SaltModelTesting()
Tomáš Kukrál7ded3642017-03-27 15:52:51 +020014
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000015
azvyagintsevb8ddb492018-09-12 14:59:45 +030016slaveNode = env.SLAVE_NODE ?: 'python&&docker'
Ivan Berezovskiy46b7bbc2018-10-30 22:32:13 +040017gerritCredentials = env.CREDENTIALS_ID ?: 'gerrit'
azvyagintsevf252b592018-08-13 18:39:14 +030018
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000019timeout(time: 2, unit: 'HOURS') {
azvyagintsev636493c2018-09-12 17:17:05 +030020 node(slaveNode) {
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000021 sshagent(credentials: [gerritCredentials]) {
22 def templateEnv = "${env.WORKSPACE}/template"
23 def modelEnv = "${env.WORKSPACE}/model"
24 def testEnv = "${env.WORKSPACE}/test"
25 def pipelineEnv = "${env.WORKSPACE}/pipelines"
Tomáš Kukrál9f6260f2017-03-29 23:58:26 +020026
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000027 try {
28 def templateContext = readYaml text: COOKIECUTTER_TEMPLATE_CONTEXT
29 def mcpVersion = templateContext.default_context.mcp_version
30 def sharedReclassUrl = templateContext.default_context.shared_reclass_url
31 def clusterDomain = templateContext.default_context.cluster_domain
32 def clusterName = templateContext.default_context.cluster_name
33 def saltMaster = templateContext.default_context.salt_master_hostname
34 def cutterEnv = "${env.WORKSPACE}/cutter"
35 def jinjaEnv = "${env.WORKSPACE}/jinja"
36 def outputDestination = "${modelEnv}/classes/cluster/${clusterName}"
37 def systemEnv = "${modelEnv}/classes/system"
38 def targetBranch = "feature/${clusterName}"
39 def templateBaseDir = "${env.WORKSPACE}/template"
40 def templateDir = "${templateEnv}/template/dir"
41 def templateOutputDir = templateBaseDir
42 def user
43 def testResult = false
44 wrap([$class: 'BuildUser']) {
45 user = env.BUILD_USER_ID
azvyagintsev636493c2018-09-12 17:17:05 +030046 }
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000047 currentBuild.description = clusterName
48 common.infoMsg("Using context:\n" + templateContext)
azvyagintsevbd2655d2018-10-25 16:25:04 +030049
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000050
51 stage('Download Cookiecutter template') {
52 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
53 def cookiecutterTemplateUrl = templateContext.default_context.cookiecutter_template_url
54 def cookiecutterTemplateBranch = templateContext.default_context.cookiecutter_template_branch
55 // Use mcpVersion git tag if not specified branch for cookiecutter-templates
56 if (cookiecutterTemplateBranch == '') {
57 cookiecutterTemplateBranch = mcpVersion
58 // Don't have nightly/testing/stable for cookiecutter-templates repo, therefore use master
59 if (["nightly", "testing", "stable"].contains(mcpVersion)) {
60 cookiecutterTemplateBranch = 'master'
61 }
62 }
azvyagintsevbd2655d2018-10-25 16:25:04 +030063 checkout([
64 $class : 'GitSCM',
65 branches : [[name: 'FETCH_HEAD'],],
66 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: templateEnv]],
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000067 userRemoteConfigs: [[url: cookiecutterTemplateUrl, refspec: cookiecutterTemplateBranch, credentialsId: gerritCredentials],],
azvyagintsevbd2655d2018-10-25 16:25:04 +030068 ])
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000069 }
70 stage('Create empty reclass model') {
71 dir(path: modelEnv) {
72 sh "rm -rfv .git; git init"
73 sh "git submodule add ${sharedReclassUrl} 'classes/system'"
azvyagintsevd209f7c2018-10-25 18:36:31 +030074 }
azvyagintsevd209f7c2018-10-25 18:36:31 +030075
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000076 def sharedReclassBranch = templateContext.default_context.shared_reclass_branch
77 // Use mcpVersion git tag if not specified branch for reclass-system
78 if (sharedReclassBranch == '') {
79 sharedReclassBranch = mcpVersion
80 // Don't have nightly/testing for reclass-system repo, therefore use master
81 if (["nightly", "testing", "stable"].contains(mcpVersion)) {
82 common.warningMsg("Fetching reclass-system from master!")
83 sharedReclassBranch = 'master'
84 }
azvyagintsevd209f7c2018-10-25 18:36:31 +030085 }
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000086 checkout([
87 $class : 'GitSCM',
88 branches : [[name: 'FETCH_HEAD'],],
89 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: systemEnv]],
90 userRemoteConfigs: [[url: sharedReclassUrl, refspec: sharedReclassBranch, credentialsId: gerritCredentials],],
91 ])
92 git.commitGitChanges(modelEnv, "Added new shared reclass submodule", "${user}@localhost", "${user}")
azvyagintsevd209f7c2018-10-25 18:36:31 +030093 }
azvyagintsevd209f7c2018-10-25 18:36:31 +030094
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +000095 stage('Generate model') {
96 python.setupCookiecutterVirtualenv(cutterEnv)
97 python.generateModel(COOKIECUTTER_TEMPLATE_CONTEXT, 'default_context', saltMaster, cutterEnv, modelEnv, templateEnv, false)
98 git.commitGitChanges(modelEnv, "Create model ${clusterName}", "${user}@localhost", "${user}")
azvyagintsevd209f7c2018-10-25 18:36:31 +030099 }
azvyagintsevd209f7c2018-10-25 18:36:31 +0300100
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000101 stage("Test") {
102 if (TEST_MODEL.toBoolean() && sharedReclassUrl != '') {
103 distribRevision = mcpVersion
104 if (['master'].contains(mcpVersion)) {
105 distribRevision = 'nightly'
106 }
107 if (distribRevision.contains('/')) {
108 distribRevision = distribRevision.split('/')[-1]
109 }
110 // Check if we are going to test bleeding-edge release, which doesn't have binary release yet
111 if (!common.checkRemoteBinary([apt_mk_version: distribRevision]).linux_system_repo_url) {
112 common.errorMsg("Binary release: ${distribRevision} not exist. Fallback to 'proposed'! ")
113 distribRevision = 'proposed'
114 }
115 sh("cp -r ${modelEnv} ${testEnv}")
116 def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
117 common.infoMsg("Attempt to run test against distribRevision: ${distribRevision}")
118 try {
119 def config = [
120 'dockerHostname' : "${saltMaster}.${clusterDomain}",
121 'reclassEnv' : testEnv,
122 'distribRevision' : distribRevision,
123 'dockerContainerName': DockerCName,
124 'testContext' : 'salt-model-node'
125 ]
126 testResult = saltModelTesting.testNode(config)
127 common.infoMsg("Test finished: SUCCESS")
128 } catch (Exception ex) {
129 common.warningMsg("Test finished: FAILED")
130 testResult = false
131 }
azvyagintsevd209f7c2018-10-25 18:36:31 +0300132 } else {
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000133 common.warningMsg("Test stage has been skipped!")
134 }
135 }
136 stage("Generate config drives") {
137 // apt package genisoimage is required for this stage
138
139 // download create-config-drive
140 // FIXME: that should be refactored, to use git clone - to be able download it from custom repo.
141 def mcpCommonScriptsBranch = templateContext['default_context']['mcp_common_scripts_branch']
142 if (mcpCommonScriptsBranch == '') {
143 mcpCommonScriptsBranch = mcpVersion
144 // Don't have n/t/s for mcp-common-scripts repo, therefore use master
145 if (["nightly", "testing", "stable"].contains(mcpVersion)) {
146 common.warningMsg("Fetching mcp-common-scripts from master!")
147 mcpCommonScriptsBranch = 'master'
148 }
149 }
Ivan Berezovskiy46b7bbc2018-10-30 22:32:13 +0400150 def commonScriptsRepoUrl = templateContext['default_context']['mcp_common_scripts_repo'] ?: 'ssh://gerrit.mcp.mirantis.com:29418/mcp/mcp-common-scripts'
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000151 checkout([
152 $class : 'GitSCM',
153 branches : [[name: 'FETCH_HEAD'],],
154 extensions : [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mcp-common-scripts']],
155 userRemoteConfigs: [[url: commonScriptsRepoUrl, refspec: mcpCommonScriptsBranch, credentialsId: gerritCredentials],],
156 ])
157
158 sh 'cp mcp-common-scripts/config-drive/create_config_drive.sh create-config-drive && chmod +x create-config-drive'
159 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'
160
161 sh "git clone --mirror https://github.com/Mirantis/mk-pipelines.git ${pipelineEnv}/mk-pipelines"
162 sh "git clone --mirror https://github.com/Mirantis/pipeline-library.git ${pipelineEnv}/pipeline-library"
163 args = "--user-data user_data --hostname ${saltMaster} --model ${modelEnv} --mk-pipelines ${pipelineEnv}/mk-pipelines/ --pipeline-library ${pipelineEnv}/pipeline-library/ ${saltMaster}.${clusterDomain}-config.iso"
164
165 // load data from model
166 def smc = [:]
167 smc['SALT_MASTER_MINION_ID'] = "${saltMaster}.${clusterDomain}"
168 smc['SALT_MASTER_DEPLOY_IP'] = templateContext['default_context']['salt_master_management_address']
169 smc['DEPLOY_NETWORK_GW'] = templateContext['default_context']['deploy_network_gateway']
170 smc['DEPLOY_NETWORK_NETMASK'] = templateContext['default_context']['deploy_network_netmask']
171 if (templateContext['default_context'].get('deploy_network_mtu')) {
172 smc['DEPLOY_NETWORK_MTU'] = templateContext['default_context']['deploy_network_mtu']
173 }
174 smc['DNS_SERVERS'] = templateContext['default_context']['dns_server01']
175 smc['MCP_VERSION'] = "${mcpVersion}"
176 if (templateContext['default_context']['local_repositories'] == 'True') {
177 def localRepoIP = templateContext['default_context']['local_repo_url']
178 smc['MCP_SALT_REPO_KEY'] = "http://${localRepoIP}/public.gpg"
179 smc['MCP_SALT_REPO_URL'] = "http://${localRepoIP}/ubuntu-xenial"
180 smc['PIPELINES_FROM_ISO'] = 'false'
181 smc['PIPELINE_REPO_URL'] = "http://${localRepoIP}:8088"
182 smc['LOCAL_REPOS'] = 'true'
183 }
184 if (templateContext['default_context']['upstream_proxy_enabled'] == 'True') {
185 if (templateContext['default_context']['upstream_proxy_auth_enabled'] == 'True') {
186 smc['http_proxy'] = 'http://' + templateContext['default_context']['upstream_proxy_user'] + ':' + templateContext['default_context']['upstream_proxy_password'] + '@' + templateContext['default_context']['upstream_proxy_address'] + ':' + templateContext['default_context']['upstream_proxy_port']
187 smc['https_proxy'] = 'http://' + templateContext['default_context']['upstream_proxy_user'] + ':' + templateContext['default_context']['upstream_proxy_password'] + '@' + templateContext['default_context']['upstream_proxy_address'] + ':' + templateContext['default_context']['upstream_proxy_port']
188 } else {
189 smc['http_proxy'] = 'http://' + templateContext['default_context']['upstream_proxy_address'] + ':' + templateContext['default_context']['upstream_proxy_port']
190 smc['https_proxy'] = 'http://' + templateContext['default_context']['upstream_proxy_address'] + ':' + templateContext['default_context']['upstream_proxy_port']
191 }
192 }
193
194 for (i in common.entries(smc)) {
195 sh "sed -i 's,${i[0]}=.*,${i[0]}=${i[1]},' user_data"
196 }
197
198 // create cfg config-drive
199 sh "./create-config-drive ${args}"
200 sh("mkdir output-${clusterName} && mv ${saltMaster}.${clusterDomain}-config.iso output-${clusterName}/")
201
202 // save cfg iso to artifacts
203 archiveArtifacts artifacts: "output-${clusterName}/${saltMaster}.${clusterDomain}-config.iso"
204
205 if (templateContext['default_context']['local_repositories'] == 'True') {
206 def aptlyServerHostname = templateContext.default_context.aptly_server_hostname
207 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"
208
209 def smc_apt = [:]
210 smc_apt['SALT_MASTER_DEPLOY_IP'] = templateContext['default_context']['salt_master_management_address']
211 smc_apt['APTLY_DEPLOY_IP'] = templateContext['default_context']['aptly_server_deploy_address']
212 smc_apt['APTLY_DEPLOY_NETMASK'] = templateContext['default_context']['deploy_network_netmask']
213 smc_apt['APTLY_MINION_ID'] = "${aptlyServerHostname}.${clusterDomain}"
214
215 for (i in common.entries(smc_apt)) {
216 sh "sed -i \"s,export ${i[0]}=.*,export ${i[0]}=${i[1]},\" mirror_config"
217 }
218
219 // create apt config-drive
220 sh "./create-config-drive --user-data mirror_config --hostname ${aptlyServerHostname} ${aptlyServerHostname}.${clusterDomain}-config.iso"
221 sh("mv ${aptlyServerHostname}.${clusterDomain}-config.iso output-${clusterName}/")
222
223 // save apt iso to artifacts
224 archiveArtifacts artifacts: "output-${clusterName}/${aptlyServerHostname}.${clusterDomain}-config.iso"
azvyagintsevd209f7c2018-10-25 18:36:31 +0300225 }
226 }
227
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000228 stage('Save changes reclass model') {
229 sh(returnStatus: true, script: "tar -czf output-${clusterName}/${clusterName}.tar.gz --exclude='*@tmp' -C ${modelEnv} .")
230 archiveArtifacts artifacts: "output-${clusterName}/${clusterName}.tar.gz"
azvyagintsevd209f7c2018-10-25 18:36:31 +0300231
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000232 if (EMAIL_ADDRESS != null && EMAIL_ADDRESS != "") {
233 emailext(to: EMAIL_ADDRESS,
234 attachmentsPattern: "output-${clusterName}/*",
235 body: "Mirantis Jenkins\n\nRequested reclass model ${clusterName} has been created and attached to this email.\nEnjoy!\n\nMirantis",
236 subject: "Your Salt model ${clusterName}")
azvyagintsevd209f7c2018-10-25 18:36:31 +0300237 }
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000238 dir("output-${clusterName}") {
239 deleteDir()
240 }
azvyagintsevd209f7c2018-10-25 18:36:31 +0300241 }
azvyagintsevd209f7c2018-10-25 18:36:31 +0300242
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000243 // Fail, but leave possibility to get failed artifacts
244 if (!testResult && TEST_MODEL.toBoolean()) {
245 common.warningMsg('Test finished: FAILURE. Please check logs and\\or debug failed model manually!')
246 error('Test stage finished: FAILURE')
azvyagintsevd209f7c2018-10-25 18:36:31 +0300247 }
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000248
249 } catch (Throwable e) {
250 currentBuild.result = "FAILURE"
251 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
252 throw e
253 } finally {
254 stage('Clean workspace directories') {
255 sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
azvyagintsevd209f7c2018-10-25 18:36:31 +0300256 }
Aleksey Zvyagintsev8bcd3f72018-10-29 12:30:18 +0000257 // common.sendNotification(currentBuild.result,"",["slack"])
azvyagintsevd209f7c2018-10-25 18:36:31 +0300258 }
Ruslan Kamaldinov6feef402017-08-02 16:55:58 +0400259 }
Tomáš Kukrál7ded3642017-03-27 15:52:51 +0200260 }
Mikhail Ivanov9f812922017-11-07 18:52:02 +0400261}