Added 12h timeout to all pipelines

Change-Id: I085fcbda322d0877d5ffebd002fc109577788c29
diff --git a/cicd-lab-pipeline.groovy b/cicd-lab-pipeline.groovy
index 9fc42d3..d985b3f 100644
--- a/cicd-lab-pipeline.groovy
+++ b/cicd-lab-pipeline.groovy
@@ -35,320 +35,321 @@
 
 def pepperEnv = "pepperEnv"
 _MAX_PERMITTED_STACKS = 2
-
-node {
-    try {
-        // connection objects
-        def openstackCloud
-
-        // value defaults
-        def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
-        def openstackEnv = "${env.WORKSPACE}/venv"
-
+timeout(time: 12, unit: 'HOURS') {
+    node {
         try {
-            sshPubKey = SSH_PUBLIC_KEY
-        } catch (MissingPropertyException e) {
-            sshPubKey = false
-        }
+            // connection objects
+            def openstackCloud
 
-        if (HEAT_STACK_REUSE.toBoolean() == true && HEAT_STACK_NAME == '') {
-            error("If you want to reuse existing stack you need to provide it's name")
-        }
+            // value defaults
+            def openstackVersion = OPENSTACK_API_CLIENT ? OPENSTACK_API_CLIENT : 'liberty'
+            def openstackEnv = "${env.WORKSPACE}/venv"
 
-        if (HEAT_STACK_REUSE.toBoolean() == false) {
-            // Don't allow to set custom heat stack name
-            wrap([$class: 'BuildUser']) {
-                if (env.BUILD_USER_ID) {
-                    HEAT_STACK_NAME = "${env.BUILD_USER_ID}-${JOB_NAME}-${BUILD_NUMBER}"
-                } else {
-                    HEAT_STACK_NAME = "jenkins-${JOB_NAME}-${BUILD_NUMBER}"
-                }
-                currentBuild.description = HEAT_STACK_NAME
+            try {
+                sshPubKey = SSH_PUBLIC_KEY
+            } catch (MissingPropertyException e) {
+                sshPubKey = false
             }
-        }
 
-        //
-        // Bootstrap
-        //
+            if (HEAT_STACK_REUSE.toBoolean() == true && HEAT_STACK_NAME == '') {
+                error("If you want to reuse existing stack you need to provide it's name")
+            }
 
-        stage ('Download Heat templates') {
-            git.checkoutGitRepository('template', HEAT_TEMPLATE_URL, HEAT_TEMPLATE_BRANCH, HEAT_TEMPLATE_CREDENTIALS)
-        }
+            if (HEAT_STACK_REUSE.toBoolean() == false) {
+                // Don't allow to set custom heat stack name
+                wrap([$class: 'BuildUser']) {
+                    if (env.BUILD_USER_ID) {
+                        HEAT_STACK_NAME = "${env.BUILD_USER_ID}-${JOB_NAME}-${BUILD_NUMBER}"
+                    } else {
+                        HEAT_STACK_NAME = "jenkins-${JOB_NAME}-${BUILD_NUMBER}"
+                    }
+                    currentBuild.description = HEAT_STACK_NAME
+                }
+            }
 
-        stage('Install OpenStack CLI') {
-            openstack.setupOpenstackVirtualenv(openstackEnv, openstackVersion)
-        }
+            //
+            // Bootstrap
+            //
 
-        stage('Connect to OpenStack cloud') {
-            openstackCloud = openstack.createOpenstackEnv(
-                OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS,
-                OPENSTACK_API_PROJECT, OPENSTACK_API_PROJECT_DOMAIN,
-                OPENSTACK_API_PROJECT_ID, OPENSTACK_API_USER_DOMAIN,
-                OPENSTACK_API_VERSION)
-            openstack.getKeystoneToken(openstackCloud, openstackEnv)
-            wrap([$class: 'BuildUser']) {
-                if (env.BUILD_USER_ID && !env.BUILD_USER_ID.equals("jenkins") && !HEAT_STACK_REUSE.toBoolean()) {
-                    def existingStacks = openstack.getStacksForNameContains(openstackCloud, "${env.BUILD_USER_ID}-${JOB_NAME}", openstackEnv)
-                    if(existingStacks.size() >= _MAX_PERMITTED_STACKS){
-                        HEAT_STACK_DELETE = "false"
-                        throw new Exception("You cannot create new stack, you already have ${_MAX_PERMITTED_STACKS} stacks of this type (${JOB_NAME}). \nStack names: ${existingStacks}")
+            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_API_PROJECT_DOMAIN,
+                    OPENSTACK_API_PROJECT_ID, OPENSTACK_API_USER_DOMAIN,
+                    OPENSTACK_API_VERSION)
+                openstack.getKeystoneToken(openstackCloud, openstackEnv)
+                wrap([$class: 'BuildUser']) {
+                    if (env.BUILD_USER_ID && !env.BUILD_USER_ID.equals("jenkins") && !HEAT_STACK_REUSE.toBoolean()) {
+                        def existingStacks = openstack.getStacksForNameContains(openstackCloud, "${env.BUILD_USER_ID}-${JOB_NAME}", openstackEnv)
+                        if(existingStacks.size() >= _MAX_PERMITTED_STACKS){
+                            HEAT_STACK_DELETE = "false"
+                            throw new Exception("You cannot create new stack, you already have ${_MAX_PERMITTED_STACKS} stacks of this type (${JOB_NAME}). \nStack names: ${existingStacks}")
+                        }
                     }
                 }
             }
-        }
 
-        if (HEAT_STACK_REUSE.toBoolean() == false) {
-            stage('Launch new Heat stack') {
-                envParams = [
-                    'cluster_zone': HEAT_STACK_ZONE,
-                    'cluster_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 = 6969
-            }
-            saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, HEAT_STACK_NAME, 'salt_master_ip', openstackEnv)
-            currentBuild.description = "${HEAT_STACK_NAME}: ${saltMasterHost}"
-            saltMasterUrl = "http://${saltMasterHost}:${saltMasterPort}"
-            python.setupPepperVirtualenv(pepperEnv, saltMasterUrl, SALT_MASTER_CREDENTIALS)
-        }
-
-        //
-        // Install
-        //
-
-        stage('Install core infra') {
-            // salt.master, reclass
-            // refresh_pillar
-            // sync_all
-            // linux,openssh,salt.minion.ntp
-
-            orchestrate.installFoundationInfra(pepperEnv)
-            orchestrate.validateFoundationInfra(pepperEnv)
-        }
-
-        stage("Deploy GlusterFS") {
-            salt.enforceState(pepperEnv, 'I@glusterfs:server', 'glusterfs.server.service', true)
-            retry(2) {
-                salt.enforceState(pepperEnv, 'ci01*', 'glusterfs.server.setup', true)
-            }
-            sleep(5)
-            salt.enforceState(pepperEnv, 'I@glusterfs:client', 'glusterfs.client', true)
-
-            timeout(5) {
-                println "Waiting for GlusterFS volumes to get mounted.."
-                salt.cmdRun(pepperEnv, 'I@glusterfs:client', 'while true; do systemctl -a|grep "GlusterFS File System"|grep -v mounted >/dev/null || break; done')
-            }
-            print common.prettyPrint(salt.cmdRun(pepperEnv, 'I@glusterfs:client', 'mount|grep fuse.glusterfs || echo "Command failed"'))
-        }
-
-        stage("Deploy GlusterFS") {
-            salt.enforceState(pepperEnv, 'I@haproxy:proxy', 'haproxy,keepalived')
-        }
-
-        stage("Setup Docker Swarm") {
-            salt.enforceState(pepperEnv, 'I@docker:host', 'docker.host', true)
-            salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'docker.swarm', true)
-            salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'salt', true)
-            salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm:role:master', 'mine.flush')
-            salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm:role:master', 'mine.update')
-            salt.enforceState(pepperEnv, 'I@docker:swarm', 'docker.swarm', true)
-            print common.prettyPrint(salt.cmdRun(pepperEnv, 'I@docker:swarm:role:master', 'docker node ls'))
-        }
-
-        stage("Configure OSS services") {
-            salt.enforceState(pepperEnv, 'I@devops_portal:config', 'devops_portal.config')
-            salt.enforceState(pepperEnv, 'I@rundeck:server', 'rundeck.server')
-        }
-
-        stage("Deploy Docker services") {
-            // We need /etc/aptly-publisher.yaml to be present before
-            // services are deployed
-            // XXX: for some weird unknown reason, refresh_pillar is
-            // required to execute here
-            salt.runSaltProcessStep(pepperEnv, 'I@aptly:publisher', 'saltutil.refresh_pillar', [], null, true)
-            salt.enforceState(pepperEnv, 'I@aptly:publisher', 'aptly.publisher', true)
-            retry(3) {
-                sleep(5)
-                salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'docker.client')
-            }
-            // XXX: Workaround to have `/var/lib/jenkins` on all
-            // nodes where are jenkins_slave services are created.
-            salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm', 'cmd.run', ['mkdir -p /var/lib/jenkins'])
-        }
-
-        stage("Configure CI/CD services") {
-            salt.syncAll(pepperEnv, '*')
-
-            // Aptly
-            timeout(10) {
-                println "Waiting for Aptly to come up.."
-                retry(2) {
-                    // XXX: retry to workaround magical VALUE_TRIMMED
-                    // response from salt master + to give slow cloud some
-                    // more time to settle down
-                    salt.cmdRun(pepperEnv, 'I@aptly:server', 'while true; do curl -sf http://172.16.10.254:8084/api/version >/dev/null && break; done')
+            if (HEAT_STACK_REUSE.toBoolean() == false) {
+                stage('Launch new Heat stack') {
+                    envParams = [
+                        'cluster_zone': HEAT_STACK_ZONE,
+                        'cluster_public_net': HEAT_STACK_PUBLIC_NET
+                    ]
+                    openstack.createHeatStack(openstackCloud, HEAT_STACK_NAME, HEAT_STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, openstackEnv)
                 }
             }
-            salt.enforceState(pepperEnv, 'I@aptly:server', 'aptly', true)
 
-            // OpenLDAP
-            timeout(10) {
-                println "Waiting for OpenLDAP to come up.."
-                salt.cmdRun(pepperEnv, 'I@openldap:client', 'while true; do curl -sf ldap://172.16.10.254 >/dev/null && break; done')
-            }
-            salt.enforceState(pepperEnv, 'I@openldap:client', 'openldap', true)
-
-            // Gerrit
-            timeout(10) {
-                println "Waiting for Gerrit to come up.."
-                salt.cmdRun(pepperEnv, 'I@gerrit:client', 'while true; do curl -sf 172.16.10.254:8080 >/dev/null && break; done')
-            }
-            salt.enforceState(pepperEnv, 'I@gerrit:client', 'gerrit', true)
-
-            // Jenkins
-            timeout(10) {
-                println "Waiting for Jenkins to come up.."
-                salt.cmdRun(pepperEnv, 'I@jenkins:client', 'while true; do curl -sf 172.16.10.254:8081 >/dev/null && break; done')
-            }
-            retry(2) {
-                // XXX: needs retry as first run installs python-jenkins
-                // thus make jenkins modules available for second run
-                salt.enforceState(pepperEnv, 'I@jenkins:client', 'jenkins', true)
-            }
-
-            // Postgres client - initialize OSS services databases
-            timeout(300){
-                println "Waiting for postgresql database to come up.."
-                salt.cmdRun(pepperEnv, 'I@postgresql:client', 'while true; do if docker service logs postgresql_postgresql-db | grep "ready to accept"; then break; else sleep 5; fi; done')
-            }
-            // XXX: first run usually fails on some inserts, but we need to create databases at first 
-            salt.enforceState(pepperEnv, 'I@postgresql:client', 'postgresql.client', true, false)
-
-            // Setup postgres database with integration between
-            // Pushkin notification service and Security Monkey security audit service
-            timeout(10) {
-                println "Waiting for Pushkin to come up.."
-                salt.cmdRun(pepperEnv, 'I@postgresql:client', 'while true; do curl -sf 172.16.10.254:8887/apps >/dev/null && break; done')
-            }
-            salt.enforceState(pepperEnv, 'I@postgresql:client', 'postgresql.client', true)
-
-            // Rundeck
-            timeout(10) {
-                println "Waiting for Rundeck to come up.."
-                salt.cmdRun(pepperEnv, 'I@rundeck:client', 'while true; do curl -sf 172.16.10.254:4440 >/dev/null && break; done')
-            }
-            salt.enforceState(pepperEnv, 'I@rundeck:client', 'rundeck.client', true)
-
-            // Elasticsearch
-            timeout(10) {
-                println 'Waiting for Elasticsearch to come up..'
-                salt.cmdRun(pepperEnv, 'I@elasticsearch:client', 'while true; do curl -sf 172.16.10.254:9200 >/dev/null && break; done')
-            }
-            retry(3){
-              sleep(10)
-              // XXX: first run sometimes fails on update indexes, so we need to wait
-              salt.enforceState(pepperEnv, 'I@elasticsearch:client', 'elasticsearch.client', true)
-            }
-        }
-
-        stage("Finalize") {
-            //
-            // Deploy user's ssh key
-            //
-            def adminUser
-            def authorizedKeysFile
-            def adminUserCmdOut = salt.cmdRun(pepperEnv, 'I@salt:master', "[ ! -d /home/ubuntu ] || echo 'ubuntu user exists'")
-            if (adminUserCmdOut =~ /ubuntu user exists/) {
-                adminUser = "ubuntu"
-                authorizedKeysFile = "/home/ubuntu/.ssh/authorized_keys"
-            } else {
-                adminUser = "root"
-                authorizedKeysFile = "/root/.ssh/authorized_keys"
-            }
-
-            if (sshPubKey) {
-                println "Deploying provided ssh key at ${authorizedKeysFile}"
-                salt.cmdRun(pepperEnv, '*', "echo '${sshPubKey}' | tee -a ${authorizedKeysFile}")
-            }
-
-            //
-            // Generate docs
-            //
-            try {
+            stage('Connect to Salt master') {
+                def saltMasterPort
                 try {
-                    // Run sphinx state to install sphinx-build needed in
-                    // upcomming orchestrate
-                    salt.enforceState(pepperEnv, 'I@sphinx:server', 'sphinx')
+                    saltMasterPort = SALT_MASTER_PORT
+                } catch (MissingPropertyException e) {
+                    saltMasterPort = 6969
+                }
+                saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, HEAT_STACK_NAME, 'salt_master_ip', openstackEnv)
+                currentBuild.description = "${HEAT_STACK_NAME}: ${saltMasterHost}"
+                saltMasterUrl = "http://${saltMasterHost}:${saltMasterPort}"
+                python.setupPepperVirtualenv(pepperEnv, saltMasterUrl, SALT_MASTER_CREDENTIALS)
+            }
+
+            //
+            // Install
+            //
+
+            stage('Install core infra') {
+                // salt.master, reclass
+                // refresh_pillar
+                // sync_all
+                // linux,openssh,salt.minion.ntp
+
+                orchestrate.installFoundationInfra(pepperEnv)
+                orchestrate.validateFoundationInfra(pepperEnv)
+            }
+
+            stage("Deploy GlusterFS") {
+                salt.enforceState(pepperEnv, 'I@glusterfs:server', 'glusterfs.server.service', true)
+                retry(2) {
+                    salt.enforceState(pepperEnv, 'ci01*', 'glusterfs.server.setup', true)
+                }
+                sleep(5)
+                salt.enforceState(pepperEnv, 'I@glusterfs:client', 'glusterfs.client', true)
+
+                timeout(5) {
+                    println "Waiting for GlusterFS volumes to get mounted.."
+                    salt.cmdRun(pepperEnv, 'I@glusterfs:client', 'while true; do systemctl -a|grep "GlusterFS File System"|grep -v mounted >/dev/null || break; done')
+                }
+                print common.prettyPrint(salt.cmdRun(pepperEnv, 'I@glusterfs:client', 'mount|grep fuse.glusterfs || echo "Command failed"'))
+            }
+
+            stage("Deploy GlusterFS") {
+                salt.enforceState(pepperEnv, 'I@haproxy:proxy', 'haproxy,keepalived')
+            }
+
+            stage("Setup Docker Swarm") {
+                salt.enforceState(pepperEnv, 'I@docker:host', 'docker.host', true)
+                salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'docker.swarm', true)
+                salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'salt', true)
+                salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm:role:master', 'mine.flush')
+                salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm:role:master', 'mine.update')
+                salt.enforceState(pepperEnv, 'I@docker:swarm', 'docker.swarm', true)
+                print common.prettyPrint(salt.cmdRun(pepperEnv, 'I@docker:swarm:role:master', 'docker node ls'))
+            }
+
+            stage("Configure OSS services") {
+                salt.enforceState(pepperEnv, 'I@devops_portal:config', 'devops_portal.config')
+                salt.enforceState(pepperEnv, 'I@rundeck:server', 'rundeck.server')
+            }
+
+            stage("Deploy Docker services") {
+                // We need /etc/aptly-publisher.yaml to be present before
+                // services are deployed
+                // XXX: for some weird unknown reason, refresh_pillar is
+                // required to execute here
+                salt.runSaltProcessStep(pepperEnv, 'I@aptly:publisher', 'saltutil.refresh_pillar', [], null, true)
+                salt.enforceState(pepperEnv, 'I@aptly:publisher', 'aptly.publisher', true)
+                retry(3) {
+                    sleep(5)
+                    salt.enforceState(pepperEnv, 'I@docker:swarm:role:master', 'docker.client')
+                }
+                // XXX: Workaround to have `/var/lib/jenkins` on all
+                // nodes where are jenkins_slave services are created.
+                salt.runSaltProcessStep(pepperEnv, 'I@docker:swarm', 'cmd.run', ['mkdir -p /var/lib/jenkins'])
+            }
+
+            stage("Configure CI/CD services") {
+                salt.syncAll(pepperEnv, '*')
+
+                // Aptly
+                timeout(10) {
+                    println "Waiting for Aptly to come up.."
+                    retry(2) {
+                        // XXX: retry to workaround magical VALUE_TRIMMED
+                        // response from salt master + to give slow cloud some
+                        // more time to settle down
+                        salt.cmdRun(pepperEnv, 'I@aptly:server', 'while true; do curl -sf http://172.16.10.254:8084/api/version >/dev/null && break; done')
+                    }
+                }
+                salt.enforceState(pepperEnv, 'I@aptly:server', 'aptly', true)
+
+                // OpenLDAP
+                timeout(10) {
+                    println "Waiting for OpenLDAP to come up.."
+                    salt.cmdRun(pepperEnv, 'I@openldap:client', 'while true; do curl -sf ldap://172.16.10.254 >/dev/null && break; done')
+                }
+                salt.enforceState(pepperEnv, 'I@openldap:client', 'openldap', true)
+
+                // Gerrit
+                timeout(10) {
+                    println "Waiting for Gerrit to come up.."
+                    salt.cmdRun(pepperEnv, 'I@gerrit:client', 'while true; do curl -sf 172.16.10.254:8080 >/dev/null && break; done')
+                }
+                salt.enforceState(pepperEnv, 'I@gerrit:client', 'gerrit', true)
+
+                // Jenkins
+                timeout(10) {
+                    println "Waiting for Jenkins to come up.."
+                    salt.cmdRun(pepperEnv, 'I@jenkins:client', 'while true; do curl -sf 172.16.10.254:8081 >/dev/null && break; done')
+                }
+                retry(2) {
+                    // XXX: needs retry as first run installs python-jenkins
+                    // thus make jenkins modules available for second run
+                    salt.enforceState(pepperEnv, 'I@jenkins:client', 'jenkins', true)
+                }
+
+                // Postgres client - initialize OSS services databases
+                timeout(300){
+                    println "Waiting for postgresql database to come up.."
+                    salt.cmdRun(pepperEnv, 'I@postgresql:client', 'while true; do if docker service logs postgresql_postgresql-db | grep "ready to accept"; then break; else sleep 5; fi; done')
+                }
+                // XXX: first run usually fails on some inserts, but we need to create databases at first 
+                salt.enforceState(pepperEnv, 'I@postgresql:client', 'postgresql.client', true, false)
+
+                // Setup postgres database with integration between
+                // Pushkin notification service and Security Monkey security audit service
+                timeout(10) {
+                    println "Waiting for Pushkin to come up.."
+                    salt.cmdRun(pepperEnv, 'I@postgresql:client', 'while true; do curl -sf 172.16.10.254:8887/apps >/dev/null && break; done')
+                }
+                salt.enforceState(pepperEnv, 'I@postgresql:client', 'postgresql.client', true)
+
+                // Rundeck
+                timeout(10) {
+                    println "Waiting for Rundeck to come up.."
+                    salt.cmdRun(pepperEnv, 'I@rundeck:client', 'while true; do curl -sf 172.16.10.254:4440 >/dev/null && break; done')
+                }
+                salt.enforceState(pepperEnv, 'I@rundeck:client', 'rundeck.client', true)
+
+                // Elasticsearch
+                timeout(10) {
+                    println 'Waiting for Elasticsearch to come up..'
+                    salt.cmdRun(pepperEnv, 'I@elasticsearch:client', 'while true; do curl -sf 172.16.10.254:9200 >/dev/null && break; done')
+                }
+                retry(3){
+                  sleep(10)
+                  // XXX: first run sometimes fails on update indexes, so we need to wait
+                  salt.enforceState(pepperEnv, 'I@elasticsearch:client', 'elasticsearch.client', true)
+                }
+            }
+
+            stage("Finalize") {
+                //
+                // Deploy user's ssh key
+                //
+                def adminUser
+                def authorizedKeysFile
+                def adminUserCmdOut = salt.cmdRun(pepperEnv, 'I@salt:master', "[ ! -d /home/ubuntu ] || echo 'ubuntu user exists'")
+                if (adminUserCmdOut =~ /ubuntu user exists/) {
+                    adminUser = "ubuntu"
+                    authorizedKeysFile = "/home/ubuntu/.ssh/authorized_keys"
+                } else {
+                    adminUser = "root"
+                    authorizedKeysFile = "/root/.ssh/authorized_keys"
+                }
+
+                if (sshPubKey) {
+                    println "Deploying provided ssh key at ${authorizedKeysFile}"
+                    salt.cmdRun(pepperEnv, '*', "echo '${sshPubKey}' | tee -a ${authorizedKeysFile}")
+                }
+
+                //
+                // Generate docs
+                //
+                try {
+                    try {
+                        // Run sphinx state to install sphinx-build needed in
+                        // upcomming orchestrate
+                        salt.enforceState(pepperEnv, 'I@sphinx:server', 'sphinx')
+                    } catch (Throwable e) {
+                        true
+                    }
+                    retry(3) {
+                        // TODO: fix salt.orchestrateSystem
+                        // print salt.orchestrateSystem(pepperEnv, ['expression': '*', 'type': 'compound'], 'sphinx.orch.generate_doc')
+                        def out = salt.cmdRun(pepperEnv, 'I@salt:master', 'salt-run state.orchestrate sphinx.orch.generate_doc || echo "Command execution failed"')
+                        print common.prettyPrint(out)
+                        if (out =~ /Command execution failed/) {
+                            throw new Exception("Command execution failed")
+                        }
+                    }
                 } catch (Throwable e) {
+                    // We don't want sphinx docs to ruin whole build, so possible
+                    // errors are just ignored here
                     true
                 }
-                retry(3) {
-                    // TODO: fix salt.orchestrateSystem
-                    // print salt.orchestrateSystem(pepperEnv, ['expression': '*', 'type': 'compound'], 'sphinx.orch.generate_doc')
-                    def out = salt.cmdRun(pepperEnv, 'I@salt:master', 'salt-run state.orchestrate sphinx.orch.generate_doc || echo "Command execution failed"')
-                    print common.prettyPrint(out)
-                    if (out =~ /Command execution failed/) {
-                        throw new Exception("Command execution failed")
-                    }
+                salt.enforceState(pepperEnv, 'I@nginx:server', 'nginx')
+
+                def failedSvc = salt.cmdRun(pepperEnv, '*', """systemctl --failed | grep -E 'loaded[ \t]+failed' && echo 'Command execution failed' || true""")
+                if (failedSvc =~ /Command execution failed/) {
+                    common.errorMsg("Some services are not running. Environment may not be fully functional!")
                 }
-            } catch (Throwable e) {
-                // We don't want sphinx docs to ruin whole build, so possible
-                // errors are just ignored here
-                true
+
+                common.successMsg("""
+    ============================================================
+    Your CI/CD lab has been deployed and you can enjoy it:
+    Use sshuttle to connect to your private subnet:
+
+        sshuttle -r ${adminUser}@${saltMasterHost} 172.16.10.0/24
+
+    And visit services running at 172.16.10.254 (vip address):
+
+        9600    HAProxy statistics
+        8080    Gerrit
+        8081    Jenkins
+        8089    LDAP administration
+        4440    Rundeck
+        8084    DevOps Portal
+        8091    Docker swarm visualizer
+        8090    Reclass-generated documentation
+
+    If you provided SSH_PUBLIC_KEY, you can use it to login,
+    otherwise you need to get private key connected to this
+    heat template.
+
+    DON'T FORGET TO TERMINATE YOUR STACK WHEN YOU DON'T NEED IT!
+    ============================================================""")
             }
-            salt.enforceState(pepperEnv, 'I@nginx:server', 'nginx')
-
-            def failedSvc = salt.cmdRun(pepperEnv, '*', """systemctl --failed | grep -E 'loaded[ \t]+failed' && echo 'Command execution failed' || true""")
-            if (failedSvc =~ /Command execution failed/) {
-                common.errorMsg("Some services are not running. Environment may not be fully functional!")
-            }
-
-            common.successMsg("""
-============================================================
-Your CI/CD lab has been deployed and you can enjoy it:
-Use sshuttle to connect to your private subnet:
-
-    sshuttle -r ${adminUser}@${saltMasterHost} 172.16.10.0/24
-
-And visit services running at 172.16.10.254 (vip address):
-
-    9600    HAProxy statistics
-    8080    Gerrit
-    8081    Jenkins
-    8089    LDAP administration
-    4440    Rundeck
-    8084    DevOps Portal
-    8091    Docker swarm visualizer
-    8090    Reclass-generated documentation
-
-If you provided SSH_PUBLIC_KEY, you can use it to login,
-otherwise you need to get private key connected to this
-heat template.
-
-DON'T FORGET TO TERMINATE YOUR STACK WHEN YOU DON'T NEED IT!
-============================================================""")
-        }
-    } 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
-    } finally {
-        // Cleanup
-        if (HEAT_STACK_DELETE.toBoolean() == true) {
-            stage('Trigger cleanup job') {
-                build(job: 'deploy-stack-cleanup', parameters: [
-                    [$class: 'StringParameterValue', name: 'STACK_NAME', value: HEAT_STACK_NAME],
-                    [$class: 'StringParameterValue', name: 'OPENSTACK_API_PROJECT', value: OPENSTACK_API_PROJECT],
-                ])
+        } 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
+        } finally {
+            // Cleanup
+            if (HEAT_STACK_DELETE.toBoolean() == true) {
+                stage('Trigger cleanup job') {
+                    build(job: 'deploy-stack-cleanup', parameters: [
+                        [$class: 'StringParameterValue', name: 'STACK_NAME', value: HEAT_STACK_NAME],
+                        [$class: 'StringParameterValue', name: 'OPENSTACK_API_PROJECT', value: OPENSTACK_API_PROJECT],
+                    ])
+                }
             }
         }
     }