Use timestamps plugin

Change-Id: I9ed9b3ec9b3c2d45dc40d7089bbcdba83d19cc19
diff --git a/cicd-lab-pipeline.groovy b/cicd-lab-pipeline.groovy
index 96b6529..74c882d 100644
--- a/cicd-lab-pipeline.groovy
+++ b/cicd-lab-pipeline.groovy
@@ -51,185 +51,193 @@
     }
 }
 
-node {
-    try {
-        // connection objects
-        def openstackCloud
-        def saltMaster
+timestamps {
+    node {
+        try {
+            // connection objects
+            def openstackCloud
+            def saltMaster
 
-        // value defaults
-        def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
-        def openstackEnv = "${env.WORKSPACE}/venv"
+            // value defaults
+            def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
+            def openstackEnv = "${env.WORKSPACE}/venv"
 
-        if (HEAT_STACK_NAME == '') {
-            HEAT_STACK_NAME = BUILD_TAG
-        }
-
-        //
-        // Bootstrap
-        //
-
-        stage ('Download Heat templates') {
-            git.checkoutGitRepository('template', HEAT_TEMPLATE_URL, HEAT_TEMPLATE_BRANCH, HEAT_TEMPLATE_CREDENTIALS)
-        }
-
-        stage('Install OpenStack CLI') {
-            openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion)
-        }
-
-        stage('Connect to OpenStack cloud') {
-            openstackCloud = openstack.createOpenstackEnv(OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS, OPENSTACK_API_PROJECT)
-            openstack.getKeystoneToken(openstackCloud, openstackEnv)
-        }
-
-        if (HEAT_STACK_REUSE == 'false') {
-            stage('Launch new Heat stack') {
-                envParams = [
-                    'instance_zone': HEAT_STACK_ZONE,
-                    'public_net': HEAT_STACK_PUBLIC_NET
-                ]
-                openstack.createHeatStack(openstackCloud, HEAT_STACK_NAME, HEAT_STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, openstackEnv)
-            }
-        }
-
-        stage('Connect to Salt master') {
-            def saltMasterPort
-            try {
-                saltMasterPort = SALT_MASTER_PORT
-            } catch (MissingPropertyException e) {
-                saltMasterPort = 8000
-            }
-            saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, HEAT_STACK_NAME, 'salt_master_ip', openstackEnv)
-            saltMasterUrl = "http://${saltMasterHost}:${saltMasterPort}"
-            saltMaster = salt.connection(saltMasterUrl, SALT_MASTER_CREDENTIALS)
-        }
-
-        //
-        // Install
-        //
-
-        stage('Install core infra') {
-            // salt.master, reclass
-            // refresh_pillar
-            // sync_all
-            // linux,openssh,salt.minion.ntp
-
-            orchestrate.installFoundationInfra(saltMaster)
-            orchestrate.validateFoundationInfra(saltMaster)
-        }
-
-        stage("Deploy GlusterFS") {
-            salt.enforceState(saltMaster, 'I@glusterfs:server', 'glusterfs.server.service', true)
-            salt.enforceState(saltMaster, 'ci01*', 'glusterfs.server.setup', true)
-            sleep(5)
-            salt.enforceState(saltMaster, 'I@glusterfs:client', 'glusterfs.client', true)
-            print salt.cmdRun(saltMaster, 'I@glusterfs:client', 'mount|grep fuse.glusterfs || echo "Command failed"')
-        }
-
-        stage("Deploy GlusterFS") {
-            salt.enforceState(saltMaster, 'I@haproxy:proxy', 'haproxy,keepalived')
-        }
-
-        stage("Setup Docker Swarm") {
-            salt.enforceState(saltMaster, 'I@docker:host', 'docker.host', true)
-            salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.swarm', true)
-            salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'salt', true)
-            salt.runSaltProcessStep(saltMaster, 'I@docker:swarm:role:master', 'mine.flush')
-            salt.runSaltProcessStep(saltMaster, 'I@docker:swarm:role:master', 'mine.update')
-            salt.enforceState(saltMaster, 'I@docker:swarm', 'docker.swarm', true)
-            print salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', 'docker node ls')
-        }
-
-        stage("Deploy Docker services") {
-            salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.client')
-
-            // XXX: Hack to fix dependency of gerrit on mysql
-            print salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', "docker service rm gerrit; sleep 5; rm -rf /srv/volumes/gerrit/*")
-            waitForServices(saltMaster)
-            salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.client')
-            // ---- cut here (end of hack) ----
-
-            waitForServices(saltMaster)
-        }
-
-        stage("Configure CI/CD services") {
-            // Aptly
-            salt.enforceState(saltMaster, 'I@aptly:server', 'aptly', true)
-
-            // Gerrit
-            timeout(10) {
-                println "Waiting for Gerrit to come up.."
-                salt.cmdRun(saltMaster, 'I@gerrit:client', 'while true; do curl -svf 172.16.10.254:8080 >/dev/null && break; done')
-            }
-            retry(2) {
-                // Needs to run twice to pass __virtual__ method of gerrit module
-                // after installation of dependencies
-                try {
-                    salt.enforceState(saltMaster, 'I@gerrit:client', 'gerrit', true)
-                } catch (Exception e) {
-                    print "Restarting Salt minion"
-                    salt.cmdRun(saltMaster, 'I@gerrit:client', "exec 0>&-; exec 1>&-; exec 2>&-; nohup /bin/sh -c 'salt-call --local service.restart salt-minion' &")
-                    sleep(5)
-                    throw e
-                }
+            if (HEAT_STACK_NAME == '') {
+                HEAT_STACK_NAME = BUILD_TAG
             }
 
-            // Jenkins
-            timeout(10) {
-                println "Waiting for Jenkins to come up.."
-                salt.cmdRun(saltMaster, 'I@jenkins:client', 'while true; do curl -svf 172.16.10.254:8081 >/dev/null && break; done')
-            }
-            retry(2) {
-                // Same for jenkins
-                try {
-                    salt.enforceState(saltMaster, 'I@jenkins:client', 'jenkins', true)
-                } catch (Exception e) {
-                    print "Restarting Salt minion"
-                    salt.cmdRun(saltMaster, 'I@jenkins:client', "exec 0>&-; exec 1>&-; exec 2>&-; nohup /bin/sh -c 'salt-call --local service.restart salt-minion' &")
-                    sleep(5)
-                    throw e
-                }
-            }
-        }
-
-        stage("Finalize") {
             //
-            // Generate docs
+            // Bootstrap
             //
-            try {
-                retry(3) {
-                    print salt.orchestrateSystem(saltMaster, ['expression': '*', 'type': 'compound'], 'sphinx.orch.generate_doc')
-                }
-            } catch (Throwable e) {
-                // We don't want sphinx docs to ruin whole build, so possible
-                // errors are just ignored here
-                true
+
+            stage ('Download Heat templates') {
+                git.checkoutGitRepository('template', HEAT_TEMPLATE_URL, HEAT_TEMPLATE_BRANCH, HEAT_TEMPLATE_CREDENTIALS)
             }
-            salt.enforceState(saltMaster, 'I@nginx:server', 'nginx')
 
-            print """============================================================
-Your CI/CD lab has been deployed and you can enjoy it:
-Use sshuttle -r ubuntu@${saltMasterHost} 172.16.10.0/24
-to connect to your private subnet and visit services
-running at 172.16.10.254 (vip address):
-    9600    haproxy stats
-    8080    gerrit
-    8081    jenkins
-    8091    Docker swarm visualizer
-    8090    Reclass-generated documentation
+            stage('Install OpenStack CLI') {
+                openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion)
+            }
 
-Don't forget to terminate your stack when you don't needed!
-============================================================"""
-        }
-    } catch (Throwable e) {
-        // If there was an error or exception thrown, the build failed
-        currentBuild.result = "FAILURE"
-        throw e
-    } finally {
-        // Cleanup
-        if (HEAT_STACK_DELETE == 'true') {
-            stage('Trigger cleanup job') {
-                build job: 'deploy_heat_cleanup', parameters: [[$class: 'StringParameterValue', name: 'HEAT_STACK_NAME', value: HEAT_STACK_NAME]]
+            stage('Connect to OpenStack cloud') {
+                openstackCloud = openstack.createOpenstackEnv(OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS, OPENSTACK_API_PROJECT)
+                openstack.getKeystoneToken(openstackCloud, openstackEnv)
+            }
+
+            if (HEAT_STACK_REUSE == 'false') {
+                stage('Launch new Heat stack') {
+                    envParams = [
+                        'instance_zone': HEAT_STACK_ZONE,
+                        'public_net': HEAT_STACK_PUBLIC_NET
+                    ]
+                    openstack.createHeatStack(openstackCloud, HEAT_STACK_NAME, HEAT_STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, openstackEnv)
+                }
+            }
+
+            stage('Connect to Salt master') {
+                def saltMasterPort
+                try {
+                    saltMasterPort = SALT_MASTER_PORT
+                } catch (MissingPropertyException e) {
+                    saltMasterPort = 8000
+                }
+                saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, HEAT_STACK_NAME, 'salt_master_ip', openstackEnv)
+                saltMasterUrl = "http://${saltMasterHost}:${saltMasterPort}"
+                saltMaster = salt.connection(saltMasterUrl, SALT_MASTER_CREDENTIALS)
+            }
+
+            //
+            // Install
+            //
+
+            stage('Install core infra') {
+                // salt.master, reclass
+                // refresh_pillar
+                // sync_all
+                // linux,openssh,salt.minion.ntp
+
+                orchestrate.installFoundationInfra(saltMaster)
+                orchestrate.validateFoundationInfra(saltMaster)
+            }
+
+            stage("Deploy GlusterFS") {
+                salt.enforceState(saltMaster, 'I@glusterfs:server', 'glusterfs.server.service', true)
+                salt.enforceState(saltMaster, 'ci01*', 'glusterfs.server.setup', true)
+                sleep(5)
+                salt.enforceState(saltMaster, 'I@glusterfs:client', 'glusterfs.client', true)
+                print salt.cmdRun(saltMaster, 'I@glusterfs:client', 'mount|grep fuse.glusterfs || echo "Command failed"')
+            }
+
+            stage("Deploy GlusterFS") {
+                salt.enforceState(saltMaster, 'I@haproxy:proxy', 'haproxy,keepalived')
+            }
+
+            stage("Setup Docker Swarm") {
+                salt.enforceState(saltMaster, 'I@docker:host', 'docker.host', true)
+                salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.swarm', true)
+                salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'salt', true)
+                salt.runSaltProcessStep(saltMaster, 'I@docker:swarm:role:master', 'mine.flush')
+                salt.runSaltProcessStep(saltMaster, 'I@docker:swarm:role:master', 'mine.update')
+                salt.enforceState(saltMaster, 'I@docker:swarm', 'docker.swarm', true)
+                print salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', 'docker node ls')
+            }
+
+            stage("Deploy Docker services") {
+                salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.client')
+
+                // XXX: Hack to fix dependency of gerrit on mysql
+                print salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', "docker service rm gerrit; sleep 5; rm -rf /srv/volumes/gerrit/*")
+                waitForServices(saltMaster)
+
+                timeout(10) {
+                    salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', 'apt-get install -y mysql-client')
+                    println "Waiting for MySQL to come up.."
+                    salt.cmdRun(saltMaster, 'I@docker:swarm:role:master', 'while true; do mysql -h172.16.10.254 -ppassword -e"show status;" >/dev/null && break; done')
+                }
+                salt.enforceState(saltMaster, 'I@docker:swarm:role:master', 'docker.client')
+                // ---- cut here (end of hack) ----
+
+                waitForServices(saltMaster)
+            }
+
+            stage("Configure CI/CD services") {
+                // Aptly
+                salt.enforceState(saltMaster, 'I@aptly:server', 'aptly', true)
+
+                // Gerrit
+                timeout(10) {
+                    println "Waiting for Gerrit to come up.."
+                    salt.cmdRun(saltMaster, 'I@gerrit:client', 'while true; do curl -svf 172.16.10.254:8080 >/dev/null && break; done')
+                }
+                retry(2) {
+                    // Needs to run twice to pass __virtual__ method of gerrit module
+                    // after installation of dependencies
+                    try {
+                        salt.enforceState(saltMaster, 'I@gerrit:client', 'gerrit', true)
+                    } catch (Exception e) {
+                        print "Restarting Salt minion"
+                        salt.cmdRun(saltMaster, 'I@gerrit:client', "exec 0>&-; exec 1>&-; exec 2>&-; nohup /bin/sh -c 'salt-call --local service.restart salt-minion' &")
+                        sleep(5)
+                        throw e
+                    }
+                }
+
+                // Jenkins
+                timeout(10) {
+                    println "Waiting for Jenkins to come up.."
+                    salt.cmdRun(saltMaster, 'I@jenkins:client', 'while true; do curl -svf 172.16.10.254:8081 >/dev/null && break; done')
+                }
+                retry(2) {
+                    // Same for jenkins
+                    try {
+                        salt.enforceState(saltMaster, 'I@jenkins:client', 'jenkins', true)
+                    } catch (Exception e) {
+                        print "Restarting Salt minion"
+                        salt.cmdRun(saltMaster, 'I@jenkins:client', "exec 0>&-; exec 1>&-; exec 2>&-; nohup /bin/sh -c 'salt-call --local service.restart salt-minion' &")
+                        sleep(5)
+                        throw e
+                    }
+                }
+            }
+
+            stage("Finalize") {
+                //
+                // Generate docs
+                //
+                try {
+                    retry(3) {
+                        print salt.orchestrateSystem(saltMaster, ['expression': '*', 'type': 'compound'], 'sphinx.orch.generate_doc')
+                    }
+                } catch (Throwable e) {
+                    // We don't want sphinx docs to ruin whole build, so possible
+                    // errors are just ignored here
+                    true
+                }
+                salt.enforceState(saltMaster, 'I@nginx:server', 'nginx')
+
+                print """============================================================
+    Your CI/CD lab has been deployed and you can enjoy it:
+    Use sshuttle -r ubuntu@${saltMasterHost} 172.16.10.0/24
+    to connect to your private subnet and visit services
+    running at 172.16.10.254 (vip address):
+        9600    haproxy stats
+        8080    gerrit
+        8081    jenkins
+        8091    Docker swarm visualizer
+        8090    Reclass-generated documentation
+
+    Don't forget to terminate your stack when you don't needed!
+    ============================================================"""
+            }
+        } catch (Throwable e) {
+            // If there was an error or exception thrown, the build failed
+            currentBuild.result = "FAILURE"
+            throw e
+        } finally {
+            // Cleanup
+            if (HEAT_STACK_DELETE == 'true') {
+                stage('Trigger cleanup job') {
+                    build job: 'deploy_heat_cleanup', parameters: [[$class: 'StringParameterValue', name: 'HEAT_STACK_NAME', value: HEAT_STACK_NAME]]
+                }
             }
         }
     }