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 | } |