blob: 3803e616684bf896fe387e81129744ef76133c4b [file] [log] [blame]
Jakub Josef6ee6f992017-01-27 16:16:04 +01001import java.util.regex.Pattern
2/**
3 *
Jakub Joseffc2f2412017-02-27 15:14:07 +01004 * OS images build pipeline
Jakub Josef6ee6f992017-01-27 16:16:04 +01005 *
6 * Expected parameters:
7 * BUILD_OS
8 * BUILD_ONLY
9 * PACKER_DEBUG
10 * PACKER_URL
11 * PACKER_ZIP
12 * PACKER_ZIP_MD5
13 * PACKER_ARGS
14 * UPLOAD_URL
15 * SKIP_UPLOAD
16 * CLEANUP_OLD
17 * CLEANUP_KEEP
18 * PIPELINE_LIBS_URL
19 * PIPELINE_LIBS_BRANCH
20 * PIPELINE_LIBS_CREDENTIALS_ID
Jakub Joseffc2f2412017-02-27 15:14:07 +010021 * GLANCE_UPLOAD
22 * GLANCE_IMG_TYPES
23 * GLANCE_URL
24 * GLANCE_CREDENTIALS_ID
25 * GLANCE_PROJECT
26 * GLANCE_ARGS
27 * OPENSTACK_API_CLIENT
Jakub Josef6ee6f992017-01-27 16:16:04 +010028 */
29
Jakub Josef6ee6f992017-01-27 16:16:04 +010030// Load shared libs
Jakub Joseffc2f2412017-02-27 15:14:07 +010031common = new com.mirantis.mk.Common()
Jakub Josef6ee6f992017-01-27 16:16:04 +010032
33node('qemu') {
34 // Define global variables
35 def workspace = common.getWorkspace()
36 def buildTypes = BUILD_ONLY.tokenize(" ")
Jakub Joseffc2f2412017-02-27 15:14:07 +010037 def createdImages=[]
38 def uploadedImages=[]
39 def cleanedImages=[]
40
Jakub Josef6ee6f992017-01-27 16:16:04 +010041 checkout scm
42 try {
43 stage("prepare") {
44 if (!fileExists("${workspace}/tmp")) {
45 sh "mkdir -p ${workspace}/tmp"
46 }
47 if (!fileExists("${workspace}/images")) {
48 sh "mkdir ${workspace}/images"
49 }
50 }
51 if (!fileExists("bin")) {
52 println("Downloading packer")
53 sh "mkdir bin"
54 dir("bin") {
55 sh "wget -O ${PACKER_ZIP} ${PACKER_URL}"
56 sh "echo \"${PACKER_ZIP_MD5} ${PACKER_ZIP}\" >> md5sum"
57 sh "md5sum -c --status md5sum"
58 sh "unzip ${PACKER_ZIP}"
59 }
60 }
Jakub Joseffc2f2412017-02-27 15:14:07 +010061 // clean images dir before building
62 sh(script: String.format("rm -rf %s/images/*", BUILD_OS), returnStatus: true)
63 // clean virtualenv is exists
64 sh(script: String.format("rm -rf %s/venv", workspace), returnStatus: true)
65
Jakub Josef6ee6f992017-01-27 16:16:04 +010066 stage("build") {
67 dir(BUILD_OS) {
68 withEnv([String.format("PATH=%s:%s/bin", env.PATH, workspace),
69 "PACKER_LOG_PATH=${workspace}/packer.log",
70 "PACKER_LOG=1",
71 "TMPDIR=${workspace}/tmp"
72 ]) {
73 if (PACKER_DEBUG == 'true') {
74 PACKER_ARGS = "${PACKER_ARGS} -debug"
75 }
Filip Pytloun35640b62017-02-23 09:45:34 +010076
77 wrap([$class: 'AnsiColorBuildWrapper']) {
78 sh "packer build -only=${BUILD_ONLY} ${PACKER_ARGS} -parallel=false template.json"
79 }
80
Jakub Josef6ee6f992017-01-27 16:16:04 +010081 def packerStatus = sh(script: "grep \"Some builds didn't complete successfully and had errors\" ${PACKER_LOG_PATH}", returnStatus: true)
82 // grep returns 0 if find something
83 if (packerStatus != 0) {
84 if (buildTypes.contains("qemu")) {
85 def imageQemu = sh(script: "find images/ | grep -- '-qemu-' | tail -1", returnStdout: true).trim()
86 if (imageQemu != null && imageQemu != "") {
87 def qemuConvertStatus = sh(script: "qemu-img convert -c -O qcow2 ${imageQemu} ${imageQemu}.qcow2", returnStatus:true)
88 if(qemuConvertStatus == 0){
Jakub Joseffc2f2412017-02-27 15:14:07 +010089 def imageDir = imageQemu.substring(0, imageQemu.lastIndexOf("/") + 1)
90 def imageQemuName = imageQemu.substring(imageQemu.lastIndexOf("/") + 1)
Jakub Josefc6bcfd72017-02-14 18:14:28 +010091 def moveResult = sh(script: "mv ${imageQemu}.qcow2 ${imageDir}..", returnStatus: true)
92 if(moveResult == 0){
93 sh "rm -rf ${imageDir}"
94 sh "rm -f ${imageQemu}"
Jakub Joseffc2f2412017-02-27 15:14:07 +010095 createdImages.add(imageQemuName+".qcow2")
Jakub Josefc6bcfd72017-02-14 18:14:28 +010096 }
Jakub Josef6ee6f992017-01-27 16:16:04 +010097 }else{
98 throw new Exception("Qemu image convert failed")
99 }
100 }
101 }
102 if (buildTypes.contains("docker")) {
103 def imageDocker = sh(script: "find images/ | grep -- '-docker-' | grep '.tar\$' | tail -1", returnStdout: true).trim()
104 if (imageDocker != null && imageDocker != "") {
105 def pbZip2Status = sh(script: "pbzip2 ${imageDocker}", returnStatus: true)
106 if(pbZip2Status == 0){
107 sh "rm -f ${imageDocker}"
Jakub Joseffc2f2412017-02-27 15:14:07 +0100108 createdImages.add(imageDocker+".bz2")
Jakub Josef6ee6f992017-01-27 16:16:04 +0100109 }else{
110 throw new Exception("pbzip2 image convert failed")
111 }
112 }
113 }
114 } else {
115 throw new Exception("Packer build failed")
116 }
117 }
118 }
119 }
120 stage("upload"){
121 dir(BUILD_OS + "/images") {
122 def images = findFiles(glob: "*.*")
Jakub Josef6ee6f992017-01-27 16:16:04 +0100123 def imageBuilds = [:]
Jakub Joseffc2f2412017-02-27 15:14:07 +0100124 def openstack = new com.mirantis.mk.Openstack()
125 def openstackEnv = String.format("%s/venv", workspace);
126 def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
127 def rcFile = openstack.createOpenstackEnv(GLANCE_URL, GLANCE_CREDENTIALS_ID, GLANCE_PROJECT)
128 def glanceImgTypes = GLANCE_IMG_TYPES.tokenize(" ")
129 openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion)
130 openstack.runOpenstackCommand("pip install python-glanceclient==1.0.0", rcFile, openstackEnv)
Jakub Josef6ee6f992017-01-27 16:16:04 +0100131 for (int i = 0; i < images.size(); i++) {
132 def imageName = images[i].name
133 def imageNameList = imageName.tokenize(".")
134 def imageType = "." + imageNameList[imageNameList.size() - 1]
135 if(imageType.equals(".md5")){
136 continue;
137 }
Jakub Joseffc2f2412017-02-27 15:14:07 +0100138
Jakub Josef6ee6f992017-01-27 16:16:04 +0100139 imageBuilds["build${i}"]={
140 if (SKIP_UPLOAD != 'true') {
141 sh "md5sum ${imageName} > ${imageName}.md5"
142 println("Uploading image " + imageName)
143 def uploadImageStatus = sh(script: "curl -f -T ${imageName} ${UPLOAD_URL}", returnStatus: true)
144 def uploadMd5Status = sh(script: "curl -f -T ${imageName}.md5 ${UPLOAD_URL}", returnStatus: true)
Jakub Joseffc2f2412017-02-27 15:14:07 +0100145
146 if (GLANCE_UPLOAD == 'true' && glanceImgTypes.contains(imageType.substring(1))) {
147 def glanceRunArgs = String.format("%s --disk-format %s --container-format bare", GLANCE_ARGS, imageType.substring(1))
148 if (GLANCE_PUBLIC == 'true') {
149 glanceRunArgs += " --visibility public"
150 }
151
152 def imageShortName = imageNameList.get(0)
153 openstack.runOpenstackCommand(String.format("glance image-create --name '%s' %s --file %s", imageShortName, glanceRunArgs, imageName), rcFile, openstackEnv)
154 }
Jakub Josef6ee6f992017-01-27 16:16:04 +0100155 if(uploadImageStatus==0 && uploadMd5Status == 0){
156 uploadedImages.add(imageName)
Jakub Josefc6bcfd72017-02-14 18:14:28 +0100157 sh(String.format("rm -r %s %s.md5",imageName, imageName))
Jakub Joseffc2f2412017-02-27 15:14:07 +0100158 createdImages.remove(imageName)
Jakub Josef6ee6f992017-01-27 16:16:04 +0100159 }else{
160 throw new Exception("Image upload failed")
161 }
162 }
163 if (CLEANUP_OLD == 'true') {
164 def remoteImages = sh(script: "curl -f -sss ${UPLOAD_URL} | grep -Eo '>.*\\.(qcow2|box|tar\\.bz2)</a>' | sed -e 's,>,,g' -e 's,</a,,g'", returnStdout: true)
165 if (remoteImages != "") {
166 def cleanupImages = getCleanupImageList(remoteImages, imageType, BUILD_OS)
167 def deleteCount = cleanupImages.size() - Integer.parseInt(CLEANUP_KEEP)
168 if (deleteCount > 0) {
169 for (int j = 0; j < deleteCount; j++) {
170 println(String.format("Deleting image %s from aptly", cleanupImages[j]))
Jakub Josefc6bcfd72017-02-14 18:14:28 +0100171 sh "curl -f -X DELETE ${UPLOAD_URL}" + cleanupImages[j]
172 sh "curl -f -X DELETE ${UPLOAD_URL}" + cleanupImages[j] + ".md5"
Jakub Josef6ee6f992017-01-27 16:16:04 +0100173 cleanedImages.add(cleanupImages[j])
174 }
175 }
176 }
177 }
178 }
179 }
180 parallel imageBuilds
181 println(String.format("Uploaded %s images with names %s", uploadedImages.size(), uploadedImages.toString()))
182 println(String.format("Cleaned %s images with names %s", cleanedImages.size(), cleanedImages.toString()))
183 }
184 }
185 } catch (Throwable e) {
186 // If there was an error or exception thrown, the build failed
187 currentBuild.result = "FAILURE"
188 throw e
189 } finally {
190 common.sendNotification(currentBuild.result, "", ["slack"])
191 if (buildTypes.contains("docker")) {
192 withEnv(["PACKER_LOG_PATH=${workspace}/packer.log"]) {
193 sh "docker rmi --force \$(grep \"docker: Image ID:\" ${PACKER_LOG_PATH} | cut -d : -f 6 | head -1 | sed s,\\ ,,g) || true"
194 }
195 }
Jakub Joseffc2f2412017-02-27 15:14:07 +0100196 // clean created images if error occured
197 if(!createdImages.isEmpty()){
198 dir(BUILD_OS + "/images"){
199 for(int i=0;i<createdImages.size();i++){
200 sh String.format("rm -f %s",createdImages.get(i))
201 }
202 }
203 }
Jakub Josef6ee6f992017-01-27 16:16:04 +0100204 }
205}
206
207@NonCPS
208def getCleanupImageList(remoteImagesString, imageType, osImage) {
209 def remoteImages = remoteImagesString.tokenize("\n")
210 def imageTypeForRegex = Pattern.quote(imageType)
Jakub Josefc6bcfd72017-02-14 18:14:28 +0100211 def osImageForRegex = Pattern.quote(osImage.replaceAll(/\./,"-"))
212 def remoteImagesSameType = remoteImages.findAll { it ->
213 it =~ /${imageTypeForRegex}$/
Jakub Josef6ee6f992017-01-27 16:16:04 +0100214 }
Jakub Josefc6bcfd72017-02-14 18:14:28 +0100215 return remoteImagesSameType.toSorted().findAll { it ->
216 it =~ /^${osImageForRegex}-/
Jakub Josef6ee6f992017-01-27 16:16:04 +0100217 }
Filip Pytloun35640b62017-02-23 09:45:34 +0100218}