Using salt-pepper to connect SALT_MASTER from Jenkins slaves

Due to specific Jenkins behavior of groovy interpreter it is impossible
to connect to SALT_MASTER_IP from Jenkins slaves. The solution has been
suggested by Jakub Josef so in order to cope with this issue salt-pepper
is used to connect to SALT_MASTER_IP from slaves.

Change-Id: I6537de0937383f6688224e609cb730e62a993b37
diff --git a/src/com/mirantis/mk/Python.groovy b/src/com/mirantis/mk/Python.groovy
index b95275f..29f471f 100644
--- a/src/com/mirantis/mk/Python.groovy
+++ b/src/com/mirantis/mk/Python.groovy
@@ -283,3 +283,13 @@
     echo(data)
     return data
 }
+
+/**
+ * Install salt-pepper in isolated environment
+ *
+ * @param path        Path where virtualenv is created
+ */
+def setupPepperVirtualenv(path) {
+    requirements = ['salt-pepper']
+    setupVirtualenv(path, 'python2', requirements)
+}
\ No newline at end of file
diff --git a/src/com/mirantis/mk/Salt.groovy b/src/com/mirantis/mk/Salt.groovy
index 755dcd5..853965e 100644
--- a/src/com/mirantis/mk/Salt.groovy
+++ b/src/com/mirantis/mk/Salt.groovy
@@ -22,7 +22,6 @@
         "creds": common.getCredentials(credentialsId)
     ]
     params["authToken"] = saltLogin(params)
-
     return params
 }
 
@@ -43,7 +42,7 @@
 }
 
 /**
- * Run action using Salt API
+ * Run action using Salt API (using plain HTTP request from Jenkins master)
  *
  * @param master   Salt connection object
  * @param client   Client type
@@ -66,7 +65,6 @@
         'client': client,
         'expr_form': target.type,
     ]
-
     if(batch != null && ( (batch instanceof Integer && batch > 0) || (batch instanceof String && batch.contains("%")))){
         data['client']= "local_batch"
         data['batch'] = batch
@@ -92,6 +90,54 @@
 }
 
 /**
+ * Run action using Salt API (using salt pepper from slave shell)
+ *
+ * @param master   Salt connection object
+ * @param client   Client type
+ * @param target   Target specification, eg. for compound matches by Pillar
+ *                 data: ['expression': 'I@openssh:server', 'type': 'compound'])
+ * @param function Function to execute (eg. "state.sls")
+ * @param batch    Batch param to salt (integer or string with percents)
+ * @param args     Additional arguments to function
+ * @param kwargs   Additional key-value arguments to function
+ * @param timeout  Additional argument salt api timeout
+ * @param read_timeout http session read timeout
+ */
+@NonCPS
+def runSaltCommandPepper(pepperVenv, client, target, function, batch = null, args = null, kwargs = null, timeout = -1, read_timeout = -1) {
+    def http = new com.mirantis.mk.Http()
+
+    data = [
+        'tgt': target.expression,
+        'fun': function,
+        'client': client,
+        'expr_form': target.type,
+    ]
+    if(batch != null && ( (batch instanceof Integer && batch > 0) || (batch instanceof String && batch.contains("%")))){
+        data['client']= "local_batch"
+        data['batch'] = batch
+    }
+
+    if (args) {
+        data['arg'] = args
+    }
+
+    if (kwargs) {
+        data['kwarg'] = kwargs
+    }
+
+    if (timeout != -1) {
+        data['timeout'] = timeout
+    }
+
+    headers = [
+      'X-Auth-Token': "${master.authToken}"
+    ]
+
+    return runPepperCommand(data, pepperVenv)
+}
+
+/**
  * Return pillar for given master and target
  * @param master Salt connection object
  * @param target Get pillar target
@@ -637,3 +683,52 @@
     }
     runSaltProcessStep(master, 'I@salt:master', 'cmd.run', ["git -C ${reclass_dir} update-index --skip-worktree classes/cluster/overrides.yml"])
 }
+
+/**
+* Execute salt commands via salt-api with
+* CLI client salt-pepper
+*
+* @param data   Salt command map
+* @param venv   Path to virtualenv with
+*/
+
+def runPepperCommand(data, venv)   {
+    def python = new com.mirantis.mk.Python()
+    def dataStr = new groovy.json.JsonBuilder(data).toString()
+
+    def pepperCmd = "pepper -c ${venv}/pepperrc --make-token --json \'${dataStr}\'"
+
+    if (venv) {
+        output = python.runVirtualenvCommand(venv, pepperCmd)
+    } else {
+        echo("[Command]: ${pepperCmd}")
+        output = sh (
+            script: pepperCmd,
+            returnStdout: true
+        ).trim()
+    }
+
+    return new groovy.json.JsonSlurperClassic().parseText(output)
+}
+
+/**
+* Create config file for pepper
+*
+* @param url            SALT_MASTER URL
+* @param credentialsId  credentials to SALT_API
+* @param path path to pepper env
+*/
+def createPepperEnv(url, credentialsId, path) {
+    def common = new com.mirantis.mk.Common()
+    rcFile = "${path}/pepperrc"
+    creds = common.getPasswordCredentials(credentialsId)
+    rc = """\
+[main]
+SALTAPI_EAUTH=pam
+SALTAPI_URL=${url}
+SALTAPI_USER=${creds.username}
+SALTAPI_PASS=${creds.password.toString()}
+"""
+    writeFile file: rcFile, text: rc
+    return rcFile
+}