Refactor structure of Pipeline library

* Moved everything under com.mirantis.mcp package
* Functions are grouped under different files, each with specific
  functionality (e.g Artifactory, Docker, Git, etc)
* Tools.groovy remains as is to avoid breaking existing jobs that
  use it

Next steps:
* Modify existing pipelines to use functions from new package
* Gradually get rid of Tools.groovy

Change-Id: I56386f994666baa1c6db51b27beef4de4284ecb8
diff --git a/src/ci/mcp/Tools.groovy b/src/ci/mcp/Tools.groovy
index fb552ea..565232f 100644
--- a/src/ci/mcp/Tools.groovy
+++ b/src/ci/mcp/Tools.groovy
@@ -1,6 +1,11 @@
 package ci.mcp
 
 /**
+* Please do not add new functions to this file. Add them to corresponding
+* files in com/mirantis/mcp instead.
+**/
+
+/**
  * https://issues.jenkins-ci.org/browse/JENKINS-26481
  * fix groovy List.collect()
 **/
diff --git a/src/com/mirantis/mcp/Common.groovy b/src/com/mirantis/mcp/Common.groovy
new file mode 100644
index 0000000..847c851
--- /dev/null
+++ b/src/com/mirantis/mcp/Common.groovy
@@ -0,0 +1,20 @@
+package com.mirantis.mcp
+
+/**
+ * https://issues.jenkins-ci.org/browse/JENKINS-26481
+ * fix groovy List.collect()
+ **/
+@NonCPS
+def constructString(ArrayList options, String keyOption, String separator = " ") {
+    return options.collect { keyOption + it }.join(separator).replaceAll("\n", "")
+}
+
+/**
+ * Generate current timestamp
+ *
+ * @param format Defaults to yyyyMMddHHmmss
+ */
+def getDatetime(format = "yyyyMMddHHmmss") {
+    def now = new Date();
+    return now.format(format, TimeZone.getTimeZone('UTC'));
+}
\ No newline at end of file
diff --git a/src/com/mirantis/mcp/Docker.groovy b/src/com/mirantis/mcp/Docker.groovy
new file mode 100644
index 0000000..230ea73
--- /dev/null
+++ b/src/com/mirantis/mcp/Docker.groovy
@@ -0,0 +1,38 @@
+package com.mirantis.mcp
+
+/**
+ * Add LABEL to the end of the Dockerfile
+ * User can also add some custom properties
+ *
+ * @param dockerfilePath is the path to Dockerfile, the default is ./Dockerfile
+ * @param customProperties a Array of Strings that should be added to mandatory props
+ *        in format ["prop1=value1", "prop2=value2"]
+ * */
+def setDockerfileLabels(String dockerfilePath = "./Dockerfile", ArrayList customProperties = null) {
+
+    if (!fileExists(dockerfilePath)) {
+        throw new RuntimeException("Unable to add LABEL to Dockerfile, ${dockerfilePath} doesn't exists")
+    }
+    echo "Updating ${dockerfilePath}"
+
+    def namespace = "com.mirantis.image-specs."
+    def properties = [
+            "gerritProject=${env.GERRIT_PROJECT}",
+            "gerritChangeNumber=${env.GERRIT_CHANGE_NUMBER}",
+            "gerritPatchsetNumber=${env.GERRIT_PATCHSET_NUMBER}",
+            "gerritChangeId=${env.GERRIT_CHANGE_ID}",
+            "gerritPatchsetRevision=${env.GERRIT_PATCHSET_REVISION}"
+    ]
+
+    if (customProperties != null) {
+        properties.addAll(customProperties)
+    }
+    def common = new com.mirantis.mcp.Common()
+    def metadata = common.constructString(properties, namespace, " ")
+    sh """
+      cat <<EOF>> ${dockerfilePath}
+      # Apply additional build metadata
+      LABEL ${metadata}
+    """
+    return metadata
+}
diff --git a/src/com/mirantis/mcp/Git.groovy b/src/com/mirantis/mcp/Git.groovy
new file mode 100644
index 0000000..4874084
--- /dev/null
+++ b/src/com/mirantis/mcp/Git.groovy
@@ -0,0 +1,23 @@
+package com.mirantis.mcp
+
+/**
+ * Parse HEAD of current directory and return commit hash
+ */
+def getGitCommit() {
+    git_commit = sh(
+            script: 'git rev-parse HEAD',
+            returnStdout: true
+    ).trim()
+    return git_commit
+}
+
+/**
+ * Describe a commit using the most recent tag reachable from it
+ */
+def getGitDescribe() {
+    git_commit = sh (
+            script: 'git describe --tags',
+            returnStdout: true
+    ).trim()
+    return git_commit
+}
diff --git a/src/com/mirantis/mcp/MCPArtifactory.groovy b/src/com/mirantis/mcp/MCPArtifactory.groovy
new file mode 100644
index 0000000..8d76d8a
--- /dev/null
+++ b/src/com/mirantis/mcp/MCPArtifactory.groovy
@@ -0,0 +1,228 @@
+package com.mirantis.mcp
+
+/**
+ * Return string of mandatory build properties for binaries
+ * User can also add some custom properties.
+ *
+ * @param customProperties a Array of Strings that should be added to mandatory props
+ *        in format ["prop1=value1", "prop2=value2"]
+ * */
+def getBinaryBuildProperties(ArrayList customProperties) {
+    def namespace = "com.mirantis."
+    def properties = [
+            "gerritProject=${env.GERRIT_PROJECT}",
+            "gerritChangeNumber=${env.GERRIT_CHANGE_NUMBER}",
+            "gerritPatchsetNumber=${env.GERRIT_PATCHSET_NUMBER}",
+            "gerritChangeId=${env.GERRIT_CHANGE_ID}",
+            "gerritPatchsetRevision=${env.GERRIT_PATCHSET_REVISION}"
+    ]
+
+    if (customProperties) {
+        properties.addAll(customProperties)
+    }
+
+    def common = new com.mirantis.mcp.Common()
+
+    return common.constructString(properties, namespace, ";")
+}
+
+/**
+ * Get URL to artifact by properties
+ * Returns String with URL to found artifact or null if nothing
+ *
+ * @param artifactoryURL String, an URL to Artifactory
+ * @param properties LinkedHashMap, a Hash of properties (key-value) which
+ *        which should determine artifact in Artifactory
+ */
+def uriByProperties(String artifactoryURL, LinkedHashMap properties) {
+    def key, value
+    def properties_str = ''
+    for (int i = 0; i < properties.size(); i++) {
+        // avoid serialization errors
+        key = properties.entrySet().toArray()[i].key
+        value = properties.entrySet().toArray()[i].value
+        properties_str += "${key}=${value}&"
+    }
+    def search_url = "${artifactoryURL}/api/search/prop?${properties_str}"
+
+    def result = sh(script: "bash -c \"curl -X GET \'${search_url}\'\"",
+            returnStdout: true).trim()
+    def content = new groovy.json.JsonSlurperClassic().parseText(result)
+    def uri = content.get("results")
+    if (uri) {
+        return uri.last().get("uri")
+    } else {
+        return null
+    }
+}
+
+/**
+* Get URL to artifact by properties
+* Returns String with URL to found artifact or null if nothing
+*
+* @param artifactoryURL String, an URL to Artifactory
+* @param properties String, URI in format prop1=val1&prop2=val2&prop3val3
+*        which should determine artifact in Artifactory
+*/
+def uriByProperties(String artifactoryURL, String properties) {
+  def search_url = "${artifactoryURL}/api/search/prop?${properties}"
+
+  def result = sh(script: "bash -c \"curl -X GET \'${search_url}\'\"",
+          returnStdout: true).trim()
+  def content = new groovy.json.JsonSlurperClassic().parseText(result)
+  def uri = content.get("results")
+  if ( uri ) {
+      return uri.last().get("uri")
+  } else {
+      return null
+  }
+}
+
+/**
+ * Set properties for artifact in Artifactory repo
+ *
+ * @param artifactUrl String, an URL to artifact in Artifactory repo
+ * @param properties LinkedHashMap, a Hash of properties (key-value) which
+ *        should be assigned for choosen artifact
+ * @param recursive Boolean, if artifact_url is a directory, whether to set
+ *        properties recursively or not
+ */
+def setProperties(String artifactUrl, LinkedHashMap properties, Boolean recursive = false) {
+    def properties_str = 'properties='
+    def key, value
+    if (recursive) {
+        recursive = 'recursive=1'
+    } else {
+        recursive = 'recursive=0'
+    }
+    for (int i = 0; i < properties.size(); i++) {
+        // avoid serialization errors
+        key = properties.entrySet().toArray()[i].key
+        value = properties.entrySet().toArray()[i].value
+        properties_str += "${key}=${value}|"
+    }
+    def url = "${artifactUrl}?${properties_str}&${recursive}"
+    withCredentials([
+            [$class          : 'UsernamePasswordMultiBinding',
+             credentialsId   : 'artifactory',
+             passwordVariable: 'ARTIFACTORY_PASSWORD',
+             usernameVariable: 'ARTIFACTORY_LOGIN']
+    ]) {
+        sh "bash -c \"curl -X PUT -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} \'${url}\'\""
+    }
+}
+
+/**
+ * Get properties for specified artifact in Artifactory
+ * Returns LinkedHashMap of properties
+ *
+ * @param artifactUrl String, an URL to artifact in Artifactory repo
+ */
+def getPropertiesForArtifact(String artifactUrl) {
+    def url = "${artifactUrl}?properties"
+    def result
+    withCredentials([
+            [$class          : 'UsernamePasswordMultiBinding',
+             credentialsId   : 'artifactory',
+             passwordVariable: 'ARTIFACTORY_PASSWORD',
+             usernameVariable: 'ARTIFACTORY_LOGIN']
+    ]) {
+        result = sh(script: "bash -c \"curl -X GET -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} \'${url}\'\"",
+                returnStdout: true).trim()
+    }
+    def properties = new groovy.json.JsonSlurperClassic().parseText(result)
+    return properties.get("properties")
+}
+
+/**
+ * Upload docker image to Artifactory
+ *
+ * @param artifactoryURL String, an URL to Artifactory
+ * @param registry String, the name of Docker registry
+ * @param image String, Docker image name
+ * @param version String, Docker image version
+ * @param repository String, The name of Artifactory Docker repository
+ */
+def uploadImageToArtifactory(registry, image, version, repository) {
+    def server = Artifactory.server('mcp-ci')
+    def artDocker
+    withCredentials([
+            [$class: 'UsernamePasswordMultiBinding',
+             credentialsId: 'artifactory',
+             passwordVariable: 'ARTIFACTORY_PASSWORD',
+             usernameVariable: 'ARTIFACTORY_LOGIN']
+    ]) {
+        sh ("docker login -u ${ARTIFACTORY_LOGIN} -p ${ARTIFACTORY_PASSWORD} ${registry}")
+        sh ("docker push ${registry}/${image}:${version}")
+        //artDocker = Artifactory.docker("${env.ARTIFACTORY_LOGIN}", "${env.ARTIFACTORY_PASSWORD}")
+    }
+
+    //artDocker.push("${registry}/${image}:${version}", "${repository}")
+    def image_url = "${env.ARTIFACTORY_URL}/api/storage/${repository}/${image}/${version}"
+    def properties = ['com.mirantis.build_name':"${env.JOB_NAME}",
+                      'com.mirantis.build_id': "${env.BUILD_NUMBER}",
+                      'com.mirantis.changeid': "${env.GERRIT_CHANGE_ID}",
+                      'com.mirantis.patchset_number': "${env.GERRIT_PATCHSET_NUMBER}",
+                      'com.mirantis.target_tag': "${version}"]
+
+    setProperties(image_url, properties)
+}
+
+/**
+ * Upload binaries to Artifactory
+ *
+ * @param server ArtifactoryServer, the instance of Artifactory server
+ * @param buildInfo BuildInfo, the instance of a build-info object which can be published
+ * @param uploadSpec String, a spec which is a JSON file that specifies which files should be
+ *        uploaded or downloaded and the target path
+ * @param publishInfo Boolean, whether publish a build-info object to Artifactory
+ */
+def uploadBinariesToArtifactory(uploadSpec, buildInfo, publishInfo=false) {
+    def server = Artifactory.server('mcp-ci')
+    buildInfo.append(server.upload(uploadSpec))
+
+    if ( publishInfo ) {
+        buildInfo.env.capture = true
+        buildInfo.env.filter.addInclude("*")
+        buildInfo.env.filter.addExclude("*PASSWORD*")
+        buildInfo.env.filter.addExclude("*password*")
+        buildInfo.env.collect()
+
+        server.publishBuildInfo(buildInfo)
+    }
+}
+
+/**
+ * Promote Docker image artifact to release repo
+ *
+ * @param artifactoryURL String, an URL to Artifactory
+ * @param artifactoryDevRepo String, the source dev repository name
+ * @param artifactoryProdRepo String, the target repository for the move or copy
+ * @param dockerRepo String, the docker repository name to promote
+ * @param artifactTag String, an image tag name to promote
+ * @param targetTag String, target tag to assign the image after promotion
+ * @param copy Boolean, an optional value to set whether to copy instead of move
+ *        Default: false
+ */
+def promoteDockerArtifact(String artifactoryURL, String artifactoryDevRepo,
+                          String artifactoryProdRepo, String dockerRepo,
+                          String artifactTag, String targetTag, Boolean copy = false) {
+    def url = "${artifactoryURL}/api/docker/${artifactoryDevRepo}/v2/promote"
+    writeFile file: "query.json",
+            text: """{
+                  \"targetRepo\": \"${artifactoryProdRepo}\",
+                  \"dockerRepository\": \"${dockerRepo}\",
+                  \"tag\": \"${artifactTag}\",
+                  \"targetTag\" : \"${targetTag}\",
+                  \"copy\": \"${copy}\"
+              }""".stripIndent()
+    sh "cat query.json"
+    withCredentials([
+            [$class          : 'UsernamePasswordMultiBinding',
+             credentialsId   : 'artifactory',
+             passwordVariable: 'ARTIFACTORY_PASSWORD',
+             usernameVariable: 'ARTIFACTORY_LOGIN']
+    ]) {
+        sh "bash -c \"curl  -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} -H \"Content-Type:application/json\" -X POST -d @query.json ${url}\""
+    }
+}