Merge "Add class filer for k8s pipelines."
diff --git a/branch-git-repos.groovy b/branch-git-repos.groovy
new file mode 100644
index 0000000..47c143a
--- /dev/null
+++ b/branch-git-repos.groovy
@@ -0,0 +1,135 @@
+#!groovy
+
+/**
+ * (Re-)Create git branches
+ *
+ * @param GIT_REPO_LIST List of repositories to handle
+ * Multiline text: '<name> <url> <src_obj>' (full format)
+ * or: '<url>' (assuming src_obj=='SUBS_SOURCE_REF')
+ * @param GIT_CREDENTIALS Credentials ID to use for the ALL given repositories
+ * @param BRANCH New branch name
+ * @param SOURCE_REVISION Source object (commit/tag/branch) to apply to all repos
+ * having empty src_obj or src_obj=='SUBS_SOURCE_REF'
+ *
+ * @see <a href="https://mirantis.jira.com/browse/PROD-17759">PROD-17759</a>
+ */
+
+// Get job environment to use as a map to get values with defaults
+Map jobEnv = env.getEnvironment().findAll { k, v -> v }
+
+// Prepare job parameters
+ArrayList gitRepoList = jobEnv.get('GIT_REPO_LIST', '').readLines()
+String gitBranchNew = jobEnv.get('BRANCH')
+String srcObj = jobEnv.get('SOURCE_REVISION', 'master')
+String gitCredentialsId = jobEnv.get('GIT_CREDENTIALS')
+
+// Check if new branch name is given
+if (! gitBranchNew) {
+ error ('No new branch name is given')
+}
+
+/**
+ * Returns local path for the given URL constructed from hostname and repository
+ *
+ * @param repoUrl git repository URL
+ * @return string representing local relative patch
+ */
+String getRepoLocalPath(String repoUrl) {
+ // Regex to split git repository URLs
+ String re = '^(?:(?<proto>[a-z]+)://)?(?:(?<creds>[^@]+)@)?(?<host>[^:/]+)(?::(?<port>[0-9]+)/|[:/])(?<repo>.+)$'
+
+ java.util.regex.Matcher urlMatcher = repoUrl =~ re
+ if (urlMatcher.matches()) {
+ return new File(
+ urlMatcher.group('host'),
+ urlMatcher.group('repo').replaceAll(/\.git$/,'')
+ ).toString()
+ } else {
+ return ''
+ }
+}
+
+// Variables to use as repo parameters
+String gitRepoName
+String gitRepoUrl
+String gitSrcObj
+
+// Store current commit SHA
+String gitCommit
+
+node() {
+ for (gitRepo in gitRepoList) {
+ (gitRepoName, gitRepoUrl, gitSrcObj) = gitRepo.trim().tokenize(' ')
+
+ if (gitRepoName.startsWith('#')){
+ echo ("Skipping repo '${gitRepo}'")
+ continue
+ }
+
+ if (! gitRepoUrl) {
+ // The only token is the git repo url
+ gitRepoUrl = gitRepoName
+ gitRepoName = getRepoLocalPath(gitRepoUrl)
+ gitSrcObj = srcObj
+ } else if (! gitSrcObj) {
+ // Two tokens - can't decide is gitRepoName or gitSrcObj given
+ error ("Wrong repository string format: '${gitRepo}'")
+ }
+
+ if (gitSrcObj.contains('SUBS_SOURCE_REF')) {
+ echo ("Replacing 'SUBS_SOURCE_REF' => ${SOURCE_REVISION}")
+ gitSrcObj = gitSrcObj.replace('SUBS_SOURCE_REF', srcObj)
+ }
+
+ // Remove preifix `origin/` from gitSrcObj
+ java.util.regex.Pattern reOrigin = ~'^origin/'
+ gitSrcObj = gitSrcObj - reOrigin
+
+ checkout([
+ $class: 'GitSCM',
+ branches: [
+ [name: 'FETCH_HEAD'],
+ ],
+ userRemoteConfigs: [
+ [url: gitRepoUrl, refspec: gitSrcObj, credentialsId: gitCredentialsId],
+ ],
+ extensions: [
+ [$class: 'PruneStaleBranch'],
+ [$class: 'RelativeTargetDirectory', relativeTargetDir: gitRepoName],
+ [$class: 'SubmoduleOption', disableSubmodules: true],
+ [$class: 'UserIdentity', name: 'MCP CI', email: 'ci+infra@mirantis.com'],
+ ],
+ ])
+
+ // Proceed branch creation
+ dir(gitRepoName) {
+ sshagent (credentials: [gitCredentialsId]) {
+ // FIXME: Ensure git has configured user and email
+ // See: https://issues.jenkins-ci.org/browse/JENKINS-46052
+ sh 'git config user.name "MCP CI"'
+ sh 'git config user.email "ci+infra@mirantis.com"'
+
+ // Update list of branches
+ sh 'git remote update origin --prune'
+
+ // Ensure there is no branch or tag with gitBranchNew name
+ sh "git branch -d '${gitBranchNew}' && git push origin ':${gitBranchNew}' || :"
+ sh "git tag -d '${gitBranchNew}' && git push origin ':refs/tags/${gitBranchNew}' || :"
+
+ // Check if gitSrcObj is a branch
+ gitCommit = sh (script: "git ls-remote --heads --quiet origin '${gitSrcObj}' | awk '{print \$1}'",
+ returnStdout: true).trim()
+ if (gitCommit) {
+ // Rename existing branch
+ sh "git checkout -b '${gitSrcObj}' -t 'origin/${gitSrcObj}'" // Checkout old branch
+ sh "git branch -m '${gitSrcObj}' '${gitBranchNew}'" // ... rename it
+ sh "git push origin ':${gitSrcObj}'" // ... remove old remote branch
+ } else {
+ // Create new branch
+ sh "git checkout -b '${gitBranchNew}' '${gitSrcObj}'" // Create new local branch
+ }
+ sh "git push origin '${gitBranchNew}'" // ... push new branch
+ }
+ }
+ }
+}
diff --git a/cloud-update.groovy b/cloud-update.groovy
index 19d563f..2729d98 100644
--- a/cloud-update.groovy
+++ b/cloud-update.groovy
@@ -38,6 +38,7 @@
* RESTORE_GALERA Restore Galera DB (bool)
* RESTORE_CONTRAIL_DB Restore Cassandra and Zookeeper DBs for OpenContrail (bool)
* RUN_CVP_TESTS Run cloud validation pipelines before and after upgrade
+ * MINIONS_TEST_TIMEOUT Time in seconds for a Salt result to receive a response when calling a minionsReachable method.
*
**/
def common = new com.mirantis.mk.Common()
@@ -57,6 +58,11 @@
def command
def commandKwargs
+def wait = 10
+if (common.validInputParam('MINIONS_TEST_TIMEOUT') && MINIONS_TEST_TIMEOUT.isInteger()) {
+ wait = "${MINIONS_TEST_TIMEOUT}".toInteger()
+}
+
def updatePkgs(pepperEnv, target, targetType="", targetPackages="") {
def salt = new com.mirantis.mk.Salt()
def common = new com.mirantis.mk.Common()
@@ -153,11 +159,11 @@
if (targetType == 'cfg') {
common.warningMsg('salt-master pkg upgrade, rerun the pipeline if disconnected')
salt.runSaltProcessStep(pepperEnv, target, 'pkg.install', ['salt-master'], null, true, 5)
- salt.minionsReachable(pepperEnv, 'I@salt:master', '*')
+ salt.minionsReachable(pepperEnv, 'I@salt:master', '*', null, wait)
}
// salt minion pkg
salt.runSaltProcessStep(pepperEnv, target, 'pkg.install', ['salt-minion'], null, true, 5)
- salt.minionsReachable(pepperEnv, 'I@salt:master', target)
+ salt.minionsReachable(pepperEnv, 'I@salt:master', target, null, wait)
common.infoMsg('Performing pkg upgrades ... ')
common.retry(3){
out = salt.runSaltCommand(pepperEnv, 'local', ['expression': target, 'type': 'compound'], command, true, packages, commandKwargs)
@@ -317,7 +323,7 @@
common.retry(3){
out = salt.runSaltProcessStep(pepperEnv, target, 'cmd.run', [args + ' install salt-minion'], null, true, 5)
}
- salt.minionsReachable(pepperEnv, 'I@salt:master', target)
+ salt.minionsReachable(pepperEnv, 'I@salt:master', target, null, wait)
common.retry(3){
out = salt.runSaltProcessStep(pepperEnv, target, 'cmd.run', [args + ' install ' + packages])
}
@@ -433,7 +439,7 @@
while(count < maxRetries) {
try {
sleep(10)
- salt.minionsReachable(pepperEnv, 'I@salt:master', target)
+ salt.minionsReachable(pepperEnv, 'I@salt:master', target, null, wait)
break
} catch (Exception e) {
common.warningMsg("${target} not ready yet. Waiting ...")
@@ -483,7 +489,7 @@
} else {
salt.runSaltProcessStep(pepperEnv, target, 'system.reboot', null, null, true, 5)
sleep 10
- salt.minionsReachable(pepperEnv, 'I@salt:master', target)
+ salt.minionsReachable(pepperEnv, 'I@salt:master', target, null, wait)
}
}
}
@@ -541,7 +547,7 @@
virsh.liveSnapshotMerge(pepperEnv, nodeProvider, target, SNAPSHOT_NAME)
}
}
- salt.minionsReachable(pepperEnv, 'I@salt:master', tgt)
+ salt.minionsReachable(pepperEnv, 'I@salt:master', tgt, null, wait)
}
diff --git a/generate-cookiecutter-products.groovy b/generate-cookiecutter-products.groovy
index 553029e..0924951 100644
--- a/generate-cookiecutter-products.groovy
+++ b/generate-cookiecutter-products.groovy
@@ -25,7 +25,7 @@
// options from CC contexts
// currently, just mix them together in one set
def testCfg01ExtraFormulas = 'glusterfs jenkins logrotate maas ntp rsyslog fluentd telegraf prometheus ' +
- 'grafana backupninja auditd'
+ 'grafana backupninja'
timeout(time: 2, unit: 'HOURS') {
@@ -58,6 +58,10 @@
user = env.BUILD_USER_ID
}
+ if (mcpVersion != '2018.4.0') {
+ testCfg01ExtraFormulas += ' auditd'
+ }
+
currentBuild.description = clusterName
print("Using context:\n" + COOKIECUTTER_TEMPLATE_CONTEXT)
diff --git a/git-merge-branches-pipeline.groovy b/git-merge-branches-pipeline.groovy
deleted file mode 100644
index d1c3ee2..0000000
--- a/git-merge-branches-pipeline.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Git merge branches pipeline
- * REPO_URL - Repository URL
- * TARGET_BRANCH - Target branch for merging
- * SOURCE_BRANCH - The branch will be merged to TARGET_BRANCH
- * CREDENTIALS_ID - Used credentails ID
- *
-**/
-
-def common = new com.mirantis.mk.Common()
-def git = new com.mirantis.mk.Git()
-timeout(time: 12, unit: 'HOURS') {
- node {
- try{
- stage("checkout") {
- git.checkoutGitRepository('repo', REPO_URL, TARGET_BRANCH, IMAGE_CREDENTIALS_ID)
- }
- stage("merge") {
- dir("repo"){
- sh("git fetch origin/${SOURCE_BRANCH} && git merge ${SOURCE_BRANCH} && git push origin ${TARGET_BRANCH}")
- }
- }
- } catch (Throwable e) {
- // If there was an error or exception thrown, the build failed
- currentBuild.result = "FAILURE"
- currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
- throw e
- }
- }
-}
-
diff --git a/promote-mirror-ubuntu-related.groovy b/promote-mirror-ubuntu-related.groovy
new file mode 100644
index 0000000..cd663cf
--- /dev/null
+++ b/promote-mirror-ubuntu-related.groovy
@@ -0,0 +1,27 @@
+/**
+ *
+ * Promote Ubuntu-related mirrors in same time.
+ * Promote ubuntu|maas|maas-ephermal should be always together.
+ *
+ * Expected parameters:
+ * MCP_VERSION
+ * SNAPSHOT_NAME - Snapshot name to set
+ * SNAPSHOT_ID - Set name for specified snapshot ID
+ */
+
+common = new com.mirantis.mk.Common()
+
+timeout(time: 1, unit: 'HOURS') {
+ node() {
+ stage("Promote") {
+ catchError {
+ for (String jobname : ['mirror-snapshot-name-maas-xenial', 'mirror-snapshot-name-ubuntu', 'mirror-snapshot-name-maas-ephemeral-v3']) {
+ build job: jobname, parameters: [
+ [$class: 'StringParameterValue', name: 'SNAPSHOT_NAME', value: SNAPSHOT_NAME],
+ [$class: 'StringParameterValue', name: 'SNAPSHOT_ID', value: SNAPSHOT_ID],
+ ]
+ }
+ }
+ }
+ }
+}
diff --git a/promote-vcp-images.groovy b/promote-vcp-images.groovy
new file mode 100644
index 0000000..181eafa
--- /dev/null
+++ b/promote-vcp-images.groovy
@@ -0,0 +1,134 @@
+/**
+ *
+ * Promote VCP(qcow2) images
+ *
+ * Expected parameters:
+ * VCP_IMAGE_LIST - multiline with qcow2 file names
+ * TAG - Target tag of image.Possible are: "nightly|testing|proposed|201X.X.X"
+ * SOURCE_TAG - Initial tag to be tagged with TAG. Will replace SUBS_SOURCE_VCP_IMAGE_TAG in VCP_IMAGE_LIST
+ * UPLOAD_URL - WebDav url with creds, from\to download images
+ *
+ */
+
+def common = new com.mirantis.mk.Common()
+def jenkinsUtils = new com.mirantis.mk.JenkinsUtils()
+
+// Better to chose slave with ssd and fast network to webDav host
+slaveNode = env.SLAVE_NODE ?: 'jsl23.mcp.mirantis.net'
+def job_env = env.getEnvironment().findAll { k, v -> v }
+def verify = job_env.VERIFY_DOWNLOAD ?: true
+
+
+timeout(time: 6, unit: 'HOURS') {
+ node(slaveNode) {
+
+ String description = ''
+ insufficientPermissions = false
+ try {
+ // Pre-run verify
+ // promote is restricted to users in aptly-promote-users LDAP group
+ if (!jenkinsUtils.currentUserInGroups(["mcp-cicd-admins", "aptly-promote-users"])) {
+ insufficientPermissions = true
+ 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))
+ }
+ // Check for required opts
+ for (opt in ['UPLOAD_URL', 'SOURCE_TAG', 'TAG', 'VCP_IMAGE_LIST']) {
+ if (!job_env.get(opt, null)) {
+ error("Invalid input params, at least ${opt} param missing")
+ }
+ }
+ def images = job_env.VCP_IMAGE_LIST.trim().tokenize()
+ for (image in images) {
+ if (image.startsWith('#')) {
+ common.warningMsg("Skipping image ${image}")
+ continue
+ }
+ common.infoMsg("Replacing SUBS_SOURCE_VCP_IMAGE_TAG => ${job_env.SOURCE_TAG}")
+ sourceImage = image.replace('SUBS_SOURCE_VCP_IMAGE_TAG', job_env.SOURCE_TAG)
+ targetImage = image.replace('SUBS_SOURCE_VCP_IMAGE_TAG', job_env.TAG)
+
+ // TODO: normalize url's?
+ sourceImageUrl = job_env.UPLOAD_URL + '/' + sourceImage
+ sourceImageMd5Url = job_env.UPLOAD_URL + '/' + sourceImage + '.md5'
+ targetImageUrl = job_env.UPLOAD_URL + '/' + targetImage
+ targetImageMd5Url = job_env.UPLOAD_URL + '/' + targetImage + '.md5'
+
+ common.infoMsg("Attempt to download: ${sourceImage} => ${targetImage}")
+ common.retry(3, 5) {
+ sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage} ${sourceImageUrl}")
+ }
+ def targetImageMd5 = common.cutOrDie("md5sum ${targetImage} | tee ${targetImage}.md5", 0)
+ if (verify.toBoolean()) {
+ common.infoMsg("Checking md5's ")
+ sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage}_source_md5 ${sourceImageMd5Url}")
+ def sourceImageMd5 = readFile(file: "${targetImage}_source_md5").tokenize(' ')[0]
+ // Compare downloaded and remote files
+ if (sourceImageMd5 != targetImageMd5) {
+ error("Image ${targetImage} md5sum verify failed!")
+ } else {
+ common.infoMsg("sourceImageMd5: ${sourceImageMd5} == target to upload ImageMd5: ${targetImageMd5}")
+ }
+ // Compare downloaded file, and remote file-to-be-promoted. If same - no sense to promote same file
+ remoteImageMd5Status = sh(script: "wget --progress=dot:giga --auth-no-challenge -O ${targetImage}_expected_target_md5 ${targetImageMd5Url}", returnStatus: true)
+ if (remoteImageMd5Status == '8') {
+ common.infoMsg("target to upload ImageMd5 file not even exist.Continue..")
+ } else {
+ def remoteImageMd5 = readFile(file: "${targetImage}_expected_target_md5").tokenize(' ')[0]
+ if (sourceImageMd5 == remoteImageMd5) {
+ common.infoMsg("sourceImageMd5: ${sourceImageMd5} and target to upload ImageMd5: ${targetImageMd5} are same")
+ common.warningMsg("Skipping to upload: ${targetImage} since it already same")
+ description += "Skipping to upload: ${targetImage} since it already same\n"
+ continue
+ }
+ }
+ common.infoMsg("Check, that we are not going to overwrite released file..")
+ if (['proposed', 'testing', 'nightly'].contains(job_env.TAG)) {
+ common.infoMsg("Uploading to ${job_env.TAG} looks safe..")
+ } else if (['stable'].contains(job_env.TAG)) {
+ common.warningMsg("Uploading to ${job_env.TAG} not safe! But still possible")
+ } else {
+ common.warningMsg("Looks like uploading to new release: ${job_env.TAG}. Checking, that it is not exist yet..")
+ remoteImageStatus = ''
+ remoteImageStatus = sh(script: "wget --auth-no-challenge --spider ${targetImageUrl} 2>/dev/null", returnStatus: true)
+ // wget return code 8 ,if file not exist
+ if (remoteImageStatus != '8') {
+ error("Attempt to overwrite existing release! Target: ${targetImage} already exist!")
+ }
+ }
+ }
+
+ common.infoMsg("Attempt to UPLOAD: ${targetImage} => ${targetImageUrl}")
+ //
+ def uploadImageStatus = ''
+ def uploadImageMd5Status = ''
+ common.retry(3, 5) {
+ uploadImageStatus = sh(script: "curl -f -T ${targetImage} ${job_env.UPLOAD_URL}", returnStatus: true)
+ if (uploadImageStatus != 0) {
+ error("Uploading file: ${targetImage} failed!")
+ }
+ }
+ uploadImageMd5Status = sh(script: "curl -f -T ${targetImage}.md5 ${job_env.UPLOAD_URL}", returnStatus: true)
+ if (uploadImageMd5Status != 0) {
+ error("Uploading file: ${targetImage}.md5 failed!")
+ }
+
+ description += "<a href='http://apt.mirantis.net:8085/images/${targetImage}'>${job_env.SOURCE_TAG}=>${targetImage}</a>"
+ }
+ currentBuild.description = description
+ } catch (Throwable e) {
+ // If there was an error or exception thrown, the build failed
+ if (insufficientPermissions) {
+ currentBuild.result = "ABORTED"
+ currentBuild.description = "Promote aborted due to insufficient permissions"
+ } else {
+ currentBuild.result = "FAILURE"
+ currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
+ }
+ throw e
+ }
+ finally {
+ common.infoMsg("Cleanup..")
+ sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
+ }
+ }
+}
diff --git a/release-mcp-version.groovy b/release-mcp-version.groovy
index b1b3d77..1972465 100644
--- a/release-mcp-version.groovy
+++ b/release-mcp-version.groovy
@@ -16,92 +16,104 @@
* DOCKER_IMAGES
* GIT_CREDENTIALS
* GIT_REPO_LIST
+ * VCP_IMAGE_LIST - list of images
+ * RELEASE_VCP_IMAGES - boolean
* EMAIL_NOTIFY
* NOTIFY_RECIPIENTS
* NOTIFY_TEXT
*
-*/
+ */
common = new com.mirantis.mk.Common()
git = new com.mirantis.mk.Git()
-def triggerAptlyPromoteJob(aptlyUrl, components, diffOnly, dumpPublish, packages, recreate, source, storages, target){
- build job: "aptly-promote-all-testing-stable", parameters: [
- [$class: 'StringParameterValue', name: 'APTLY_URL', value: aptlyUrl],
- [$class: 'StringParameterValue', name: 'COMPONENTS', value: components],
- [$class: 'BooleanParameterValue', name: 'DIFF_ONLY', value: diffOnly],
- [$class: 'BooleanParameterValue', name: 'DUMP_PUBLISH', value: dumpPublish],
- [$class: 'StringParameterValue', name: 'PACKAGES', value: packages],
- [$class: 'BooleanParameterValue', name: 'RECREATE', value: recreate],
- [$class: 'StringParameterValue', name: 'SOURCE', value: source],
- [$class: 'StringParameterValue', name: 'STORAGES', value: storages],
- [$class: 'StringParameterValue', name: 'TARGET', value: target],
- ]
+def triggerAptlyPromoteJob(aptlyUrl, components, diffOnly, dumpPublish, packages, recreate, source, storages, target) {
+ build job: "aptly-promote-all-testing-stable", parameters: [
+ [$class: 'StringParameterValue', name: 'APTLY_URL', value: aptlyUrl],
+ [$class: 'StringParameterValue', name: 'COMPONENTS', value: components],
+ [$class: 'BooleanParameterValue', name: 'DIFF_ONLY', value: diffOnly],
+ [$class: 'BooleanParameterValue', name: 'DUMP_PUBLISH', value: dumpPublish],
+ [$class: 'StringParameterValue', name: 'PACKAGES', value: packages],
+ [$class: 'BooleanParameterValue', name: 'RECREATE', value: recreate],
+ [$class: 'StringParameterValue', name: 'SOURCE', value: source],
+ [$class: 'StringParameterValue', name: 'STORAGES', value: storages],
+ [$class: 'StringParameterValue', name: 'TARGET', value: target],
+ ]
}
def triggerDockerMirrorJob(dockerCredentials, dockerRegistryUrl, targetTag, imageList, sourceImageTag) {
- build job: "docker-images-mirror", parameters: [
- [$class: 'StringParameterValue', name: 'TARGET_REGISTRY_CREDENTIALS_ID', value: dockerCredentials],
- [$class: 'StringParameterValue', name: 'REGISTRY_URL', value: dockerRegistryUrl],
- [$class: 'StringParameterValue', name: 'IMAGE_TAG', value: targetTag],
- [$class: 'StringParameterValue', name: 'IMAGE_LIST', value: imageList],
- [$class: 'StringParameterValue', name: 'SOURCE_IMAGE_TAG', value: sourceImageTag],
- ]
+ build job: "docker-images-mirror", parameters: [
+ [$class: 'StringParameterValue', name: 'TARGET_REGISTRY_CREDENTIALS_ID', value: dockerCredentials],
+ [$class: 'StringParameterValue', name: 'REGISTRY_URL', value: dockerRegistryUrl],
+ [$class: 'StringParameterValue', name: 'IMAGE_TAG', value: targetTag],
+ [$class: 'StringParameterValue', name: 'IMAGE_LIST', value: imageList],
+ [$class: 'StringParameterValue', name: 'SOURCE_IMAGE_TAG', value: sourceImageTag],
+ ]
}
def triggerMirrorRepoJob(snapshotId, snapshotName) {
- build job: "mirror-snapshot-name-all", parameters: [
- [$class: 'StringParameterValue', name: 'SNAPSHOT_NAME', value: snapshotName],
- [$class: 'StringParameterValue', name: 'SNAPSHOT_ID', value: snapshotId],
- ]
+ build job: "mirror-snapshot-name-all", parameters: [
+ [$class: 'StringParameterValue', name: 'SNAPSHOT_NAME', value: snapshotName],
+ [$class: 'StringParameterValue', name: 'SNAPSHOT_ID', value: snapshotId],
+ ]
}
def triggerGitTagJob(gitRepoList, gitCredentials, tag, sourceTag) {
- build job: "tag-git-repos-stable", parameters: [
- [$class: 'StringParameterValue', name: 'GIT_REPO_LIST', value: gitRepoList],
- [$class: 'StringParameterValue', name: 'GIT_CREDENTIALS', value: gitCredentials],
- [$class: 'StringParameterValue', name: 'TAG', value: tag],
- [$class: 'StringParameterValue', name: 'SOURCE_TAG', value: sourceTag],
- ]
+ build job: "tag-git-repos-all", parameters: [
+ [$class: 'StringParameterValue', name: 'GIT_REPO_LIST', value: gitRepoList],
+ [$class: 'StringParameterValue', name: 'GIT_CREDENTIALS', value: gitCredentials],
+ [$class: 'StringParameterValue', name: 'TAG', value: tag],
+ [$class: 'StringParameterValue', name: 'SOURCE_TAG', value: sourceTag],
+ ]
+}
+
+def triggerPromoteVCPJob(VcpImageList, tag, sourceTag) {
+ build job: "promote-vcp-images-all", parameters: [
+ [$class: 'StringParameterValue', name: 'VCP_IMAGE_LIST', value: VcpImageList],
+ [$class: 'StringParameterValue', name: 'TAG', value: tag],
+ [$class: 'StringParameterValue', name: 'SOURCE_TAG', value: sourceTag]
+ ]
}
timeout(time: 12, unit: 'HOURS') {
- node() {
- try {
- stage("Promote"){
- if(RELEASE_APTLY.toBoolean())
- {
- common.infoMsg("Promoting Aptly")
- triggerAptlyPromoteJob(APTLY_URL, 'all', false, true, 'all', false, "(.*)/${SOURCE_REVISION}", APTLY_STORAGES, "{0}/${TARGET_REVISION}")
- }
+ node() {
+ try {
+ stage("Promote") {
+ if (RELEASE_APTLY.toBoolean()) {
+ common.infoMsg("Promoting Aptly")
+ triggerAptlyPromoteJob(APTLY_URL, 'all', false, true, 'all', false, "(.*)/${SOURCE_REVISION}", APTLY_STORAGES, "{0}/${TARGET_REVISION}")
+ }
- if(RELEASE_DEB_MIRRORS.toBoolean()){
- common.infoMsg("Promoting Debmirrors")
- triggerMirrorRepoJob(SOURCE_REVISION, TARGET_REVISION)
- }
+ if (RELEASE_DEB_MIRRORS.toBoolean()) {
+ common.infoMsg("Promoting Debmirrors")
+ triggerMirrorRepoJob(SOURCE_REVISION, TARGET_REVISION)
+ }
- if(RELEASE_DOCKER.toBoolean())
- {
- common.infoMsg("Promoting Docker images")
- triggerDockerMirrorJob(DOCKER_CREDENTIALS, DOCKER_URL, TARGET_REVISION, DOCKER_IMAGES, SOURCE_REVISION)
- }
+ if (RELEASE_DOCKER.toBoolean()) {
+ common.infoMsg("Promoting Docker images")
+ triggerDockerMirrorJob(DOCKER_CREDENTIALS, DOCKER_URL, TARGET_REVISION, DOCKER_IMAGES, SOURCE_REVISION)
+ }
- if(RELEASE_GIT.toBoolean())
- {
- common.infoMsg("Promoting Git repositories")
- triggerGitTagJob(GIT_REPO_LIST, GIT_CREDENTIALS, TARGET_REVISION, SOURCE_REVISION)
+ if (RELEASE_GIT.toBoolean()) {
+ common.infoMsg("Promoting Git repositories")
+ triggerGitTagJob(GIT_REPO_LIST, GIT_CREDENTIALS, TARGET_REVISION, SOURCE_REVISION)
- }
- if (EMAIL_NOTIFY.toBoolean()) {
- emailext(to: NOTIFY_RECIPIENTS,
- body: NOTIFY_TEXT,
- subject: "MCP Promotion has been done")
- }
- }
- } catch (Throwable e) {
+ }
+ if (RELEASE_VCP_IMAGES.toBoolean()) {
+ common.infoMsg("Promoting VCP images")
+ triggerPromoteVCPJob(VCP_IMAGE_LIST, TARGET_REVISION, SOURCE_REVISION)
+
+ }
+ if (EMAIL_NOTIFY.toBoolean()) {
+ emailext(to: NOTIFY_RECIPIENTS,
+ body: NOTIFY_TEXT,
+ subject: "MCP Promotion has been done")
+ }
+ }
+ } catch (Throwable e) {
// If there was an error or exception thrown, the build failed
currentBuild.result = "FAILURE"
throw e
- }
}
- }
+ }
+}
diff --git a/rollout-config-change.groovy b/rollout-config-change.groovy
deleted file mode 100644
index dcb9034..0000000
--- a/rollout-config-change.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/**
- * Rollout changes to the node(s) configuration
- *
- * Expected parameters:
- * TST_SALT_MASTER_CREDENTIALS Credentials to the Salt API (QA environment).
- * TST_SALT_MASTER_URL Full Salt API address [https://10.10.10.1:8000].
- * PRD_SALT_MASTER_CREDENTIALS Credentials to the Salt API (PRD environment).
- * PRD_SALT_MASTER_URL Full Salt API address [https://10.10.10.1:8000].
- * Model parameters:
- * MODEL_REPO_CREDENTIALS Credentials to the Model.
- * MODEL_REPO_URL Full model repo address.
- * MODEL_REPO_SOURCE_BRANCH Source branch to merge from.
- * MODEL_REPO_TARGET_BRANCH Target branch to merge fo.
- * Change settings:
- * TARGET_SERVERS Salt compound target to match nodes to be updated [*, G@osfamily:debian].
- * TARGET_STATES States to be applied, empty string means running highstate [linux, linux,openssh, salt.minion.grains].
- * TARGET_SUBSET_TEST Number of nodes to test config changes, empty string means all targetted nodes.
- * TARGET_SUBSET_LIVE Number of selected noded to live apply selected config changes.
- * TARGET_BATCH_LIVE Batch size for the complete live config changes on all nodes, empty string means apply to all targetted nodes.
- * Test settings:
- * TEST_SERVICE Comma separated list of services to test
- * TEST_K8S_API_SERVER Kubernetes API address
- * TEST_K8S_CONFORMANCE_IMAGE Path to docker image with conformance e2e tests
- * TEST_DOCKER_INSTALL Install docker on the target if true
- * TEST_TEMPEST_IMAGE Tempest image link
- * TEST_TEMPEST_PATTERN If not false, run tests matched to pattern only
- * TEST_TEMPEST_TARGET Salt target for tempest node
- *
-**/
-
-def common = new com.mirantis.mk.Common()
-def salt = new com.mirantis.mk.Salt()
-timeout(time: 12, unit: 'HOURS') {
- node() {
- try {
-
- stage('Run config change on test env') {
- build job: "deploy-update-service-config", parameters: [
- [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: TST_SALT_MASTER_URL],
- [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: TST_SALT_MASTER_CREDENTIALS],
- [$class: 'StringParameterValue', name: 'TARGET_BATCH_LIVE', value: TARGET_BATCH_LIVE],
- [$class: 'StringParameterValue', name: 'TARGET_SERVERS', value: TARGET_SERVERS],
- [$class: 'StringParameterValue', name: 'TARGET_STATES', value: TARGET_STATES],
- [$class: 'StringParameterValue', name: 'TARGET_SUBSET_LIVE', value: TARGET_SUBSET_LIVE],
- [$class: 'StringParameterValue', name: 'TARGET_SUBSET_TEST', value: TARGET_SUBSET_TEST],
- ]
- }
-
- stage('Test config change on test env') {
- build job: "deploy-test-service", parameters: [
- [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: TST_SALT_MASTER_URL],
- [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: TST_SALT_MASTER_CREDENTIALS],
- [$class: 'StringParameterValue', name: 'TEST_SERVICE', value: TEST_SERVICE],
- [$class: 'StringParameterValue', name: 'TEST_K8S_API_SERVER', value: TEST_K8S_API_SERVER],
- [$class: 'StringParameterValue', name: 'TEST_K8S_CONFORMANCE_IMAGE', value: TEST_K8S_CONFORMANCE_IMAGE],
- ]
- }
-
- stage('Promote config change in repo') {
- build job: "git-merge-branches", parameters: [
- [$class: 'StringParameterValue', name: 'REPO_URL', value: MODEL_REPO_URL],
- [$class: 'StringParameterValue', name: 'CREDENTIALS_ID', value: MODEL_REPO_CREDENTIALS],
- [$class: 'StringParameterValue', name: 'SOURCE_BRANCH', value: MODEL_REPO_SOURCE_BRANCH],
- [$class: 'StringParameterValue', name: 'TARGET_BRANCH', value: MODEL_REPO_TARGET_BRANCH],
- ]
- }
-
- stage('Run config change on production env') {
- build job: "deploy-update-service-config", parameters: [
- [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: PRD_SALT_MASTER_URL],
- [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: PRD_SALT_MASTER_CREDENTIALS],
- [$class: 'StringParameterValue', name: 'TARGET_BATCH_LIVE', value: TARGET_BATCH_LIVE],
- [$class: 'StringParameterValue', name: 'TARGET_SERVERS', value: TARGET_SERVERS],
- [$class: 'StringParameterValue', name: 'TARGET_STATES', value: TARGET_STATES],
- [$class: 'StringParameterValue', name: 'TARGET_SUBSET_LIVE', value: TARGET_SUBSET_LIVE],
- [$class: 'StringParameterValue', name: 'TARGET_SUBSET_TEST', value: TARGET_SUBSET_TEST],
- ]
- }
-
- stage('Test config change on prod env') {
- def result = build job: "deploy-test-service", parameters: [
- [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: PRD_SALT_MASTER_URL],
- [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: PRD_SALT_MASTER_CREDENTIALS],
- [$class: 'StringParameterValue', name: 'TEST_SERVICE', value: TEST_SERVICE],
- [$class: 'StringParameterValue', name: 'TEST_K8S_API_SERVER', value: TEST_K8S_API_SERVER],
- [$class: 'StringParameterValue', name: 'TEST_K8S_CONFORMANCE_IMAGE', value: TEST_K8S_CONFORMANCE_IMAGE],
- ]
- }
-
- } catch (Throwable e) {
- currentBuild.result = 'FAILURE'
- throw e
- }
- }
-}
diff --git a/test-cookiecutter-reclass-chunk.groovy b/test-cookiecutter-reclass-chunk.groovy
index 12428ba..9e34cea 100644
--- a/test-cookiecutter-reclass-chunk.groovy
+++ b/test-cookiecutter-reclass-chunk.groovy
@@ -1,23 +1,27 @@
package com.mirantis.mk
+
def common = new com.mirantis.mk.Common()
def saltModelTesting = new com.mirantis.mk.SaltModelTesting()
/**
* Test CC model wrapper
* EXTRA_VARIABLES_YAML: yaml based string, to be directly passed into testCCModel
+ * SLAVE_NODE:
*/
+slaveNode = env.SLAVE_NODE ?: 'python&&docker'
+
timeout(time: 1, unit: 'HOURS') {
-node() {
- try {
- extra_vars = readYaml text: EXTRA_VARIABLES_YAML
- currentBuild.description = extra_vars.modelFile
- saltModelTesting.testCCModel(extra_vars)
+ node(slaveNode) {
+ try {
+ extraVars = readYaml text: EXTRA_VARIABLES_YAML
+ currentBuild.description = extraVars.modelFile
+ saltModelTesting.testCCModel(extraVars)
} catch (Throwable e) {
- // If there was an error or exception thrown, the build failed
- currentBuild.result = "FAILURE"
- currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
- throw e
- }
- }
+ // If there was an error or exception thrown, the build failed
+ currentBuild.result = "FAILURE"
+ currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
+ throw e
}
+ }
+}
diff --git a/test-cookiecutter-reclass.groovy b/test-cookiecutter-reclass.groovy
index 2a64990..e6d3070 100644
--- a/test-cookiecutter-reclass.groovy
+++ b/test-cookiecutter-reclass.groovy
@@ -2,17 +2,19 @@
gerrit = new com.mirantis.mk.Gerrit()
git = new com.mirantis.mk.Git()
python = new com.mirantis.mk.Python()
-saltModelTesting = new com.mirantis.mk.SaltModelTesting()
-slave_node = 'python&&docker'
+gerritRef = env.GERRIT_REFSPEC ?: null
+slaveNode = (env.SLAVE_NODE ?: 'python&&docker')
+def alreadyMerged = false
+
def reclassVersion = 'v1.5.4'
if (common.validInputParam('RECLASS_VERSION')) {
- reclassVersion = RECLASS_VERSION
+ reclassVersion = RECLASS_VERSION
}
def generateSaltMaster(modEnv, clusterDomain, clusterName) {
- def nodeFile = "${modEnv}/nodes/cfg01.${clusterDomain}.yml"
- def nodeString = """classes:
+ def nodeFile = "${modEnv}/nodes/cfg01.${clusterDomain}.yml"
+ def nodeString = """classes:
- cluster.${clusterName}.infra.config
parameters:
_param:
@@ -23,82 +25,78 @@
name: cfg01
domain: ${clusterDomain}
"""
- sh "mkdir -p ${modEnv}/nodes/"
- println "Create file ${nodeFile}"
- writeFile(file: nodeFile, text: nodeString)
+ sh "mkdir -p ${modEnv}/nodes/"
+ println "Create file ${nodeFile}"
+ writeFile(file: nodeFile, text: nodeString)
}
-def GetBaseName(line, remove_ext) {
- filename = line.toString().split('/').last()
- if (remove_ext && filename.endsWith(remove_ext.toString())) {
- filename = filename.take(filename.lastIndexOf(remove_ext.toString()))
- }
- return filename
-}
+/**
+ *
+ * @param contextFile - path to `contexts/XXX.yaml file`
+ * @param virtualenv - pyvenv with CC and dep's
+ * @param templateEnvDir - root of CookieCutter
+ * @return
+ */
-def generateModel(modelFile, cutterEnv) {
- def templateEnv = "${env.WORKSPACE}"
- def modelEnv = "${env.WORKSPACE}/model"
- def basename = GetBaseName(modelFile, '.yml')
- def generatedModel = "${modelEnv}/${basename}"
- def testEnv = "${env.WORKSPACE}/test"
- def content = readFile(file: "${templateEnv}/contexts/${modelFile}")
- def templateContext = readYaml text: content
- def clusterDomain = templateContext.default_context.cluster_domain
- def clusterName = templateContext.default_context.cluster_name
- def outputDestination = "${generatedModel}/classes/cluster/${clusterName}"
- def targetBranch = "feature/${clusterName}"
- def templateBaseDir = "${env.WORKSPACE}"
- def templateDir = "${templateEnv}/dir"
- def templateOutputDir = templateBaseDir
- sh(script: "rm -rf ${generatedModel} || true")
+def generateModel(contextFile, virtualenv, templateEnvDir) {
+ def modelEnv = "${templateEnvDir}/model"
+ def basename = common.GetBaseName(contextFile, '.yml')
+ def generatedModel = "${modelEnv}/${basename}"
+ def content = readFile(file: "${templateEnvDir}/contexts/${contextFile}")
+ def templateContext = readYaml text: content
+ def clusterDomain = templateContext.default_context.cluster_domain
+ def clusterName = templateContext.default_context.cluster_name
+ def outputDestination = "${generatedModel}/classes/cluster/${clusterName}"
+ def templateBaseDir = templateEnvDir
+ def templateDir = "${templateEnvDir}/dir"
+ def templateOutputDir = templateBaseDir
+ dir(templateEnvDir) {
+ sh(script: "rm -rf ${generatedModel} || true")
+ common.infoMsg("Generating model from context ${contextFile}")
+ def productList = ["infra", "cicd", "opencontrail", "kubernetes", "openstack", "oss", "stacklight", "ceph"]
+ for (product in productList) {
- common.infoMsg("Generating model from context ${modelFile}")
+ // get templateOutputDir and productDir
+ if (product.startsWith("stacklight")) {
+ templateOutputDir = "${templateEnvDir}/output/stacklight"
+ try {
+ productDir = "stacklight" + templateContext.default_context['stacklight_version']
+ } catch (Throwable e) {
+ productDir = "stacklight1"
+ }
+ } else {
+ templateOutputDir = "${templateEnvDir}/output/${product}"
+ productDir = product
+ }
- def productList = ["infra", "cicd", "opencontrail", "kubernetes", "openstack", "oss", "stacklight", "ceph"]
- for (product in productList) {
+ if (product == "infra" || (templateContext.default_context["${product}_enabled"]
+ && templateContext.default_context["${product}_enabled"].toBoolean())) {
- // get templateOutputDir and productDir
- if (product.startsWith("stacklight")) {
- templateOutputDir = "${env.WORKSPACE}/output/stacklight"
- try {
- productDir = "stacklight" + templateContext.default_context['stacklight_version']
- } catch (Throwable e) {
- productDir = "stacklight1"
- }
- } else {
- templateOutputDir = "${env.WORKSPACE}/output/${product}"
- productDir = product
+ templateDir = "${templateEnvDir}/cluster_product/${productDir}"
+ common.infoMsg("Generating product " + product + " from " + templateDir + " to " + templateOutputDir)
+
+ sh "rm -rf ${templateOutputDir} || true"
+ sh "mkdir -p ${templateOutputDir}"
+ sh "mkdir -p ${outputDestination}"
+
+ python.buildCookiecutterTemplate(templateDir, content, templateOutputDir, virtualenv, templateBaseDir)
+ sh "mv -v ${templateOutputDir}/${clusterName}/* ${outputDestination}"
+ } else {
+ common.warningMsg("Product " + product + " is disabled")
+ }
+ }
+ generateSaltMaster(generatedModel, clusterDomain, clusterName)
}
-
- if (product == "infra" || (templateContext.default_context["${product}_enabled"]
- && templateContext.default_context["${product}_enabled"].toBoolean())) {
-
- templateDir = "${templateEnv}/cluster_product/${productDir}"
- common.infoMsg("Generating product " + product + " from " + templateDir + " to " + templateOutputDir)
-
- sh "rm -rf ${templateOutputDir} || true"
- sh "mkdir -p ${templateOutputDir}"
- sh "mkdir -p ${outputDestination}"
-
- python.buildCookiecutterTemplate(templateDir, content, templateOutputDir, cutterEnv, templateBaseDir)
- sh "mv -v ${templateOutputDir}/${clusterName}/* ${outputDestination}"
- } else {
- common.warningMsg("Product " + product + " is disabled")
- }
- }
- generateSaltMaster(generatedModel, clusterDomain, clusterName)
}
-def testModel(modelFile, testEnv, reclassVersion='v1.5.4') {
- // modelFile - `modelfiname` from model/modelfiname/modelfiname.yaml
- // testEnv - path for model (model/modelfilename/)
- //* Grub all models and send it to check in paralell - by one in thread.
+def testModel(modelFile, reclassVersion = 'v1.5.4') {
+ // modelFile - `modelfiname` from model/modelfiname/modelfiname.yaml
+ //* Grub all models and send it to check in paralell - by one in thread.
- _values_string = """
+ _values_string = """
---
- MODELS_TARGZ: "${env.BUILD_URL}/artifact/reclass.tar.gz"
+ MODELS_TARGZ: "${env.BUILD_URL}/artifact/patched_reclass.tar.gz"
DockerCName: "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}_${modelFile.toLowerCase()}"
testReclassEnv: "model/${modelFile}/"
modelFile: "contexts/${modelFile}.yml"
@@ -106,143 +104,197 @@
EXTRA_FORMULAS: "${env.EXTRA_FORMULAS}"
reclassVersion: "${reclassVersion}"
"""
- build job: "test-mk-cookiecutter-templates-chunk", parameters: [
- [$class: 'StringParameterValue', name: 'EXTRA_VARIABLES_YAML', value: _values_string.stripIndent() ],
- ]
+ build job: "test-mk-cookiecutter-templates-chunk", parameters: [
+ [$class: 'StringParameterValue', name: 'EXTRA_VARIABLES_YAML',
+ value : _values_string.stripIndent()],
+ ]
}
-def gerritRef
-try {
- gerritRef = GERRIT_REFSPEC
- } catch (MissingPropertyException e) {
- gerritRef = null
- }
-
-def testModelStep(basename,testEnv) {
- // We need to wrap what we return in a Groovy closure, or else it's invoked
- // when this method is called, not when we pass it to parallel.
- // To do this, you need to wrap the code below in { }, and either return
- // that explicitly, or use { -> } syntax.
- return {
- node(slave_node) {
- testModel(basename, testEnv)
+def StepTestModel(basename) {
+ // We need to wrap what we return in a Groovy closure, or else it's invoked
+ // when this method is called, not when we pass it to parallel.
+ // To do this, you need to wrap the code below in { }, and either return
+ // that explicitly, or use { -> } syntax.
+ // return node object
+ return {
+ node(slaveNode) {
+ testModel(basename)
+ }
}
- }
}
-timeout(time: 2, unit: 'HOURS') {
- node(slave_node) {
- def templateEnv = "${env.WORKSPACE}"
- def cutterEnv = "${env.WORKSPACE}/cutter"
- def jinjaEnv = "${env.WORKSPACE}/jinja"
-
- try {
- // Fixme. Just use 'cleanup workspace' option.
- stage("Cleanup") {
- sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
- }
-
- stage('Download Cookiecutter template') {
- if (gerritRef) {
- def gerritChange = gerrit.getGerritChange(GERRIT_NAME, GERRIT_HOST, GERRIT_CHANGE_NUMBER, CREDENTIALS_ID)
- merged = gerritChange.status == "MERGED"
- if (!merged) {
- checkouted = gerrit.gerritPatchsetCheckout([
- credentialsId: CREDENTIALS_ID
- ])
- } else {
- common.successMsg("Change ${GERRIT_CHANGE_NUMBER} is already merged, no need to gate them")
- }
- } else {
- git.checkoutGitRepository(templateEnv, COOKIECUTTER_TEMPLATE_URL, COOKIECUTTER_TEMPLATE_BRANCH, CREDENTIALS_ID)
- }
- }
-
- stage("Setup") {
- python.setupCookiecutterVirtualenv(cutterEnv)
- }
-
- stage("Check workflow_definition") {
- sh(script: "python ${env.WORKSPACE}/workflow_definition_test.py")
- }
-
- def contextFileList = []
- dir("${templateEnv}/contexts") {
- for (String x : findFiles(glob: "*.yml")) {
- contextFileList.add(x)
- }
- }
-
- stage("generate-model") {
- for (contextFile in contextFileList) {
- generateModel(contextFile, cutterEnv)
- }
- }
-
- dir("${env.WORKSPACE}") {
- // Collect only models. For backward compatability - who know, probably someone use it..
- sh(script: "tar -czf model.tar.gz -C model ../contexts .", returnStatus: true)
- archiveArtifacts artifacts: "model.tar.gz"
- // to be able share reclass for all subenvs
- // Also, makes artifact test more solid - use one reclass for all of sub-models.
- // Archive Structure will be:
- // tar.gz
- // ├── contexts
- // │ └── ceph.yml
- // ├── global_reclass <<< reclass system
- // ├── model
- // │ └── ceph <<< from `context basename`
- // │ ├── classes
- // │ │ ├── cluster
- // │ │ └── system -> ../../../global_reclass
- // │ └── nodes
- // │ └── cfg01.ceph-cluster-domain.local.yml
-
- if (SYSTEM_GIT_URL == "") {
- git.checkoutGitRepository("${env.WORKSPACE}/global_reclass/", RECLASS_MODEL_URL, RECLASS_MODEL_BRANCH, CREDENTIALS_ID)
- } else {
- dir("${env.WORKSPACE}/global_reclass/") {
- if (!gerrit.gerritPatchsetCheckout(SYSTEM_GIT_URL, SYSTEM_GIT_REF, "HEAD", CREDENTIALS_ID)) {
- common.errorMsg("Failed to obtain system reclass with url: ${SYSTEM_GIT_URL} and ${SYSTEM_GIT_REF}")
- throw new RuntimeException("Failed to obtain system reclass")
+def StepPrepareCCenv(refchange, templateEnvFolder) {
+ // return git clone object
+ return {
+ // fetch needed sources
+ dir(templateEnvFolder) {
+ if (refchange) {
+ def gerritChange = gerrit.getGerritChange(GERRIT_NAME, GERRIT_HOST, GERRIT_CHANGE_NUMBER, CREDENTIALS_ID)
+ merged = gerritChange.status == "MERGED"
+ if (!merged) {
+ checkouted = gerrit.gerritPatchsetCheckout([
+ credentialsId: CREDENTIALS_ID
+ ])
+ } else {
+ // update global variable for success return from pipeline
+ //alreadyMerged = true
+ common.successMsg("Change ${GERRIT_CHANGE_NUMBER} is already merged, no need to gate them")
+ currentBuild.result = 'ABORTED'
+ throw new hudson.AbortException('change already merged')
}
- }
+ } else {
+ git.checkoutGitRepository(templateEnvFolder, COOKIECUTTER_TEMPLATE_URL, COOKIECUTTER_TEMPLATE_BRANCH, CREDENTIALS_ID)
}
- // link all models, to use one global reclass
- for (String context : contextFileList) {
- def basename = GetBaseName(context, '.yml')
- dir("${env.WORKSPACE}/model/${basename}"){
- sh(script: 'mkdir -p classes/; ln -sfv ../../../global_reclass classes/system ')
- }
- }
- // Save all models and all contexts. Warning! `h` flag has been used.
- sh(script: "tar -chzf reclass.tar.gz --exclude='*@tmp' model contexts global_reclass", returnStatus: true)
- archiveArtifacts artifacts: "reclass.tar.gz"
- }
-
- stage("test-contexts") {
- stepsForParallel = [:]
- common.infoMsg("Found: ${contextFileList.size()} contexts to test.")
- for (String context : contextFileList) {
- def basename = GetBaseName(context, '.yml')
- def testEnv = "${env.WORKSPACE}/model/${basename}"
- stepsForParallel.put("Test:${basename}", testModelStep(basename, testEnv))
- }
- parallel stepsForParallel
- common.infoMsg('All tests done')
- }
-
- stage('Clean workspace directories') {
- sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
- }
-
-} catch (Throwable e) {
- currentBuild.result = "FAILURE"
- currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
- throw e
- } finally {
- def dummy = "dummy"
- //FAILING common.sendNotification(currentBuild.result,"",["slack"])
+ }
}
- }
+}
+
+def StepGenerateModels(_contextFileList, _virtualenv, _templateEnvDir) {
+ return {
+ for (contextFile in _contextFileList) {
+ generateModel(contextFile, _virtualenv, _templateEnvDir)
+ }
+ }
+}
+
+timeout(time: 1, unit: 'HOURS') {
+ node(slaveNode) {
+ def templateEnvHead = "${env.WORKSPACE}/EnvHead/"
+ def templateEnvPatched = "${env.WORKSPACE}/EnvPatched/"
+ def contextFileListHead = []
+ def contextFileListPatched = []
+ def vEnv = "${env.WORKSPACE}/venv"
+
+ try {
+ sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
+ stage('Download and prepare CC env') {
+ // Prepare 2 env - for patchset, and for HEAD
+ paralellEnvs = [:]
+ paralellEnvs.failFast = true
+ paralellEnvs['downloadEnvHead'] = StepPrepareCCenv('', templateEnvHead)
+ paralellEnvs['downloadEnvPatched'] = StepPrepareCCenv(gerritRef, templateEnvPatched)
+ parallel paralellEnvs
+ }
+ stage("Check workflow_definition") {
+ // Check only for patchset
+ python.setupVirtualenv(vEnv, 'python2', [], "${templateEnvPatched}/requirements.txt")
+ common.infoMsg(python.runVirtualenvCommand(vEnv, "python ${templateEnvPatched}/workflow_definition_test.py"))
+ }
+
+ stage("generate models") {
+ dir("${templateEnvHead}/contexts") {
+ for (String x : findFiles(glob: "*.yml")) {
+ contextFileListHead.add(x)
+ }
+ }
+ dir("${templateEnvPatched}/contexts") {
+ for (String x : findFiles(glob: "*.yml")) {
+ contextFileListPatched.add(x)
+ }
+ }
+ // Generate over 2env's - for patchset, and for HEAD
+ paralellEnvs = [:]
+ paralellEnvs.failFast = true
+ paralellEnvs['GenerateEnvPatched'] = StepGenerateModels(contextFileListPatched, vEnv, templateEnvPatched)
+ paralellEnvs['GenerateEnvHead'] = StepGenerateModels(contextFileListHead, vEnv, templateEnvHead)
+ parallel paralellEnvs
+
+ // Collect artifacts
+ dir(templateEnvPatched) {
+ // Collect only models. For backward comparability - who know, probably someone use it..
+ sh(script: "tar -czf model.tar.gz -C model ../contexts .", returnStatus: true)
+ archiveArtifacts artifacts: "model.tar.gz"
+ }
+
+ // to be able share reclass for all subenvs
+ // Also, makes artifact test more solid - use one reclass for all of sub-models.
+ // Archive Structure will be:
+ // tar.gz
+ // ├── contexts
+ // │ └── ceph.yml
+ // ├── global_reclass <<< reclass system
+ // ├── model
+ // │ └── ceph <<< from `context basename`
+ // │ ├── classes
+ // │ │ ├── cluster
+ // │ │ └── system -> ../../../global_reclass
+ // │ └── nodes
+ // │ └── cfg01.ceph-cluster-domain.local.yml
+
+ if (SYSTEM_GIT_URL == "") {
+ git.checkoutGitRepository("${env.WORKSPACE}/global_reclass/", RECLASS_MODEL_URL, RECLASS_MODEL_BRANCH, CREDENTIALS_ID)
+ } else {
+ dir("${env.WORKSPACE}/global_reclass/") {
+ if (!gerrit.gerritPatchsetCheckout(SYSTEM_GIT_URL, SYSTEM_GIT_REF, "HEAD", CREDENTIALS_ID)) {
+ common.errorMsg("Failed to obtain system reclass with url: ${SYSTEM_GIT_URL} and ${SYSTEM_GIT_REF}")
+ throw new RuntimeException("Failed to obtain system reclass")
+ }
+ }
+ }
+ // link all models, to use one global reclass
+ // For HEAD
+ dir(templateEnvHead) {
+ for (String context : contextFileListHead) {
+ def basename = common.GetBaseName(context, '.yml')
+ dir("${templateEnvHead}/model/${basename}") {
+ sh(script: 'mkdir -p classes/; ln -sfv ../../../../global_reclass classes/system ')
+ }
+ }
+ // Save all models and all contexts. Warning! `h` flag must be used.
+ sh(script: "tar -chzf head_reclass.tar.gz --exclude='*@tmp' model contexts global_reclass", returnStatus: true)
+ archiveArtifacts artifacts: "head_reclass.tar.gz"
+ // move for "Compare Pillars" stage
+ sh(script: "mv -v head_reclass.tar.gz ${env.WORKSPACE}")
+ }
+ // For patched
+ dir(templateEnvPatched) {
+ for (String context : contextFileListPatched) {
+ def basename = common.GetBaseName(context, '.yml')
+ dir("${templateEnvPatched}/model/${basename}") {
+ sh(script: 'mkdir -p classes/; ln -sfv ../../../../global_reclass classes/system ')
+ }
+ }
+ // Save all models and all contexts. Warning! `h` flag must be used.
+ sh(script: "tar -chzf patched_reclass.tar.gz --exclude='*@tmp' model contexts global_reclass", returnStatus: true)
+ archiveArtifacts artifacts: "patched_reclass.tar.gz"
+ // move for "Compare Pillars" stage
+ sh(script: "mv -v patched_reclass.tar.gz ${env.WORKSPACE}")
+ }
+ }
+
+ stage("Compare Pillars") {
+ // Compare patched and HEAD reclass pillars
+ compareRoot = "${env.WORKSPACE}/test_compare/"
+ sh(script: """
+ mkdir -pv ${compareRoot}/new ${compareRoot}/old
+ tar -xzf patched_reclass.tar.gz --directory ${compareRoot}/new
+ tar -xzf head_reclass.tar.gz --directory ${compareRoot}/old
+ """)
+ common.warningMsg('infra/secrets.yml has been skipped from compare!')
+ rezult = common.comparePillars(compareRoot, env.BUILD_URL, "-Ev \'infra/secrets.yml\'")
+ currentBuild.description = rezult
+ }
+ stage("test-contexts") {
+ // Test contexts for patched only
+ stepsForParallel = [:]
+ common.infoMsg("Found: ${contextFileListPatched.size()} patched contexts to test.")
+ for (String context : contextFileListPatched) {
+ def basename = common.GetBaseName(context, '.yml')
+ stepsForParallel.put("ContextPatchTest:${basename}", StepTestModel(basename))
+ }
+ parallel stepsForParallel
+ common.infoMsg('All tests done')
+ }
+
+ sh(script: 'find . -mindepth 1 -delete > /dev/null || true')
+
+ } catch (Throwable e) {
+ currentBuild.result = "FAILURE"
+ currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
+ throw e
+ } finally {
+ def dummy = "dummy"
+ //FAILING common.sendNotification(currentBuild.result,"",["slack"])
+ }
+ }
}
diff --git a/test-service.groovy b/test-service.groovy
deleted file mode 100644
index f9c34e3..0000000
--- a/test-service.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- *
- * Service test pipeline
- *
- * Expected parameters:
- * SALT_MASTER_URL URL of Salt master
- * SALT_MASTER_CREDENTIALS Credentials to the Salt API
- * Test settings:
- * TEST_SERVICE Comma separated list of services to test
- * TEST_K8S_API_SERVER Kubernetes API address
- * TEST_K8S_CONFORMANCE_IMAGE Path to docker image with conformance e2e tests
- * TEST_DOCKER_INSTALL Install docker on the target if true
- * TEST_TEMPEST_IMAGE Tempest image link
- * TEST_TEMPEST_PATTERN If not false, run tests matched to pattern only
- * TEST_TEMPEST_TARGET Salt target for tempest node
- *
- */
-
-common = new com.mirantis.mk.Common()
-git = new com.mirantis.mk.Git()
-salt = new com.mirantis.mk.Salt()
-test = new com.mirantis.mk.Test()
-def python = new com.mirantis.mk.Python()
-
-def pepperEnv = "pepperEnv"
-timeout(time: 12, unit: 'HOURS') {
- node("python") {
- try {
-
- stage('Setup virtualenv for Pepper') {
- python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
- }
-
- //
- // Test
- //
- def artifacts_dir = '_artifacts/'
-
- if (common.checkContains('TEST_SERVICE', 'k8s')) {
- stage('Run k8s bootstrap tests') {
- def image = 'tomkukral/k8s-scripts'
- def output_file = image.replaceAll('/', '-') + '.output'
-
- // run image
- test.runConformanceTests(pepperEnv, 'ctl01*', TEST_K8S_API_SERVER, image)
-
- // collect output
- sh "mkdir -p ${artifacts_dir}"
- file_content = salt.getFileContent(pepperEnv, 'ctl01*', '/tmp/' + output_file)
- writeFile file: "${artifacts_dir}${output_file}", text: file_content
- sh "cat ${artifacts_dir}${output_file}"
-
- // collect artifacts
- archiveArtifacts artifacts: "${artifacts_dir}${output_file}"
- }
-
- stage('Run k8s conformance e2e tests') {
- def image = K8S_CONFORMANCE_IMAGE
- def output_file = image.replaceAll('/', '-') + '.output'
-
- // run image
- test.runConformanceTests(pepperEnv, 'ctl01*', TEST_K8S_API_SERVER, image)
-
- // collect output
- sh "mkdir -p ${artifacts_dir}"
- file_content = salt.getFileContent(pepperEnv, 'ctl01*', '/tmp/' + output_file)
- writeFile file: "${artifacts_dir}${output_file}", text: file_content
- sh "cat ${artifacts_dir}${output_file}"
-
- // collect artifacts
- archiveArtifacts artifacts: "${artifacts_dir}${output_file}"
- }
- }
-
- if (common.checkContains('TEST_SERVICE', 'openstack')) {
- if (common.checkContains('TEST_DOCKER_INSTALL', 'true')) {
- test.install_docker(pepperEnv, TEST_TEMPEST_TARGET)
- }
-
- stage('Run OpenStack tests') {
- test.runTempestTests(pepperEnv, TEST_TEMPEST_IMAGE, TEST_TEMPEST_TARGET, TEST_TEMPEST_PATTERN)
- }
-
- writeFile(file: 'report.xml', text: salt.getFileContent(pepperEnv, TEST_TEMPEST_TARGET, '/root/report.xml'))
- junit(keepLongStdio: true, testResults: 'report.xml', healthScaleFactor: Double.parseDouble(TEST_JUNIT_RATIO))
- def testResults = test.collectJUnitResults(currentBuild.rawBuild.getAction(hudson.tasks.test.AbstractTestResultAction.class))
- if(testResults){
- currentBuild.desc = String.format("result: %s", testResults["failed"] / testResults["total"])
- }
- }
- } catch (Throwable e) {
- currentBuild.result = 'FAILURE'
- throw e
- }
- }
-}
diff --git a/xtrabackup-restore-mysql-db.groovy b/xtrabackup-restore-mysql-db.groovy
index da3d463..b1d4a4e 100644
--- a/xtrabackup-restore-mysql-db.groovy
+++ b/xtrabackup-restore-mysql-db.groovy
@@ -67,7 +67,7 @@
if(backup_dir == null || backup_dir.isEmpty()) { backup_dir='/var/backups/mysql/xtrabackup' }
print(backup_dir)
salt.runSaltProcessStep(pepperEnv, 'I@galera:master', 'file.remove', ["${backup_dir}/dbrestored"], null, true)
- salt.cmdRun(pepperEnv, 'I@xtrabackup:client', "su root -c 'salt-call state.sls xtrabackup'")
+ salt.runSaltProcessStep(pepperEnv, 'I@galera:master', 'state.apply', ["xtrabackup.client.restore"], null, true)
salt.runSaltProcessStep(pepperEnv, 'I@galera:master', 'service.start', ['mysql'], null, true)
// wait until mysql service on galera master is up