blob: 8a314ef068595352bad7f6b95dac26d489771a68 [file] [log] [blame]
Tomáš Kukrálb9957b32017-02-28 14:49:00 +01001/**
2 *
3 * Launch heat stack with basic k8s
4 * Flow parameters:
5 * STACK_TYPE Orchestration engine: heat, ''
6 * INSTALL What should be installed (k8s, openstack, ...)
Tomáš Kukrál55357d02017-02-28 22:56:45 +01007 * TEST What should be tested (k8s, openstack, ...)
Tomáš Kukrálb9957b32017-02-28 14:49:00 +01008 *
9 * Expected parameters:
10 *
11 * required for STACK_TYPE=heat
12 * HEAT_TEMPLATE_URL URL to git repo with Heat templates
13 * HEAT_TEMPLATE_CREDENTIALS Credentials to the Heat templates repo
14 * HEAT_TEMPLATE_BRANCH Heat templates repo branch
15 * HEAT_STACK_TEMPLATE Heat stack HOT template
16 * HEAT_STACK_ENVIRONMENT Heat stack environmental parameters
17 * HEAT_STACK_ZONE Heat stack availability zone
18 * HEAT_STACK_PUBLIC_NET Heat stack floating IP pool
19 * HEAT_STACK_DELETE Delete Heat stack when finished (bool)
20 * HEAT_STACK_CLEANUP_JOB Name of job for deleting Heat stack
21 * HEAT_STACK_REUSE Reuse Heat stack (don't create one)
22 * OPENSTACK_API_URL OpenStack API address
23 * OPENSTACK_API_CREDENTIALS Credentials to the OpenStack API
24 * OPENSTACK_API_PROJECT OpenStack project to connect to
25 * OPENSTACK_API_CLIENT Versions of OpenStack python clients
26 * OPENSTACK_API_VERSION Version of the OpenStack API (2/3)
27 *
28 * SALT_MASTER_CREDENTIALS Credentials to the Salt API
29 *
30 * required for STACK_TYPE=NONE or empty string
31 * SALT_MASTER_URL URL of Salt-API
32
33 * K8S_API_SERVER Kubernetes API address
34 * K8S_CONFORMANCE_IMAGE Path to docker image with conformance e2e tests
35 *
Victor Ryzhenkin5f3b7e62017-03-09 16:02:58 +040036 * TEMPEST_IMAGE_LINK Tempest image link
37 *
Matthew Mosesohna85f24e2017-04-28 13:45:19 +030038 * optional parameters for overwriting soft params
39 * KUBERNETES_HYPERKUBE_IMAGE Docker repository and tag for hyperkube image
Matthew Mosesohnd00a8f52017-05-22 17:07:19 +030040 * CALICO_CNI_IMAGE Docker repository and tag for calico CNI image
41 * CALICO_NODE_IMAGE Docker repository and tag for calico node image
42 * CALICOCTL_IMAGE Docker repository and tag for calicoctl image
Matthew Mosesohna85f24e2017-04-28 13:45:19 +030043 *
Tomáš Kukrálb9957b32017-02-28 14:49:00 +010044 */
45
Tomáš Kukrál1f8b5012017-04-28 21:07:10 +020046common = new com.mirantis.mk.Common()
Tomáš Kukrálb9957b32017-02-28 14:49:00 +010047git = new com.mirantis.mk.Git()
48openstack = new com.mirantis.mk.Openstack()
Tomáš Kukrál1f8b5012017-04-28 21:07:10 +020049orchestrate = new com.mirantis.mk.Orchestrate()
Tomáš Kukrálb9957b32017-02-28 14:49:00 +010050salt = new com.mirantis.mk.Salt()
Ondrej Smola8f35e482017-03-30 14:04:36 +020051test = new com.mirantis.mk.Test()
Tomáš Kukrál1f8b5012017-04-28 21:07:10 +020052
Tomáš Kukrálab2f3702017-05-11 09:17:43 +020053_MAX_PERMITTED_STACKS = 2
Matthew Mosesohna85f24e2017-04-28 13:45:19 +030054overwriteFile = "/srv/salt/reclass/classes/cluster/overwrite.yml"
Tomáš Kukrále80680a2017-03-02 16:34:35 +010055
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +010056timestamps {
57 node {
Tomáš Kukrála18de112017-03-02 13:57:47 +010058 try {
59 //
60 // Prepare machines
61 //
Tomáš Kukrála18de112017-03-02 13:57:47 +010062 stage ('Create infrastructure') {
Tomáš Kukrál6d627d62017-03-23 17:39:07 +010063
Tomáš Kukrála18de112017-03-02 13:57:47 +010064 if (STACK_TYPE == 'heat') {
65 // value defaults
66 def openstackCloud
67 def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
68 def openstackEnv = "${env.WORKSPACE}/venv"
Tomáš Kukrálb9957b32017-02-28 14:49:00 +010069
Filip Pytloun794ad952017-03-03 10:39:26 +010070 if (HEAT_STACK_REUSE.toBoolean() == true && HEAT_STACK_NAME == '') {
71 error("If you want to reuse existing stack you need to provide it's name")
72 }
73
74 if (HEAT_STACK_REUSE.toBoolean() == false) {
75 // Don't allow to set custom heat stack name
76 wrap([$class: 'BuildUser']) {
Tomáš Kukrál24d7fe62017-03-03 10:57:11 +010077 if (env.BUILD_USER_ID) {
78 HEAT_STACK_NAME = "${env.BUILD_USER_ID}-${JOB_NAME}-${BUILD_NUMBER}"
79 } else {
80 HEAT_STACK_NAME = "jenkins-${JOB_NAME}-${BUILD_NUMBER}"
81 }
Filip Pytloun794ad952017-03-03 10:39:26 +010082 currentBuild.description = HEAT_STACK_NAME
83 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +010084 }
Tomáš Kukrála18de112017-03-02 13:57:47 +010085
Tomáš Kukrálcbabec42017-03-02 16:24:04 +010086 // set description
Tomáš Kukrálaeb5c922017-03-02 17:00:48 +010087 currentBuild.description = "${HEAT_STACK_NAME}"
Tomáš Kukrálcbabec42017-03-02 16:24:04 +010088
Tomáš Kukrála18de112017-03-02 13:57:47 +010089 // get templates
90 git.checkoutGitRepository('template', HEAT_TEMPLATE_URL, HEAT_TEMPLATE_BRANCH, HEAT_TEMPLATE_CREDENTIALS)
91
92 // create openstack env
93 openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion)
94 openstackCloud = openstack.createOpenstackEnv(OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS, OPENSTACK_API_PROJECT)
95 openstack.getKeystoneToken(openstackCloud, openstackEnv)
Jakub Josef458913d2017-05-10 15:37:56 +020096 //
97 // Verify possibility of create stack for given user and stack type
98 //
99 wrap([$class: 'BuildUser']) {
Tomáš Kukrálab2f3702017-05-11 09:17:43 +0200100 if (env.BUILD_USER_ID && !env.BUILD_USER_ID.equals("jenkins") && !HEAT_STACK_REUSE.toBoolean()) {
Jakub Josef78c3f8b2017-05-10 15:45:29 +0200101 def existingStacks = openstack.getStacksForNameContains(openstackCloud, "${env.BUILD_USER_ID}-${JOB_NAME}", openstackEnv)
102 if(existingStacks.size() >= _MAX_PERMITTED_STACKS){
Jakub Josef124403a2017-05-10 15:58:06 +0200103 HEAT_STACK_DELETE = "false"
Jakub Josef78c3f8b2017-05-10 15:45:29 +0200104 throw new Exception("You cannot create new stack, you already have ${_MAX_PERMITTED_STACKS} stacks of this type (${JOB_NAME}). \nStack names: ${existingStacks}")
105 }
Jakub Josef458913d2017-05-10 15:37:56 +0200106 }
107 }
Tomáš Kukrála18de112017-03-02 13:57:47 +0100108 // launch stack
Filip Pytloun794ad952017-03-03 10:39:26 +0100109 if (HEAT_STACK_REUSE.toBoolean() == false) {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100110 stage('Launch new Heat stack') {
111 // create stack
112 envParams = [
113 'instance_zone': HEAT_STACK_ZONE,
114 'public_net': HEAT_STACK_PUBLIC_NET
115 ]
116 openstack.createHeatStack(openstackCloud, HEAT_STACK_NAME, HEAT_STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, openstackEnv)
117 }
118 }
119
120 // get SALT_MASTER_URL
121 saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, HEAT_STACK_NAME, 'salt_master_ip', openstackEnv)
Tomáš Kukrálaeb5c922017-03-02 17:00:48 +0100122 currentBuild.description = "${HEAT_STACK_NAME}: ${saltMasterHost}"
Tomáš Kukrál615aa9c2017-03-04 15:29:08 +0100123
Ales Komarek47a29f12017-04-26 12:05:47 +0200124 SALT_MASTER_URL = "http://${saltMasterHost}:6969"
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100125 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100126 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100127
Tomáš Kukrála18de112017-03-02 13:57:47 +0100128 //
129 // Connect to Salt master
130 //
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100131
Tomáš Kukrála18de112017-03-02 13:57:47 +0100132 def saltMaster
133 stage('Connect to Salt API') {
134 saltMaster = salt.connection(SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
135 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100136
Tomáš Kukrála18de112017-03-02 13:57:47 +0100137 //
138 // Install
139 //
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100140
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100141 if (common.checkContains('INSTALL', 'core')) {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100142 stage('Install core infrastructure') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200143 orchestrate.installFoundationInfra(saltMaster)
Tomáš Kukrálc265e352017-03-02 11:45:11 +0100144
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100145 if (common.checkContains('INSTALL', 'kvm')) {
Ales Komarek47a29f12017-04-26 12:05:47 +0200146 orchestrate.installInfraKvm(saltMaster)
147 orchestrate.installFoundationInfra(saltMaster)
Tomáš Kukrála18de112017-03-02 13:57:47 +0100148 }
149
Ales Komarek47a29f12017-04-26 12:05:47 +0200150 orchestrate.validateFoundationInfra(saltMaster)
Tomáš Kukrála18de112017-03-02 13:57:47 +0100151 }
152 }
153
154 // install k8s
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100155 if (common.checkContains('INSTALL', 'k8s')) {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100156 stage('Install Kubernetes infra') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200157 orchestrate.installKubernetesInfra(saltMaster)
Tomáš Kukrálc265e352017-03-02 11:45:11 +0100158 }
159
Tomáš Kukrála18de112017-03-02 13:57:47 +0100160 stage('Install Kubernetes control') {
Tomáš Kukrál06c27a92017-03-01 16:21:46 +0100161
Ales Komarek47a29f12017-04-26 12:05:47 +0200162 // Overwrite Kubernetes vars if specified
163 if (env.getEnvironment().containsKey("KUBERNETES_HYPERKUBE_IMAGE")) {
Tomáš Kukrálfbb98322017-05-02 10:38:05 +0200164 salt.runSaltProcessStep(saltMaster, 'I@salt:master', 'file.append', overwriteFile, " kubernetes_hyperkube_image: ${KUBERNETES_HYPERKUBE_IMAGE}")
Ales Komarek47a29f12017-04-26 12:05:47 +0200165 }
Matthew Mosesohnd00a8f52017-05-22 17:07:19 +0300166 // Overwrite Calico vars if specified
167 if (env.getEnvironment().containsKey("CALICO_CNI_IMAGE")) {
168 salt.runSaltProcessStep(saltmaster, 'I@salt:master', 'file.append', overwriteFile, " kubernetes_calico_cni_image: ${CALICO_CNI_IMAGE}")
169 }
170 if (env.getEnvironment().containsKey("CALICO_NODE_IMAGE")) {
171 salt.runSaltProcessStep(saltmaster, 'I@salt:master', 'file.append', overwriteFile, " kubernetes_calico_node_image: ${CALICO_NODE_IMAGE}")
172 }
173 if (env.getEnvironment().containsKey("CALICOCTL_IMAGE")) {
174 salt.runSaltProcessStep(saltmaster, 'I@salt:master', 'file.append', overwriteFile, " kubernetes_calicoctl_image: ${CALICOCTL_IMAGE}")
175 }
Tomáš Kukrálc265e352017-03-02 11:45:11 +0100176
Ales Komarek47a29f12017-04-26 12:05:47 +0200177 orchestrate.installKubernetesControl(saltMaster)
Tomáš Kukrálc265e352017-03-02 11:45:11 +0100178 }
Tomáš Kukrálfeb959e2017-05-18 11:00:41 +0200179
180
181 if (common.checkContains('INSTALL', 'contrail')) {
182 state('Install Contrail for Kubernetes') {
183 orchestrate.installContrailNetwork(saltMaster)
184 orchestrate.installContrailCompute(saltMaster)
185 }
186 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100187 }
188
Tomáš Kukrála18de112017-03-02 13:57:47 +0100189 // install openstack
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100190 if (common.checkContains('INSTALL', 'openstack')) {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100191 // install Infra and control, tests, ...
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100192
Tomáš Kukrála18de112017-03-02 13:57:47 +0100193 stage('Install OpenStack infra') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200194 orchestrate.installOpenstackInfra(saltMaster)
Tomáš Kukrálc265e352017-03-02 11:45:11 +0100195 }
196
Tomáš Kukrála18de112017-03-02 13:57:47 +0100197 stage('Install OpenStack control') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200198 orchestrate.installOpenstackControl(saltMaster)
Tomáš Kukrála18de112017-03-02 13:57:47 +0100199 }
200
201 stage('Install OpenStack network') {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100202
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100203 if (common.checkContains('INSTALL', 'contrail')) {
Ales Komarek47a29f12017-04-26 12:05:47 +0200204 orchestrate.installContrailNetwork(saltMaster)
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100205 } else if (common.checkContains('INSTALL', 'ovs')) {
Ales Komarek47a29f12017-04-26 12:05:47 +0200206 orchestrate.installOpenstackNetwork(saltMaster)
Tomáš Kukrála18de112017-03-02 13:57:47 +0100207 }
208
Tomáš Kukrálfbb98322017-05-02 10:38:05 +0200209 salt.runSaltProcessStep(saltMaster, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; neutron net-list'])
210 salt.runSaltProcessStep(saltMaster, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; nova net-list'])
Tomáš Kukrála18de112017-03-02 13:57:47 +0100211 }
212
213 stage('Install OpenStack compute') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200214 orchestrate.installOpenstackCompute(saltMaster)
Tomáš Kukrála18de112017-03-02 13:57:47 +0100215
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100216 if (common.checkContains('INSTALL', 'contrail')) {
Ales Komarek47a29f12017-04-26 12:05:47 +0200217 orchestrate.installContrailCompute(saltMaster)
Tomáš Kukráld5a40af2017-03-07 15:34:58 +0100218 }
Tomáš Kukrála18de112017-03-02 13:57:47 +0100219 }
Tomáš Kukrál2b4c1282017-03-03 17:25:26 +0100220
Tomáš Kukrál00ed1302017-03-03 17:38:40 +0100221 }
Tomáš Kukrál2b4c1282017-03-03 17:25:26 +0100222
Tomáš Kukrál00ed1302017-03-03 17:38:40 +0100223
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100224 if (common.checkContains('INSTALL', 'stacklight')) {
Tomáš Kukrál2b4c1282017-03-03 17:25:26 +0100225 stage('Install StackLight') {
Ales Komarek47a29f12017-04-26 12:05:47 +0200226 orchestrate.installStacklightControl(saltMaster)
227 orchestrate.installStacklightClient(saltMaster)
Tomáš Kukrál2b4c1282017-03-03 17:25:26 +0100228 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100229 }
230
Tomáš Kukrála18de112017-03-02 13:57:47 +0100231 //
232 // Test
233 //
Tomáš Kukrál695d6462017-04-21 16:31:52 +0200234 def artifacts_dir = '_artifacts/'
Tomáš Kukrála18de112017-03-02 13:57:47 +0100235
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100236 if (common.checkContains('TEST', 'k8s')) {
Tomáš Kukrála18de112017-03-02 13:57:47 +0100237 stage('Run k8s bootstrap tests') {
Tomáš Kukrál695d6462017-04-21 16:31:52 +0200238 def image = 'tomkukral/k8s-scripts'
Tomáš Kukrál44ca7fe2017-04-21 16:44:19 +0200239 def output_file = image.replaceAll('/', '-') + '.output'
Tomáš Kukrál695d6462017-04-21 16:31:52 +0200240
Tomáš Kukrálb22826d2017-04-21 16:38:00 +0200241 // run image
242 test.runConformanceTests(saltMaster, K8S_API_SERVER, image)
Tomáš Kukrálf56beed2017-04-21 16:21:06 +0200243
244 // collect output
Tomáš Kukrálf56beed2017-04-21 16:21:06 +0200245 sh "mkdir -p ${artifacts_dir}"
Tomáš Kukrálb22826d2017-04-21 16:38:00 +0200246 file_content = salt.getFileContent(saltMaster, 'ctl01*', '/tmp/' + output_file)
Tomáš Kukrálf56beed2017-04-21 16:21:06 +0200247 writeFile file: "${artifacts_dir}${output_file}", text: file_content
Tomáš Kukrálf56beed2017-04-21 16:21:06 +0200248 sh "cat ${artifacts_dir}${output_file}"
249
250 // collect artifacts
251 archiveArtifacts artifacts: "${artifacts_dir}${output_file}"
Tomáš Kukrála18de112017-03-02 13:57:47 +0100252 }
253
Tomáš Kukrálb22826d2017-04-21 16:38:00 +0200254 stage('Run k8s conformance e2e tests') {
255 //test.runConformanceTests(saltMaster, K8S_API_SERVER, K8S_CONFORMANCE_IMAGE)
256
257 def image = K8S_CONFORMANCE_IMAGE
Tomáš Kukrál44ca7fe2017-04-21 16:44:19 +0200258 def output_file = image.replaceAll('/', '-') + '.output'
Tomáš Kukrálb22826d2017-04-21 16:38:00 +0200259
260 // run image
261 test.runConformanceTests(saltMaster, K8S_API_SERVER, image)
262
263 // collect output
264 sh "mkdir -p ${artifacts_dir}"
265 file_content = salt.getFileContent(saltMaster, 'ctl01*', '/tmp/' + output_file)
266 writeFile file: "${artifacts_dir}${output_file}", text: file_content
267 sh "cat ${artifacts_dir}${output_file}"
268
269 // collect artifacts
270 archiveArtifacts artifacts: "${artifacts_dir}${output_file}"
271 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100272 }
Tomáš Kukrálb9957b32017-02-28 14:49:00 +0100273
Tomáš Kukrál6d627d62017-03-23 17:39:07 +0100274 if (common.checkContains('TEST', 'openstack')) {
Victor Ryzhenkin5f3b7e62017-03-09 16:02:58 +0400275 stage('Run OpenStack tests') {
276 test.runTempestTests(saltMaster, TEMPEST_IMAGE_LINK)
277 }
278
279 stage('Copy Tempest results to config node') {
280 test.copyTempestResults(saltMaster)
281 }
282 }
283
Tomáš Kukrál420d7ff2017-03-21 11:38:33 +0100284 stage('Finalize') {
Tomáš Kukrál38ab6bb2017-04-21 21:29:31 +0200285 if (INSTALL != '') {
286 try {
287 salt.runSaltProcessStep(saltMaster, '*', 'state.apply', [], null, true)
288 } catch (Exception e) {
289 common.warningMsg('State apply failed but we should continue to run')
290 }
Tomáš Kukrál420d7ff2017-03-21 11:38:33 +0100291 }
292 }
Tomáš Kukrála18de112017-03-02 13:57:47 +0100293 } catch (Throwable e) {
294 currentBuild.result = 'FAILURE'
295 throw e
Tomáš Kukrál65fb5fd2017-03-02 14:56:49 +0100296 } finally {
Tomáš Kukrálb9957b32017-02-28 14:49:00 +0100297
Tomáš Kukrál96b12d52017-03-07 22:43:37 +0100298
Tomáš Kukrála18de112017-03-02 13:57:47 +0100299 //
300 // Clean
301 //
302
Tomáš Kukrálc9a630a2017-03-07 17:28:53 +0100303 if (STACK_TYPE == 'heat') {
Tomáš Kukrál4507c6a2017-03-23 19:00:39 +0100304 // send notification
305 common.sendNotification(currentBuild.result, HEAT_STACK_NAME, ["slack"])
306
Tomáš Kukrálc9a630a2017-03-07 17:28:53 +0100307 if (HEAT_STACK_DELETE.toBoolean() == true) {
308 common.errorMsg('Heat job cleanup triggered')
309 stage('Trigger cleanup job') {
310 build job: 'deploy-heat-cleanup', parameters: [[$class: 'StringParameterValue', name: 'HEAT_STACK_NAME', value: HEAT_STACK_NAME]]
311 }
312 } else {
Tomáš Kukrálbcc53ea2017-03-08 08:15:57 +0100313 if (currentBuild.result == 'FAILURE') {
314 common.errorMsg("Deploy job FAILED and was not deleted. Please fix the problem and delete stack on you own.")
Tomáš Kukrálc9a630a2017-03-07 17:28:53 +0100315
Tomáš Kukrálbcc53ea2017-03-08 08:15:57 +0100316 if (SALT_MASTER_URL) {
317 common.errorMsg("Salt master URL: ${SALT_MASTER_URL}")
318 }
Tomáš Kukrálb908dac2017-03-07 18:58:35 +0100319 }
320
Tomáš Kukrála18de112017-03-02 13:57:47 +0100321 }
Tomáš Kukrál9e0fb732017-03-02 10:12:05 +0100322 }
Tomáš Kukrálb9957b32017-02-28 14:49:00 +0100323 }
324 }
325}