azvyagintsev | 8778188 | 2018-08-30 18:45:22 +0300 | [diff] [blame] | 1 | /** |
| 2 | * |
| 3 | * Promote VCP(qcow2) images |
| 4 | * |
| 5 | * Expected parameters: |
| 6 | * VCP_IMAGE_LIST - multiline with qcow2 file names |
| 7 | * TAG - Target tag of image.Possible are: "nightly|testing|proposed|201X.X.X" |
| 8 | * SOURCE_TAG - Initial tag to be tagged with TAG. Will replace SUBS_SOURCE_VCP_IMAGE_TAG in VCP_IMAGE_LIST |
| 9 | * UPLOAD_URL - WebDav url with creds, from\to download images |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | def common = new com.mirantis.mk.Common() |
| 14 | def jenkinsUtils = new com.mirantis.mk.JenkinsUtils() |
| 15 | |
| 16 | // Better to chose slave with ssd and fast network to webDav host |
Kirill Mashchenko | 25363e8 | 2018-11-22 14:16:47 +0400 | [diff] [blame] | 17 | slaveNode = env.SLAVE_NODE ?: 'promotion' |
azvyagintsev | 8778188 | 2018-08-30 18:45:22 +0300 | [diff] [blame] | 18 | def job_env = env.getEnvironment().findAll { k, v -> v } |
| 19 | def verify = job_env.VERIFY_DOWNLOAD ?: true |
azvyagintsev | ff65d40 | 2018-10-02 18:37:42 +0300 | [diff] [blame] | 20 | def overwrite = job_env.FORCE_OVERWRITE.toBoolean() ?: false |
| 21 | |
azvyagintsev | 8778188 | 2018-08-30 18:45:22 +0300 | [diff] [blame] | 22 | |
| 23 | |
| 24 | timeout(time: 6, unit: 'HOURS') { |
| 25 | node(slaveNode) { |
| 26 | |
| 27 | String description = '' |
| 28 | insufficientPermissions = false |
| 29 | try { |
| 30 | // Pre-run verify |
| 31 | // promote is restricted to users in aptly-promote-users LDAP group |
| 32 | if (!jenkinsUtils.currentUserInGroups(["mcp-cicd-admins", "aptly-promote-users"])) { |
| 33 | insufficientPermissions = true |
| 34 | error(String.format("You don't have permissions to make promote from source:%s to target:%s! Only CI/CD and QA team can perform promote.", job_env.SOURCE_TAG, job_env.TAG)) |
| 35 | } |
| 36 | // Check for required opts |
| 37 | for (opt in ['UPLOAD_URL', 'SOURCE_TAG', 'TAG', 'VCP_IMAGE_LIST']) { |
| 38 | if (!job_env.get(opt, null)) { |
| 39 | error("Invalid input params, at least ${opt} param missing") |
| 40 | } |
| 41 | } |
| 42 | def images = job_env.VCP_IMAGE_LIST.trim().tokenize() |
| 43 | for (image in images) { |
| 44 | if (image.startsWith('#')) { |
| 45 | common.warningMsg("Skipping image ${image}") |
| 46 | continue |
| 47 | } |
| 48 | common.infoMsg("Replacing SUBS_SOURCE_VCP_IMAGE_TAG => ${job_env.SOURCE_TAG}") |
| 49 | sourceImage = image.replace('SUBS_SOURCE_VCP_IMAGE_TAG', job_env.SOURCE_TAG) |
| 50 | targetImage = image.replace('SUBS_SOURCE_VCP_IMAGE_TAG', job_env.TAG) |
| 51 | |
| 52 | // TODO: normalize url's? |
| 53 | sourceImageUrl = job_env.UPLOAD_URL + '/' + sourceImage |
| 54 | sourceImageMd5Url = job_env.UPLOAD_URL + '/' + sourceImage + '.md5' |
| 55 | targetImageUrl = job_env.UPLOAD_URL + '/' + targetImage |
| 56 | targetImageMd5Url = job_env.UPLOAD_URL + '/' + targetImage + '.md5' |
| 57 | |
| 58 | common.infoMsg("Attempt to download: ${sourceImage} => ${targetImage}") |
| 59 | common.retry(3, 5) { |
| 60 | sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage} ${sourceImageUrl}") |
| 61 | } |
| 62 | def targetImageMd5 = common.cutOrDie("md5sum ${targetImage} | tee ${targetImage}.md5", 0) |
| 63 | if (verify.toBoolean()) { |
| 64 | common.infoMsg("Checking md5's ") |
| 65 | sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage}_source_md5 ${sourceImageMd5Url}") |
| 66 | def sourceImageMd5 = readFile(file: "${targetImage}_source_md5").tokenize(' ')[0] |
| 67 | // Compare downloaded and remote files |
| 68 | if (sourceImageMd5 != targetImageMd5) { |
| 69 | error("Image ${targetImage} md5sum verify failed!") |
| 70 | } else { |
| 71 | common.infoMsg("sourceImageMd5: ${sourceImageMd5} == target to upload ImageMd5: ${targetImageMd5}") |
| 72 | } |
| 73 | // Compare downloaded file, and remote file-to-be-promoted. If same - no sense to promote same file |
| 74 | remoteImageMd5Status = sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage}_expected_target_md5 ${targetImageMd5Url}", returnStatus: true) |
| 75 | if (remoteImageMd5Status == '8') { |
| 76 | common.infoMsg("target to upload ImageMd5 file not even exist.Continue..") |
| 77 | } else { |
| 78 | def remoteImageMd5 = readFile(file: "${targetImage}_expected_target_md5").tokenize(' ')[0] |
| 79 | if (sourceImageMd5 == remoteImageMd5) { |
| 80 | common.infoMsg("sourceImageMd5: ${sourceImageMd5} and target to upload ImageMd5: ${targetImageMd5} are same") |
| 81 | common.warningMsg("Skipping to upload: ${targetImage} since it already same") |
| 82 | description += "Skipping to upload: ${targetImage} since it already same\n" |
| 83 | continue |
| 84 | } |
| 85 | } |
| 86 | common.infoMsg("Check, that we are not going to overwrite released file..") |
| 87 | if (['proposed', 'testing', 'nightly'].contains(job_env.TAG)) { |
| 88 | common.infoMsg("Uploading to ${job_env.TAG} looks safe..") |
| 89 | } else if (['stable'].contains(job_env.TAG)) { |
| 90 | common.warningMsg("Uploading to ${job_env.TAG} not safe! But still possible") |
| 91 | } else { |
| 92 | common.warningMsg("Looks like uploading to new release: ${job_env.TAG}. Checking, that it is not exist yet..") |
| 93 | remoteImageStatus = '' |
| 94 | remoteImageStatus = sh(script: "wget --auth-no-challenge --spider ${targetImageUrl} 2>/dev/null", returnStatus: true) |
| 95 | // wget return code 8 ,if file not exist |
azvyagintsev | ff65d40 | 2018-10-02 18:37:42 +0300 | [diff] [blame] | 96 | if (remoteImageStatus != 8 && !overwrite) { |
azvyagintsev | 8778188 | 2018-08-30 18:45:22 +0300 | [diff] [blame] | 97 | error("Attempt to overwrite existing release! Target: ${targetImage} already exist!") |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | common.infoMsg("Attempt to UPLOAD: ${targetImage} => ${targetImageUrl}") |
| 103 | // |
| 104 | def uploadImageStatus = '' |
| 105 | def uploadImageMd5Status = '' |
| 106 | common.retry(3, 5) { |
| 107 | uploadImageStatus = sh(script: "curl -f -T ${targetImage} ${job_env.UPLOAD_URL}", returnStatus: true) |
| 108 | if (uploadImageStatus != 0) { |
| 109 | error("Uploading file: ${targetImage} failed!") |
| 110 | } |
| 111 | } |
| 112 | uploadImageMd5Status = sh(script: "curl -f -T ${targetImage}.md5 ${job_env.UPLOAD_URL}", returnStatus: true) |
| 113 | if (uploadImageMd5Status != 0) { |
| 114 | error("Uploading file: ${targetImage}.md5 failed!") |
| 115 | } |
| 116 | |
Alexander Evseev | 1edde6b | 2018-10-30 15:22:44 +0300 | [diff] [blame] | 117 | description += "<a href='http://images.mcp.mirantis.net/${targetImage}'>${job_env.SOURCE_TAG}=>${targetImage}</a>" |
azvyagintsev | 8778188 | 2018-08-30 18:45:22 +0300 | [diff] [blame] | 118 | } |
| 119 | currentBuild.description = description |
| 120 | } catch (Throwable e) { |
| 121 | // If there was an error or exception thrown, the build failed |
| 122 | if (insufficientPermissions) { |
| 123 | currentBuild.result = "ABORTED" |
| 124 | currentBuild.description = "Promote aborted due to insufficient permissions" |
| 125 | } else { |
| 126 | currentBuild.result = "FAILURE" |
| 127 | currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message |
| 128 | } |
| 129 | throw e |
| 130 | } |
| 131 | finally { |
| 132 | common.infoMsg("Cleanup..") |
| 133 | sh(script: 'find . -mindepth 1 -delete > /dev/null || true') |
| 134 | } |
| 135 | } |
| 136 | } |