Merge "Implement patch-diff artifacts for test-cookiecutter-reclass"
diff --git a/cvp-spt.groovy b/cvp-spt.groovy
index ea4680f..b9d53d5 100644
--- a/cvp-spt.groovy
+++ b/cvp-spt.groovy
@@ -44,7 +44,7 @@
                  file: "report.xml",
                  nodeType: 'NODESET',
                  url: '',
-                 xpath: '/testsuite/testcase[@classname="cvp-spt.cvp_spt.tests.test_hw2hw"]/properties/property']]
+                 xpath: '/testsuite/testcase[@classname="cvp_spt.tests.test_hw2hw"]/properties/property']]
             plot csvFileName: 'plot-8634d2fe-dc48-4713-99f9-b69a381483bc.csv',
                  group: 'SPT',
                  style: 'line',
@@ -53,7 +53,7 @@
                  file: "report.xml",
                  nodeType: 'NODESET',
                  url: '',
-                 xpath: '/testsuite/testcase[@classname="cvp-spt.cvp_spt.tests.test_vm2vm"]/properties/property']]
+                 xpath: '/testsuite/testcase[@classname="cvp_spt.tests.test_vm2vm"]/properties/property']]
         }
     } catch (Throwable e) {
         // If there was an error or exception thrown, the build failed
diff --git a/test-salt-formulas-pipeline.groovy b/test-salt-formulas-pipeline.groovy
index ca4eb67..0caef9c 100644
--- a/test-salt-formulas-pipeline.groovy
+++ b/test-salt-formulas-pipeline.groovy
@@ -4,28 +4,19 @@
  *  DEFAULT_GIT_URL
  *  CREDENTIALS_ID
  *  KITCHEN_TESTS_PARALLEL
- *  RUN_TEST_IN_DOCKER     If true, run test stage in docker
  *  SMOKE_TEST_DOCKER_IMG  Docker image for run test (default "ubuntu:16.04")
  */
 common = new com.mirantis.mk.Common()
 def gerrit = new com.mirantis.mk.Gerrit()
 def ruby = new com.mirantis.mk.Ruby()
 
-def gerritRef
-try {
-  gerritRef = GERRIT_REFSPEC
-} catch (MissingPropertyException e) {
-  gerritRef = null
-}
-
-def defaultGitRef, defaultGitUrl
-try {
-  defaultGitRef = DEFAULT_GIT_REF
-  defaultGitUrl = DEFAULT_GIT_URL
-} catch (MissingPropertyException e) {
-  defaultGitRef = null
-  defaultGitUrl = null
-}
+def gerritRef = env.GERRIT_REFSPEC ?: null
+def defaultGitRef = env.DEFAULT_GIT_REF ?: null
+def defaultGitUrl = env.DEFAULT_GIT_URL ?: null
+def slaveNode = env.SLAVE_NODE ?: 'python&&docker'
+def saltVersion = env.SALT_VERSION ?: ""
+def dockerLib = new com.mirantis.mk.Docker()
+def img = dockerLib.getImage(env.SMOKE_TEST_DOCKER_IMG, "ubuntu:16.04")
 
 def checkouted = false
 
@@ -70,9 +61,13 @@
     [$class: 'StringParameterValue', name: 'SALT_VERSION', value: SALT_VERSION]
   ]
 }
-timeout(time: 12, unit: 'HOURS') {
-  node("python") {
+timeout(time: 2, unit: 'HOURS') {
+  node(slaveNode) {
     try {
+      if (fileExists("tests/build")) {
+        common.infoMsg('Cleaning test env')
+        sh ("sudo rm -rf tests/build")
+      }
       stage("checkout") {
         if (gerritRef) {
           // job is triggered by Gerrit
@@ -102,39 +97,39 @@
           throw new Exception("Cannot checkout gerrit patchset, GERRIT_REFSPEC and DEFAULT_GIT_REF is null")
         }
     }
-    stage("test") {
-      if (checkouted) {
-        try {
-          saltVersion = SALT_VERSION
-            } catch (MissingPropertyException e) {
-          saltVersion = "" // default value is empty string, means latest
-        }
-        withEnv(["SALT_VERSION=${saltVersion}"]) {
-          boolean run_test_in_docker = (env.RUN_TEST_IN_DOCKER ?: false).toBoolean()
-          if (run_test_in_docker) {
-            def dockerLib = new com.mirantis.mk.Docker()
-            def img = dockerLib.getImage(env.SMOKE_TEST_DOCKER_IMG, "ubuntu:16.04")
-            def workspace = common.getWorkspace()
-            img.inside("-u root:root -v ${workspace}/:/formula/") {
-              sh("""cd /etc/apt/ && echo > sources.list \
-              && echo "deb [arch=amd64] http://cz.archive.ubuntu.com/ubuntu xenial main restricted universe multiverse" >> sources.list \
-              && echo "deb [arch=amd64] http://cz.archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse" >> sources.list \
-              && echo "deb [arch=amd64] http://cz.archive.ubuntu.com/ubuntu xenial-backports main restricted universe multiverse" >> sources.list \
-              && echo 'Acquire::Languages "none";' > apt.conf.d/docker-no-languages \
-              && echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > apt.conf.d/docker-gzip-indexes \
-              && echo 'APT::Get::Install-Recommends "false"; APT::Get::Install-Suggests "false";' > apt.conf.d/docker-recommends \
-              && apt-get update \
-              && apt-get install -y git-core wget curl apt-transport-https \
-              && apt-get install -y python-pip python3-pip python-virtualenv python3-virtualenv python-yaml autoconf build-essential""")
-              sh("cd /formula/ && make clean && make test")
+      stage("test") {
+        if (checkouted) {
+          try {
+            // TODO add try\finally for image-stuck case. (copy-paste from SaltModelTesting)
+            withEnv(["SALT_VERSION=${saltVersion}"]) {
+              img.inside("-v ${env.WORKSPACE}/:/formula/ -u root:root --cpus=4 --ulimit nofile=4096:8192") {
+                sh('''#!/bin/bash -xe
+                      cd /etc/apt/
+                      echo "deb [arch=amd64] http://cz.archive.ubuntu.com/ubuntu xenial main restricted universe" > sources.list
+                      echo "deb [arch=amd64] http://cz.archive.ubuntu.com/ubuntu xenial-updates main restricted universe" >> sources.list
+                      echo 'Acquire::Languages "none";' > apt.conf.d/docker-no-languages
+                      echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > apt.conf.d/docker-gzip-indexes
+                      echo 'APT::Get::Install-Recommends "false"; APT::Get::Install-Suggests "false";' > apt.conf.d/docker-recommends
+                      apt-get update
+                      apt-get install -y git-core wget curl apt-transport-https
+                      apt-get install -y python-pip python3-pip python-virtualenv python3-virtualenv python-yaml autoconf build-essential
+                      cd /formula/
+                      make clean
+                      make test
+                      make clean
+                      ''')
+              }
             }
-          } else {
-            common.warningMsg("Those tests should be always be run in clean env! Recommends to use docker env!")
-            sh("make clean && make test")
+          }
+          finally {
+            if (fileExists("tests/build")) {
+              common.infoMsg('Cleaning test env')
+              sh ("sudo rm -rf tests/build")
+            }
           }
         }
+
       }
-    }
     stage("kitchen") {
         if (checkouted) {
           if (fileExists(".kitchen.yml")) {
diff --git a/update-package.groovy b/update-package.groovy
index 790e2ac..10f3a85 100644
--- a/update-package.groovy
+++ b/update-package.groovy
@@ -11,12 +11,10 @@
  *   TARGET_BATCH_LIVE          Batch size for the complete live package update on all nodes, empty string means apply to all targetted nodes.
  *
 **/
-
+pepperEnv = "pepperEnv"
+salt = new com.mirantis.mk.Salt()
 def common = new com.mirantis.mk.Common()
-def salt = new com.mirantis.mk.Salt()
 def python = new com.mirantis.mk.Python()
-
-def pepperEnv = "pepperEnv"
 def targetTestSubset
 def targetLiveSubset
 def targetLiveAll
@@ -25,6 +23,10 @@
 def packages
 def command
 def commandKwargs
+def installSaltStack(target, pkgs){
+    salt.runSaltProcessStep(pepperEnv, target, 'pkg.install', ["force_yes=True", "pkgs='$pkgs'"], null, true, 30)
+}
+
 timeout(time: 12, unit: 'HOURS') {
     node() {
         try {
@@ -89,8 +91,27 @@
             }
 
             stage('Apply package upgrades on sample') {
+                if(packages == null || packages.contains("salt-master") || packages.contains("salt-common") || packages.contains("salt-minion") || packages.contains("salt-api")){
+                    def saltTargets = (targetLiveSubset.split(' or ').collect{it as String})
+                    for(int i = 0; i < saltTargets.size(); i++ ){
+                        common.infoMsg("During salt-minion upgrade on cfg node, pipeline lose connectivy to salt-master for 2 min. If pipeline ended with error rerun pipeline again.")
+                        common.retry(10, 5) {
+                            if(salt.minionsReachable(pepperEnv, 'I@salt:master', "I@salt:master and ${saltTargets[i]}")){
+                                installSaltStack("I@salt:master and ${saltTargets[i]}", '["salt-master", "salt-common", "salt-api", "salt-minion"]')
+                            }
+                            if(salt.minionsReachable(pepperEnv, 'I@salt:master', "I@salt:minion and not I@salt:master and ${saltTargets[i]}")){
+                                installSaltStack("I@salt:minion and not I@salt:master and ${saltTargets[i]}", '["salt-minion"]')
+                            }
+                        }
+                    }
+                }
                 out = salt.runSaltCommand(pepperEnv, 'local', ['expression': targetLiveSubset, 'type': 'compound'], command, null, packages, commandKwargs)
                 salt.printSaltCommandResult(out)
+                for(value in out.get("return")[0].values()){
+                    if (value.containsKey('result') && value.result == false) {
+                        throw new Exception("The package upgrade on sample node has failed. Please check the Salt run result above for more information.")
+                    }
+                }
             }
 
             stage('Confirm package upgrades on all nodes') {
@@ -100,8 +121,30 @@
             }
 
             stage('Apply package upgrades on all nodes') {
+
+                if(packages == null || packages.contains("salt-master") || packages.contains("salt-common") || packages.contains("salt-minion") || packages.contains("salt-api")){
+                    def saltTargets = (targetLiveAll.split(' or ').collect{it as String})
+                    for(int i = 0; i < saltTargets.size(); i++ ){
+                        common.infoMsg("During salt-minion upgrade on cfg node, pipeline lose connectivy to salt-master for 2 min. If pipeline ended with error rerun pipeline again.")
+                        common.retry(10, 5) {
+                            if(salt.minionsReachable(pepperEnv, 'I@salt:master', "I@salt:master and ${saltTargets[i]}")){
+                                installSaltStack("I@salt:master and ${saltTargets[i]}", '["salt-master", "salt-common", "salt-api", "salt-minion"]')
+                            }
+                            if(salt.minionsReachable(pepperEnv, 'I@salt:master', "I@salt:minion and not I@salt:master and ${saltTargets[i]}")){
+                                installSaltStack("I@salt:minion and not I@salt:master and ${saltTargets[i]}", '["salt-minion"]')
+                            }
+                        }
+                    }
+                }
+
                 out = salt.runSaltCommand(pepperEnv, 'local', ['expression': targetLiveAll, 'type': 'compound'], command, null, packages, commandKwargs)
                 salt.printSaltCommandResult(out)
+                for(value in out.get("return")[0].values()){
+                    if (value.containsKey('result') && value.result == false) {
+                        throw new Exception("The package upgrade on sample node has failed. Please check the Salt run result above for more information.")
+                    }
+                }
+                common.warningMsg("Pipeline has finished successfully, but please, check if any packages have been kept back.")
             }
 
         } catch (Throwable e) {
diff --git a/upgrade-mcp-release.groovy b/upgrade-mcp-release.groovy
index fb291ab..d5c0e77 100644
--- a/upgrade-mcp-release.groovy
+++ b/upgrade-mcp-release.groovy
@@ -64,7 +64,12 @@
     archiveArtifacts artifacts: "$filename"
 }
 
-timeout(time: 12, unit: 'HOURS') {
+def pipelineTimeout = 12
+if (common.validInputParam('PIPELINE_TIMEOUT') && PIPELINE_TIMEOUT.isInteger()) {
+    pipelineTimeout = "${PIPELINE_TIMEOUT}".toInteger()
+}
+
+timeout(time: pipelineTimeout, unit: 'HOURS') {
     node("python") {
         try {
             def gitMcpVersion = MCP_VERSION