| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 1 | /** | 
 | 2 |  * | 
| Richard Felkl | 859f4dd | 2018-01-04 23:03:27 +0100 | [diff] [blame^] | 3 |  * Build mirror image pipeline | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 4 |  * | 
 | 5 |  * Expected parameters: | 
| Richard Felkl | 859f4dd | 2018-01-04 23:03:27 +0100 | [diff] [blame^] | 6 |  * CLUSTER_MODEL - An URL to the Reclass model for the mirror VM. | 
 | 7 |  * CLUSTER_NAME - Cluster name used in the above model. | 
 | 8 |  * IMAGE_NAME - Name of the result image. | 
 | 9 |  * OS_CREDENTIALS_ID - ID of credentials for OpenStack API stored in Jenkins. | 
 | 10 |  * OS_PROJECT - Project in OpenStack under the VM will be spawned. | 
 | 11 |  * OS_URL - Keystone auth endpoint of the OpenStack. | 
 | 12 |  * OS_VERSION - OpenStack version | 
 | 13 |  * SCRIPTS_REF - ref on the github to get the scripts from. | 
 | 14 |  * SALT_MASTER_CREDENTIALS - ID of credentials to be used to connect to the Salt API of the VM | 
 | 15 |  * UPLOAD_URL - URL of an WebDAV used to upload the image after creating. | 
 | 16 |  * VM_AVAILABILITY_ZONE - Availability zone in OpenStack in the VM will be spawned. | 
 | 17 |  * VM_CONNECT_RETRIES - Number of retries for SSH connection to the VM after it’s spawned after 8 minutes. | 
 | 18 |  * VM_CONNECT_DELAY - Delay between connect retries above. | 
 | 19 |  * VM_FLAVOR - Flavor to be used for VM in OpenStack. | 
 | 20 |  * VM_FLOATING_IP_POOL - Floating IP pool to be used to assign floating IP to the VM. | 
 | 21 |  * VM_IMAGE - Name of the image to be used for VM in OpenStack. | 
 | 22 |  * VM_IP - Static IP that is assigned to the VM which belongs to the network used. | 
 | 23 |  * VM_IP_RETRIES - Number of retries between tries to assign the floating IP to the VM. | 
 | 24 |  * VM_IP_DELAY - Delay between floating IP assign retries above. | 
 | 25 |  * VM_NETWORK_ID - ID of the network that VM connects to. | 
 | 26 |  * | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 27 |  */ | 
 | 28 |  | 
 | 29 | // Load shared libs | 
 | 30 | def salt = new com.mirantis.mk.Salt() | 
 | 31 | def common = new com.mirantis.mk.Common() | 
 | 32 | def python = new com.mirantis.mk.Python() | 
 | 33 | def openstack = new com.mirantis.mk.Openstack() | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 34 | def date = new Date() | 
 | 35 | def dateTime = date.format("ddMMyyyy-HHmmss") | 
 | 36 | def venvPepper = "venvPepper" | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 37 | def privateKey = "" | 
 | 38 | def floatingIP = "" | 
 | 39 | def openstackServer = "" | 
 | 40 | def rcFile = "" | 
 | 41 | def openstackEnv = "" | 
 | 42 | def serverStatus = "" | 
| Richard Felkl | 802e446 | 2017-12-06 10:08:05 +0100 | [diff] [blame] | 43 | def uploadImageStatus = "" | 
 | 44 | def uploadMd5Status = "" | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 45 |  | 
 | 46 | def retry(int times = 5, int delay = 0, Closure body) { | 
 | 47 |     int retries = 0 | 
 | 48 |     def exceptions = [] | 
 | 49 |     while(retries++ < times) { | 
 | 50 |         try { | 
 | 51 |             return body.call() | 
 | 52 |         } catch(e) { | 
 | 53 |             sleep(delay) | 
 | 54 |         } | 
 | 55 |     } | 
 | 56 |     currentBuild.result = "FAILURE" | 
 | 57 |     throw new Exception("Failed after $times retries") | 
 | 58 | } | 
 | 59 |  | 
 | 60 | node("python&&disk-xl") { | 
 | 61 |     try { | 
 | 62 |         def workspace = common.getWorkspace() | 
 | 63 |         rcFile = openstack.createOpenstackEnv(OS_URL, OS_CREDENTIALS_ID, OS_PROJECT, "default", "", "default", "2", "") | 
 | 64 |         openstackEnv = String.format("%s/venv", workspace) | 
| Richard Felkl | 077b642 | 2017-12-14 16:19:54 +0100 | [diff] [blame] | 65 |         def openstackVersion = OS_VERSION | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 66 |  | 
 | 67 |         VM_IP_DELAY = VM_IP_DELAY as Integer | 
 | 68 |         VM_IP_RETRIES = VM_IP_RETRIES as Integer | 
 | 69 |         VM_CONNECT_DELAY = VM_CONNECT_DELAY as Integer | 
 | 70 |         VM_CONNECT_RETRIES = VM_CONNECT_RETRIES as Integer | 
 | 71 |  | 
 | 72 |         stage("Get templates"){ | 
 | 73 |  | 
 | 74 |             if (!fileExists("${workspace}/tmp")) { | 
 | 75 |                 sh "mkdir -p ${workspace}/tmp" | 
 | 76 |             } | 
 | 77 |  | 
| Richard Felkl | 077b642 | 2017-12-14 16:19:54 +0100 | [diff] [blame] | 78 |             sh "wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/salt-bootstrap.sh" | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 79 |             openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion) | 
 | 80 |         } | 
 | 81 |  | 
 | 82 |         stage("Spawn Instance"){ | 
 | 83 |             privateKey = openstack.runOpenstackCommand("openstack keypair create mcp-offline-keypair-${dateTime}", rcFile, openstackEnv) | 
 | 84 |  | 
 | 85 |             common.infoMsg(privateKey) | 
 | 86 |             sh "echo '${privateKey}' > id_rsa;chmod 600 id_rsa" | 
 | 87 |  | 
 | 88 |             floatingIP = openstack.runOpenstackCommand("openstack ip floating create --format value -c floating_ip_address ${VM_FLOATING_IP_POOL}", rcFile, openstackEnv) | 
 | 89 |  | 
 | 90 |             withEnv(["CLUSTER_NAME=${CLUSTER_NAME}", "CLUSTER_MODEL=${CLUSTER_MODEL}"]) { | 
 | 91 |                 sh "envsubst < salt-bootstrap.sh > salt-bootstrap.sh.temp;mv salt-bootstrap.sh.temp salt-bootstrap.sh; cat salt-bootstrap.sh" | 
 | 92 |             } | 
 | 93 |  | 
| Richard Felkl | 54cc3d5 | 2017-11-28 17:19:22 +0100 | [diff] [blame] | 94 |             openstackServer = openstack.runOpenstackCommand("openstack server create --key-name mcp-offline-keypair-${dateTime} --availability-zone ${VM_AVAILABILITY_ZONE} --image ${VM_IMAGE} --flavor ${VM_FLAVOR} --nic net-id=${VM_NETWORK_ID},v4-fixed-ip=${VM_IP} --user-data salt-bootstrap.sh mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 95 |             sleep(60) | 
 | 96 |  | 
 | 97 |             retry(VM_IP_RETRIES, VM_IP_DELAY){ | 
 | 98 |                 openstack.runOpenstackCommand("openstack ip floating add ${floatingIP} mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 99 |             } | 
 | 100 |  | 
 | 101 |             sleep(500) | 
 | 102 |  | 
 | 103 |             retry(VM_CONNECT_RETRIES, VM_CONNECT_DELAY){ | 
 | 104 |                 sh "scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i id_rsa root@${floatingIP}:/srv/initComplete ./" | 
 | 105 |             } | 
 | 106 |  | 
 | 107 |             python.setupPepperVirtualenv(venvPepper, "http://${floatingIP}:6969", SALT_MASTER_CREDENTIALS) | 
 | 108 |         } | 
 | 109 |         stage("Prepare instance"){ | 
 | 110 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'saltutil.refresh_pillar', [], null, true) | 
 | 111 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'saltutil.sync_all', [], null, true) | 
 | 112 |             salt.enforceState(venvPepper, '*apt*', ['salt'], true, false, null, false, -1, 2) | 
 | 113 |             salt.enforceState(venvPepper, '*apt*', ['linux'], true, false, null, false, -1, 2) | 
 | 114 |             salt.enforceState(venvPepper, '*apt*', ['nginx'], true, false, null, false, -1, 2) | 
 | 115 |         } | 
 | 116 |  | 
 | 117 |         stage("Create Docker Registry"){ | 
 | 118 |             common.infoMsg("Creating Docker Registry") | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 119 |             salt.enforceState(venvPepper, '*apt*', ["docker.host"], true, false, null, false, -1, 2) | 
 | 120 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["docker run --restart always -d -p 5000:5000 --name registry registry:2"], null, true) | 
 | 121 |             salt.enforceState(venvPepper, '*apt*', ["docker.client.registry"], true, false, null, false, -1, 2) | 
 | 122 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["docker system prune --all --force"], null, true) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 123 |         } | 
 | 124 |  | 
 | 125 |         stage("Create Aptly"){ | 
 | 126 |             common.infoMsg("Creating Aptly") | 
 | 127 |             salt.enforceState(venvPepper, '*apt*', ['aptly'], true, false, null, false, -1, 2) | 
 | 128 |             //TODO: Do it new way | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 129 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["aptly_mirror_update.sh -s -v", "runas=aptly"], null, true) | 
 | 130 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["nohup aptly api serve --no-lock > /dev/null 2>&1 </dev/null &", "runas=aptly"], null, true) | 
 | 131 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["aptly-publisher --timeout=1200 publish -v -c /etc/aptly-publisher.yaml --architectures amd64 --url http://127.0.0.1:8080 --recreate --force-overwrite", "runas=aptly"], null, true) | 
 | 132 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["aptly db cleanup", "runas=aptly"], null, true) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 133 |             //NEW way | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 134 |             //salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.script', ['salt://aptly/files/aptly_mirror_update.sh', "args=-sv", "runas=aptly"], null, true) | 
 | 135 |             //salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.script', ['salt://aptly/files/aptly_publish_update.sh', "args=-acrfv", "runas=aptly"], null, true) | 
 | 136 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/aptly/aptly-update.sh -O /srv/scripts/aptly-update.sh"], null, true) | 
 | 137 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["chmod +x /srv/scripts/aptly-update.sh"], null, true) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 138 |         } | 
 | 139 |  | 
| Richard Felkl | 859f4dd | 2018-01-04 23:03:27 +0100 | [diff] [blame^] | 140 |         stage("Create Debmirrors"){ | 
 | 141 |             common.infoMsg("Creating Debmirrors") | 
 | 142 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/debmirror.sh -O /srv/scripts/debmirror.sh"], null, true) | 
 | 143 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["chmod +x /srv/scripts/debmirror.sh"], null, true) | 
 | 144 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["export MCP_VERSION='${MCP_VERSION}';/srv/scripts/debmirror.sh"], null, true) | 
 | 145 |         } | 
 | 146 |  | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 147 |         stage("Create Git mirror"){ | 
 | 148 |             common.infoMsg("Creating Git mirror") | 
 | 149 |             salt.enforceState(venvPepper, '*apt*', ['git.server'], true, false, null, false, -1, 2) | 
 | 150 |         } | 
 | 151 |  | 
 | 152 |         stage("Create PyPi mirror"){ | 
 | 153 |             common.infoMsg("Creating PyPi mirror") | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 154 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["pip install pip2pi"], null, true) | 
 | 155 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/pypi_mirror/requirements.txt -O /srv/pypi_mirror/requirements.txt"], null, true) | 
 | 156 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["pip2pi /srv/pypi_mirror/packages/ -r /srv/pypi_mirror/requirements.txt"], null, true) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 157 |         } | 
 | 158 |  | 
 | 159 |         stage("Create mirror of images"){ | 
 | 160 |             common.infoMsg("Creating mirror of images") | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 161 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/images_mirror/images.txt -O /srv/images.txt"], null, true) | 
 | 162 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["wget https://raw.githubusercontent.com/Mirantis/mcp-common-scripts/${SCRIPTS_REF}/mirror-image/images_mirror/update-images.sh -O /srv/scripts/update-images.sh"], null, true) | 
 | 163 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["chmod +x /srv/scripts/update-images.sh"], null, true) | 
 | 164 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["/srv/scripts/update-images.sh -u http://ci.mcp.mirantis.net:8085/images"], null, true) | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 165 |         } | 
 | 166 |  | 
 | 167 |         stage("Create instance snapshot"){ | 
| Richard Felkl | ae5ef47 | 2017-12-21 15:35:54 +0100 | [diff] [blame] | 168 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["rm -rf /var/lib/cloud/sem/* /var/lib/cloud/instance /var/lib/cloud/instances/*"], null, true) | 
 | 169 |             salt.runSaltProcessStep(venvPepper, '*apt*', 'cmd.run', ["cloud-init init"], null, true) | 
| Richard Felkl | 14a4c6f | 2017-11-29 09:10:10 +0100 | [diff] [blame] | 170 |  | 
| Richard Felkl | 7a00607 | 2017-12-13 09:47:07 +0100 | [diff] [blame] | 171 |             retry(3, 5){ | 
 | 172 |                 openstack.runOpenstackCommand("openstack server stop mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 173 |             } | 
| Richard Felkl | 14a4c6f | 2017-11-29 09:10:10 +0100 | [diff] [blame] | 174 |  | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 175 |             retry(6, 30){ | 
 | 176 |                 serverStatus = openstack.runOpenstackCommand("openstack server show --format value -c status mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 177 |                 if(serverStatus != "SHUTOFF"){ | 
 | 178 |                     throw new ResourceException("Instance is not ready for image create.") | 
 | 179 |                 } | 
 | 180 |             } | 
| Richard Felkl | 7a00607 | 2017-12-13 09:47:07 +0100 | [diff] [blame] | 181 |             retry(3, 5){ | 
 | 182 |                 openstack.runOpenstackCommand("openstack server image create --name ${IMAGE_NAME}-${dateTime} --wait mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 183 |             } | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 184 |         } | 
 | 185 |  | 
 | 186 |         stage("Publish image"){ | 
| Richard Felkl | 802e446 | 2017-12-06 10:08:05 +0100 | [diff] [blame] | 187 |             common.infoMsg("Saving image ${IMAGE_NAME}-${dateTime}") | 
| Richard Felkl | 7a00607 | 2017-12-13 09:47:07 +0100 | [diff] [blame] | 188 |             retry(3, 5){ | 
 | 189 |                 openstack.runOpenstackCommand("openstack image save --file ${IMAGE_NAME}-${dateTime} ${IMAGE_NAME}-${dateTime}", rcFile, openstackEnv) | 
 | 190 |             } | 
| Richard Felkl | 802e446 | 2017-12-06 10:08:05 +0100 | [diff] [blame] | 191 |             sh "md5sum ${IMAGE_NAME}-${dateTime} > ${IMAGE_NAME}-${dateTime}.md5" | 
 | 192 |  | 
 | 193 |             common.infoMsg("Uploading image ${IMAGE_NAME}-${dateTime}") | 
 | 194 |             retry(3, 5){ | 
 | 195 |                 uploadImageStatus = sh(script: "curl -f -T ${IMAGE_NAME}-${dateTime} ${UPLOAD_URL}", returnStatus: true) | 
 | 196 |                 if(uploadImageStatus!=0){ | 
 | 197 |                     throw new Exception("Image upload failed") | 
 | 198 |                 } | 
 | 199 |             } | 
 | 200 |             retry(3, 5){ | 
 | 201 |                 uploadMd5Status = sh(script: "curl -f -T ${IMAGE_NAME}-${dateTime}.md5 ${UPLOAD_URL}", returnStatus: true) | 
 | 202 |                 if(uploadMd5Status != 0){ | 
 | 203 |                     throw new Exception("MD5 sum upload failed") | 
 | 204 |                 } | 
 | 205 |             } | 
| Richard Felkl | 2e9e545 | 2017-10-16 09:52:10 +0200 | [diff] [blame] | 206 |         } | 
 | 207 |  | 
 | 208 |     } catch (Throwable e) { | 
 | 209 |         // If there was an error or exception thrown, the build failed | 
 | 210 |         currentBuild.result = "FAILURE" | 
 | 211 |         throw e | 
 | 212 |     } finally { | 
 | 213 |         stage("Cleanup"){ | 
 | 214 |             if(openstackServer != ""){ | 
 | 215 |                 openstack.runOpenstackCommand("openstack ip floating remove ${floatingIP} mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 216 |                 openstack.runOpenstackCommand("openstack server delete mcp-offline-mirror-${dateTime}", rcFile, openstackEnv) | 
 | 217 |             } | 
 | 218 |             if(privateKey != ""){ | 
 | 219 |                 openstack.runOpenstackCommand("openstack keypair delete mcp-offline-keypair-${dateTime}", rcFile, openstackEnv) | 
 | 220 |             } | 
 | 221 |             sh "rm -rf ./*" | 
 | 222 |         } | 
 | 223 |     } | 
 | 224 | } |