blob: 1b39d14775c58aa070b8765e64e07f36374b36bb [file] [log] [blame]
package com.mirantis.mk
/**
*
* Debian functions
*
*/
def cleanup(image="debian:sid") {
def common = new com.mirantis.mk.Common()
def img = docker.image(image)
workspace = common.getWorkspace()
sh("docker run -e DEBIAN_FRONTEND=noninteractive -v ${workspace}:${workspace} -w ${workspace} --rm=true --privileged ${image} /bin/bash -c 'rm -rf build-area || true'")
}
/*
* Build binary Debian package from existing dsc
*
* @param file dsc file to build
* @param image Image name to use for build (default debian:sid)
*/
def buildBinary(file, image="debian:sid", extraRepoUrl=null, extraRepoKeyUrl=null) {
def common = new com.mirantis.mk.Common()
def jenkinsUID = common.getJenkinsUid()
def jenkinsGID = common.getJenkinsGid()
def pkg = file.split('/')[-1].split('_')[0]
def dockerLib = new com.mirantis.mk.Docker()
def imageArray = image.split(":")
def os = imageArray[0]
def dist = imageArray[1]
def img = dockerLib.getImage("mirantis/debian-build-${os}-${dist}:latest", image)
def workspace = common.getWorkspace()
def debug = env.getEnvironment().containsKey("DEBUG") && env["DEBUG"].toBoolean() ? "true" : ""
img.inside("-u root:root" ) {
sh("""bash -x -c 'cd ${workspace} && (which eatmydata || (apt-get update && apt-get install -y eatmydata)) &&
export DEBUG="${debug}" &&
export LD_LIBRARY_PATH=\${LD_LIBRARY_PATH:+"\$LD_LIBRARY_PATH:"}/usr/lib/libeatmydata &&
export LD_PRELOAD=\${LD_PRELOAD:+"\$LD_PRELOAD "}libeatmydata.so &&
export DEB_BUILD_OPTIONS=nocheck &&
[[ -z "${extraRepoUrl}" && "${extraRepoUrl}" != "null" ]] || echo "${extraRepoUrl}" | tr ";" "\\\\n" >/etc/apt/sources.list.d/extra.list &&
[[ -z "${extraRepoKeyUrl}" && "${extraRepoKeyUrl}" != "null" ]] || (
which curl || (apt-get update && apt-get install -y curl) &&
EXTRAKEY=`echo "${extraRepoKeyUrl}" | tr ";" " "` &&
for RepoKey in \${EXTRAKEY}; do curl --insecure -ss -f "\${RepoKey}" | apt-key add - ; done
) &&
apt-get update && apt-get install -y build-essential devscripts equivs sudo &&
groupadd -g ${jenkinsGID} jenkins &&
useradd -s /bin/bash --uid ${jenkinsUID} --gid ${jenkinsGID} -m jenkins &&
chown -R ${jenkinsUID}:${jenkinsGID} /home/jenkins &&
[ ! -f pre_build_script.sh ] || bash ./pre_build_script.sh &&
sudo -H -E -u jenkins dpkg-source -x ${file} build-area/${pkg} && cd build-area/${pkg} &&
mk-build-deps -t "apt-get -o Debug::pkgProblemResolver=yes -y" -i debian/control &&
sudo -H -E -u jenkins debuild --preserve-envvar DEBUG --no-lintian -uc -us -b'""")
}
}
/*
* Build source package from directory
*
* @param dir Tree to build
* @param image Image name to use for build (default debian:sid)
* @param snapshot Generate snapshot version (default false)
*/
def buildSource(dir, image="debian:sid", snapshot=false, gitEmail='jenkins@dummy.org', gitName='Jenkins', revisionPostfix="", remote="origin/") {
def isGit
try {
sh("test -d ${dir}/.git")
isGit = true
} catch (Exception e) {
isGit = false
}
if (isGit == true) {
buildSourceGbp(dir, image, snapshot, gitEmail, gitName, revisionPostfix, remote)
} else {
buildSourceUscan(dir, image)
}
}
/*
* Build source package, fetching upstream code using uscan
*
* @param dir Tree to build
* @param image Image name to use for build (default debian:sid)
*/
def buildSourceUscan(dir, image="debian:sid") {
def common = new com.mirantis.mk.Common()
def dockerLib = new com.mirantis.mk.Docker()
def imageArray = image.split(":")
def os = imageArray[0]
def dist = imageArray[1]
def img = dockerLib.getImage("mirantis/debian-build-${os}-${dist}:latest", image)
def workspace = common.getWorkspace()
img.inside("-u root:root" ) {
sh("""cd ${workspace} && apt-get update && apt-get install -y build-essential devscripts &&
cd ${dir} && uscan --download-current-version &&
dpkg-buildpackage -S -nc -uc -us""")
}
}
/*
* Build source package using git-buildpackage
*
* @param dir Tree to build
* @param image Image name to use for build (default debian:sid)
* @param snapshot Generate snapshot version (default false)
*/
def buildSourceGbp(dir, image="debian:sid", snapshot=false, gitName='Jenkins', gitEmail='jenkins@dummy.org', revisionPostfix="", remote="origin/") {
def common = new com.mirantis.mk.Common()
def jenkinsUID = common.getJenkinsUid()
def jenkinsGID = common.getJenkinsGid()
if (! revisionPostfix) {
revisionPostfix = ""
}
def workspace = common.getWorkspace()
def dockerLib = new com.mirantis.mk.Docker()
def imageArray = image.split(":")
def os = imageArray[0]
def dist = imageArray[1]
def img = dockerLib.getImage("mirantis/debian-build-${os}-${dist}:latest", image)
img.inside("-u root:root") {
withEnv(["DEBIAN_FRONTEND=noninteractive", "DEBFULLNAME='${gitName}'", "DEBEMAIL='${gitEmail}'"]) {
sh("""bash -x -c 'cd ${workspace} && (which eatmydata || (apt-get update && apt-get install -y eatmydata)) &&
export LD_LIBRARY_PATH=\${LD_LIBRARY_PATH:+"\$LD_LIBRARY_PATH:"}/usr/lib/libeatmydata &&
export LD_PRELOAD=\${LD_PRELOAD:+"\$LD_PRELOAD "}libeatmydata.so &&
apt-get update && apt-get install -y build-essential git-buildpackage dpkg-dev sudo &&
groupadd -g ${jenkinsGID} jenkins &&
useradd -s /bin/bash --uid ${jenkinsUID} --gid ${jenkinsGID} -m jenkins &&
chown -R ${jenkinsUID}:${jenkinsGID} /home/jenkins &&
cd ${dir} &&
sudo -H -E -u jenkins git config --global user.name "${gitName}" &&
sudo -H -E -u jenkins git config --global user.email "${gitEmail}" &&
[[ "${snapshot}" == "false" ]] || (
VERSION=`dpkg-parsechangelog --count 1 --show-field Version` &&
UPSTREAM_VERSION=`echo \$VERSION | cut -d "-" -f 1` &&
REVISION=`echo \$VERSION | cut -d "-" -f 2` &&
TIMESTAMP=`date +%Y%m%d%H%M` &&
if [[ "`cat debian/source/format`" = *quilt* ]]; then
UPSTREAM_BRANCH=`(grep upstream-branch debian/gbp.conf || echo master) | cut -d = -f 2 | tr -d " "` &&
UPSTREAM_REV=`git rev-parse --short ${remote}\$UPSTREAM_BRANCH` &&
NEW_UPSTREAM_VERSION="\$UPSTREAM_VERSION+\$TIMESTAMP.\$UPSTREAM_REV" &&
NEW_UPSTREAM_VERSION_TAG=`echo \$NEW_UPSTREAM_VERSION | sed 's/.*://'` &&
NEW_VERSION=\$NEW_UPSTREAM_VERSION-\$REVISION$revisionPostfix &&
echo "Generating new upstream version \$NEW_UPSTREAM_VERSION_TAG" &&
sudo -H -E -u jenkins git tag \$NEW_UPSTREAM_VERSION_TAG ${remote}\$UPSTREAM_BRANCH &&
sudo -H -E -u jenkins git merge -X theirs \$NEW_UPSTREAM_VERSION_TAG
else
NEW_VERSION=\$VERSION+\$TIMESTAMP.`git rev-parse --short HEAD`$revisionPostfix
fi &&
sudo -H -E -u jenkins gbp dch \
--auto \
--git-author \
--id-length=7 \
--git-log='--reverse' \
--ignore-branch \
--new-version=\$NEW_VERSION \
--distribution `lsb_release -c -s` \
--force-distribution &&
sudo -H -E -u jenkins git add -u debian/changelog &&
sudo -H -E -u jenkins git commit -m "New snapshot version \$NEW_VERSION"
) &&
sudo -H -E -u jenkins gbp buildpackage -nc --git-force-create --git-notify=false --git-ignore-branch --git-ignore-new --git-verbose --git-export-dir=../build-area -sa -S -uc -us '""")
}
}
}
/*
* Run lintian checks
*
* @param changes Changes file to test against
* @param profile Lintian profile to use (default debian)
* @param image Image name to use for build (default debian:sid)
*/
def runLintian(changes, profile="debian", image="debian:sid") {
def common = new com.mirantis.mk.Common()
def workspace = common.getWorkspace()
def dockerLib = new com.mirantis.mk.Docker()
def imageArray = image.split(":")
def os = imageArray[0]
def dist = imageArray[1]
def img = dockerLib.getImage("mirantis/debian-build-${os}-${dist}:latest", image)
img.inside("-u root:root") {
sh("""cd ${workspace} && apt-get update && apt-get install -y lintian &&
lintian -Ii -E --pedantic --profile=${profile} ${changes}""")
}
}
/*
* Import gpg key
*
* @param privateKeyCredId Public key jenkins credential id
*/
def importGpgKey(privateKeyCredId)
{
def common = new com.mirantis.mk.Common()
def workspace = common.getWorkspace()
def privKey = common.getCredentials(privateKeyCredId, "key")
def private_key = privKey.privateKeySource.privateKey
def gpg_key_id = common.getCredentials(privateKeyCredId, "key").username
def retval = sh(script: "export GNUPGHOME=${workspace}/.gnupg; gpg --list-secret-keys | grep ${gpg_key_id}", returnStatus: true)
if (retval) {
writeFile file:"${workspace}/private.key", text: private_key
sh(script: "gpg --no-tty --allow-secret-key-import --homedir ${workspace}/.gnupg --import ./private.key")
}
}
/*
* upload source package to launchpad
*
* @param ppaRepo ppa repository on launchpad
* @param dirPath repository containing the source packages
*/
def uploadPpa(ppaRepo, dirPath, privateKeyCredId) {
def common = new com.mirantis.mk.Common()
def workspace = common.getWorkspace()
def gpg_key_id = common.getCredentials(privateKeyCredId, "key").username
dir(dirPath)
{
def images = findFiles(glob: "*.orig*.tar.gz")
for (int i = 0; i < images.size(); ++i) {
def name = images[i].getName()
def orig_sha1 = common.cutOrDie("sha1sum ${name}", 0)
def orig_sha256 = common.cutOrDie("sha256sum ${name}", 0)
def orig_md5 = common.cutOrDie("md5sum ${name}", 0)
def orig_size = common.cutOrDie("ls -l ${name}", 4)
def retval = sh(script: "wget --quiet -O orig-tmp https://launchpad.net/ubuntu/+archive/primary/+files/${name}", returnStatus: true)
if (retval == 0) {
sh("mv orig-tmp ${name}")
def new_sha1 = common.cutOrDie("sha1sum ${name}", 0)
def new_sha256 = common.cutOrDie("sha256sum ${name}", 0)
def new_md5 = common.cutOrDie("md5sum ${name}", 0)
def new_size = common.cutOrDie("ls -l ${name}", 4)
sh("sed -i -e s,$orig_sha1,$new_sha1,g -e s,$orig_sha256,$new_sha256,g -e s,$orig_size,$new_size,g -e s,$orig_md5,$new_md5,g *.dsc")
sh("sed -i -e s,$orig_sha1,$new_sha1,g -e s,$orig_sha256,$new_sha256,g -e s,$orig_size,$new_size,g -e s,$orig_md5,$new_md5,g *_source.changes")
}
}
sh("export GNUPGHOME=${workspace}/.gnupg; debsign --re-sign -k ${gpg_key_id} *_source.changes")
sh("export GNUPGHOME=${workspace}/.gnupg; dput -f \"ppa:${ppaRepo}\" *_source.changes")
}
}
/**
* Reboot specified target, and wait when minion is UP.
*
* @param env Salt Connection object or env Salt command map
* @param target Salt target to upgrade packages on.
* @param timeout Sleep timeout when doing retries.
* @param attempts Number of attemps to wait for.
*/
def osReboot(env, target, timeout=30, attempts=10) {
def salt = new com.mirantis.mk.Salt()
def common = new com.mirantis.mk.Common()
salt.runSaltProcessStep(env, target, 'cmd.run', ["touch /tmp/rebooting"])
salt.runSaltProcessStep(env, target, 'system.reboot', [], null, true, 5)
common.retry(timeout, attempts) {
if (salt.runSaltProcessStep(env, target, 'cmd.run', ['test -e /tmp/rebooting || echo NOFILE'], null, true, 5)['return'][0].values()[0] != "NOFILE") {
error("The system is still rebooting...")
}
}
}
/**
* Upgrade OS on given node, wait when minion become reachable.
*
* @param env Salt Connection object or env Salt command map
* @param target Salt target to upgrade packages on.
* @param mode 'upgrade' or 'dist-upgrade'
* @param postponeReboot Boolean flag to specify if reboot have to be postponed.
* @param timeout Sleep timeout when doing retries.
* @param attempts Number of attemps to wait for.
*/
def osUpgradeNode(env, target, mode, postponeReboot=false, timeout=30, attempts=10, batch=null) {
if(mode in ['upgrade', 'dist-upgrade']) {
def common = new com.mirantis.mk.Common()
def salt = new com.mirantis.mk.Salt()
def rebootRequired = false
common.infoMsg("Running apt ${mode} on ${target}")
common.retry(3, 5) {
salt.cmdRun(env, target, 'salt-call pkg.refresh_db failhard=true', true, batch)
}
/* first try to upgrade salt components since they demand asynchronous upgrade */
upgradeSaltPackages(env, target)
def cmd = "export DEBIAN_FRONTEND=noninteractive; apt-get -y -q --allow-downgrades -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" ${mode}"
/*
* This is a long running batch operation that may return empty response
* which is a pretty typical salt behavior. This does not represent an error
* but might hide the error if it's ignored. If there is no persistent error
* with the procedure itself, the consequent run will succeed.
*/
common.retry(2, 120) {
salt.cmdRun(env, target, cmd, true, batch)
}
rebootRequired = salt.runSaltProcessStep(env, target, 'file.file_exists', ['/var/run/reboot-required'], batch, true, 5)['return'][0].values()[0].toBoolean()
if (rebootRequired) {
if (!postponeReboot) {
common.infoMsg("Reboot is required after upgrade on ${target} Rebooting...")
osReboot(env, target, timeout, attempts)
} else {
common.infoMsg("Postponing reboot on node ${target}")
}
}
} else {
common.errorMsg("Invalid upgrade mode specified: ${mode}. Has to be 'upgrade' or 'dist-upgrade'")
}
}
/**
* Upgrade salt packages on target asynchronously, wait minions' availability.
*
* @param env Salt Connection object or env Salt command map
* @param target Salt target to upgrade packages on.
* @param timeout Sleep timeout when doing retries.
* @param attempts Number of attemps to wait for.
*/
def upgradeSaltPackages(env, target, timeout=60, attempts=20) {
def common = new com.mirantis.mk.Common()
def salt = new com.mirantis.mk.Salt()
def saltUpgradeCmd =
'export DEBIAN_FRONTEND=noninteractive; apt-get -y -q ' +
'-o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" ' +
'install --only-upgrade salt-master salt-common salt-api salt-minion'
common.infoMsg("Upgrading SaltStack on ${target}")
salt.cmdRun(env, target, saltUpgradeCmd, false, null, true, [], [], true)
/* wait for 2 mins before checking the availability of minions to give
apt some time to finish updating so the dpkg releases its locks */
sleep(120)
/* taken from upgrade-mcp-release */
common.retry(attempts, timeout) {
salt.minionsReachable(env, 'I@salt:master', target)
def running = salt.runSaltProcessStep(env, target, 'saltutil.running', [], null, true, 5)
for (value in running.get("return")[0].values()) {
if (value != []) {
throw new Exception("Not all salt-minions are ready for execution")
}
}
}
}