/**
 * Helper pipeline for AWS deployments from kqueen
 *
 * Expected parameters:
 *   STACK_NAME                 Infrastructure stack name
 *   STACK_TEMPLATE             File with stack template
 *
 *   STACK_TEMPLATE_URL         URL to git repo with stack templates
 *   STACK_TEMPLATE_CREDENTIALS Credentials to the templates repo
 *   STACK_TEMPLATE_BRANCH      Stack templates repo branch
 *   STACK_COMPUTE_COUNT        Number of compute nodes to launch
 *
 *   HEAT_STACK_ENVIRONMENT     Heat stack environmental parameters
 *   HEAT_STACK_ZONE            Heat stack availability zone
 *   HEAT_STACK_PUBLIC_NET      Heat stack floating IP pool
 *   OPENSTACK_API_URL          OpenStack API address
 *   OPENSTACK_API_CREDENTIALS  Credentials to the OpenStack API
 *   OPENSTACK_API_PROJECT      OpenStack project to connect to
 *   OPENSTACK_API_VERSION      Version of the OpenStack API (2/3)
 *
 *   SALT_MASTER_CREDENTIALS    Credentials to the Salt API
 *   SALT_MASTER_URL            URL of Salt master
 */

common = new com.mirantis.mk.Common()
git = new com.mirantis.mk.Git()
openstack = new com.mirantis.mk.Openstack()
orchestrate = new com.mirantis.mk.Orchestrate()
python = new com.mirantis.mk.Python()
salt = new com.mirantis.mk.Salt()

// Define global variables
def venv
def venvPepper
def outputs = [:]

def ipRegex = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
def envParams
timeout(time: 12, unit: 'HOURS') {
    node("python") {
         try {
            // Set build-specific variables
            venv = "${env.WORKSPACE}/venv"
            venvPepper = "${env.WORKSPACE}/venvPepper"

            //
            // Prepare machines
            //
            stage ('Create infrastructure') {
                // value defaults
                envParams = [
                    'cluster_zone': HEAT_STACK_ZONE,
                    'cluster_public_net': HEAT_STACK_PUBLIC_NET
                ]

                // no underscore in STACK_NAME
                STACK_NAME = STACK_NAME.replaceAll('_', '-')
                outputs.put('stack_name', STACK_NAME)

                // set description
                currentBuild.description = STACK_NAME

                // get templates
                git.checkoutGitRepository('template', STACK_TEMPLATE_URL, STACK_TEMPLATE_BRANCH, STACK_TEMPLATE_CREDENTIALS)

                // create openstack env
                openstack.setupOpenstackVirtualenv(venv)
                openstackCloud = openstack.createOpenstackEnv(venv,
                    OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS,
                    OPENSTACK_API_PROJECT, "default", "", "default", "3")
                openstack.getKeystoneToken(openstackCloud, venv)

                // set reclass repo in heat env
                try {
                    envParams.put('cfg_reclass_branch', STACK_RECLASS_BRANCH)
                    envParams.put('cfg_reclass_address', STACK_RECLASS_ADDRESS)
                } catch (MissingPropertyException e) {
                    common.infoMsg("Property STACK_RECLASS_BRANCH or STACK_RECLASS_ADDRESS not found! Using default values from template.")
                }

                // launch stack
                openstack.createHeatStack(openstackCloud, STACK_NAME, STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, venv)

                // get SALT_MASTER_URL
                saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, STACK_NAME, 'salt_master_ip', venv)
                // check that saltMasterHost is valid
                if (!saltMasterHost || !saltMasterHost.matches(ipRegex)) {
                    common.errorMsg("saltMasterHost is not a valid ip, value is: ${saltMasterHost}")
                    throw new Exception("saltMasterHost is not a valid ip")
                }

                currentBuild.description = "${STACK_NAME} ${saltMasterHost}"

                SALT_MASTER_URL = "http://${saltMasterHost}:6969"

                // Setup virtualenv for pepper
                python.setupPepperVirtualenv(venvPepper, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
            }

            stage('Install core infrastructure') {
                def staticMgmtNetwork = false
                if (common.validInputParam('STATIC_MGMT_NETWORK')) {
                    staticMgmtNetwork = STATIC_MGMT_NETWORK.toBoolean()
                }
                orchestrate.installFoundationInfra(venvPepper, staticMgmtNetwork)

                if (common.checkContains('STACK_INSTALL', 'kvm')) {
                    orchestrate.installInfraKvm(venvPepper)
                    orchestrate.installFoundationInfra(venvPepper, staticMgmtNetwork)
                }

                orchestrate.validateFoundationInfra(venvPepper)
            }

            stage('Install Kubernetes infra') {
                // configure kubernetes_control_address - save loadbalancer
                def awsOutputs = aws.getOutputs(venv, aws_env_vars, STACK_NAME)
                common.prettyPrint(awsOutputs)
                if (awsOutputs.containsKey('ControlLoadBalancer')) {
                    salt.runSaltProcessStep(venvPepper, 'I@salt:master', 'reclass.cluster_meta_set', ['kubernetes_control_address', awsOutputs['ControlLoadBalancer']], null, true)
                    outputs.put('kubernetes_apiserver', 'https://' + awsOutputs['ControlLoadBalancer'])
                }

                // ensure certificates are generated properly
                salt.runSaltProcessStep(venvPepper, 'I@kubernetes:*', 'saltutil.refresh_pillar', [], null, true)
                salt.enforceState(venvPepper, 'I@kubernetes:*', ['salt.minion.cert'], true)

                orchestrate.installKubernetesInfra(venvPepper)
            }

            stage('Install Kubernetes control') {
                orchestrate.installKubernetesControl(venvPepper)

                // collect artifacts (kubeconfig)
                writeFile(file: 'kubeconfig', text: salt.getFileContent(venvPepper, 'I@kubernetes:master and *01*', '/etc/kubernetes/admin-kube-config'))
                archiveArtifacts(artifacts: 'kubeconfig')
            }

            stage('Install Kubernetes computes') {
                if (common.validInputParam('STACK_COMPUTE_COUNT')) {
                    if (STACK_COMPUTE_COUNT > 0) {
                        // get stack info
                        def scaling_group = aws.getOutputs(venv, aws_env_vars, STACK_NAME, 'ComputesScalingGroup')

                        //update autoscaling group
                        aws.updateAutoscalingGroup(venv, aws_env_vars, scaling_group, ["--desired-capacity " + STACK_COMPUTE_COUNT])

                        // wait for computes to boot up
                        aws.waitForAutoscalingInstances(venv, aws_env_vars, scaling_group)
                        sleep(60)
                    }
                }

                orchestrate.installKubernetesCompute(venvPepper)
            }

            stage('Finalize') {
                outputsPretty = common.prettify(outputs)
                print(outputsPretty)
                writeFile(file: 'outputs.json', text: outputsPretty)
                archiveArtifacts(artifacts: 'outputs.json')
            }

        } catch (Throwable e) {
            currentBuild.result = 'FAILURE'
            throw e
        } finally {
            if (currentBuild.result == 'FAILURE') {
                common.errorMsg("Deploy job FAILED and was not deleted. Please fix the problem and delete stack on you own.")

                if (common.validInputParam('SALT_MASTER_URL')) {
                    common.errorMsg("Salt master URL: ${SALT_MASTER_URL}")
                }
            }
        }
    }
}


