#!/usr/bin/groovy

final String NODE_LABEL = "h2o-3"
final String DOCKER_STASH = 'h2o-3-hadoop-docker-stash'
final String REGISTRY_PREFIX = "${params.dockerRegistry}"
final String IMAGE_NAME_PREFIX = "opsh2oai/h2o-3-hadoop"
final List INTERNAL_CLUSTER_HADOOPS = [
        [ distribution: "hdp-2.2", krb: false ], // 0xd6 cluster
        [ distribution: "hdp-2.4", krb: true  ], // 0xg5 cluster
        [ distribution: "cdh-6.3", krb: true  ]  // 0xg9 cluster
]

def pipelineContext = null
def buildImageVersion = null
def version = null

properties(
    [
        parameters(
            [
                string(defaultValue: 'master', description: 'Branch to checkout', name: 'gitBranch'),
                string(name: 'dockerRegistry', defaultValue: 'harbor.h2o.ai'),
                booleanParam(name: 'force', defaultValue: false, description: 'If false and image with version specified by BuildConfig exists in repository, then the build fails.'),
                booleanParam(name: 'noCache', defaultValue: true, description: 'If set to true, the docker image is built from scratch, with no stages used from the cache.')
            ]
        )
    ]
)

node (NODE_LABEL) {
    final String stageName = 'Checkout and Prepare'
    stage (stageName) {
        def scmEnv = git credentialsId: 'c6bab81a-6bb5-4497-9ec9-285ef5db36ea',
                poll: false,
                url: 'https://github.com/h2oai/h2o-3',
                branch: params.gitBranch

        def pipelineContextFactory = load('scripts/jenkins/groovy/pipelineContext.groovy')
        pipelineContext = pipelineContextFactory('.', 'MODE_HADOOP', scmEnv, true)

        try {
            buildImageVersion = pipelineContext.getBuildConfig().getDefaultImageVersion()
            version = pipelineContext.getBuildConfig().getHadoopImageVersion()
            currentBuild.displayName += " v${version}"

            pipelineContext.getBuildSummary().addStageSummary(this, stageName, '')
            pipelineContext.getBuildSummary().setStageDetails(this, stageName, env.NODE_NAME, env.WORKSPACE)

            pipelineContext.getBuildSummary().addSection(this, 'docker-details', "<a href=\"${currentBuild.rawBuild.getAbsoluteUrl()}\" style=\"color: black;\">Details</a>", """
                <ul>
                <li><strong>Git Branch:</strong> ${env.BRANCH_NAME}</li>
                <li><strong>Version:</strong> ${version}</li>
                <li><strong>Force Overwrite:</strong> ${params.force}</li>
                <li><strong>No cache:</strong> ${params.noCache}</li>
                </ul>
            """)

            pipelineContext.getUtils().stashFiles(this, DOCKER_STASH, 'docker/hadoop/**,docker/scripts/*')

            pipelineContext.getBuildSummary().markStageSuccessful(this, stageName)
        } catch (Exception e) {
            pipelineContext.getBuildSummary().markStageFailed(this, stageName)
            throw e
        }
    }
}

final String noCache = params.noCache ? "--no-cache" : ""

parallel(pipelineContext.getBuildConfig().getSupportedHadoopDistributions().collectEntries { distribution ->
    [
        "Build images for ${distribution.name.toUpperCase()} ${distribution.version}", {
            node (pipelineContext.getBuildConfig().getDefaultNodeLabel()) {
                final String buildStageName = "Build ${distribution.name.toUpperCase()} ${distribution.version}"
                stage(buildStageName) {
                    try {
                        pipelineContext.getBuildSummary().addStageSummary(this, buildStageName, '')
                        pipelineContext.getBuildSummary().setStageDetails(this, buildStageName, env.NODE_NAME, env.WORKSPACE)
                        cleanWs()

                        final String imageName = "${IMAGE_NAME_PREFIX}-${distribution.name}-${distribution.version}"
                        final boolean conflict = pipelineContext.getUtils()
                                .dockerImageExistsInRegistry(this, params.dockerRegistry, imageName, version)
                        if (conflict && !params.force) {
                            error "Tag ${imageName}:${version} already exists in the repository"
                        }
                        def repoVersion = "${distribution.version}.0"
                        if (distribution.name == "cdh" && distribution.version == "6.3") {
                            repoVersion = "6.3.2" // 6.3.0 is not available in cloudera repos
                        }

                        withCredentials([
                                file(credentialsId: 'jenkins-ldif', variable: 'JENKINS_LDIF_PATH'),
                                usernamePassword(credentialsId: 'H2O_CDH_DEV_CREDS', 
                                        usernameVariable: 'cdh_APT_USERNAME', passwordVariable: 'cdh_APT_PASSWORD'),
                                usernamePassword(credentialsId: 'H2O_HDP_DEV_CREDS', 
                                        usernameVariable: 'hdp_APT_USERNAME', passwordVariable: 'hdp_APT_PASSWORD')
                        ]) {
                            dir("${imageName}.${version}") {
                                pipelineContext.getUtils().unstashFiles(this, DOCKER_STASH)
                                sh """
                                    cd docker
                                    rm -f hadoop/common/ldap/jenkins.ldif
                                    cp \${JENKINS_LDIF_PATH} hadoop/common/ldap/jenkins.ldif

                                    docker build \
                                        ${noCache} \
                                        -t ${REGISTRY_PREFIX}/${imageName}:${version} \
                                        -f hadoop/${distribution.name}/Dockerfile \
                                        --build-arg PATH_PREFIX=hadoop/${distribution.name} \
                                        --build-arg H2O_BRANCH=${params.gitBranch} \
                                        --build-arg PARENT_VERSION=${buildImageVersion} \
                                        --build-arg VERSION=${distribution.version} \
                                        --build-arg REPO_VERSION=${repoVersion} \\
                                        --build-arg APT_USERNAME=\${${distribution.name}_APT_USERNAME} \
                                        --build-arg APT_PASSWORD=\${${distribution.name}_APT_PASSWORD} \
                                        .

                                    docker build \
                                        ${noCache} \
                                        -t ${REGISTRY_PREFIX}/${imageName}-krb:${version} \
                                        -f hadoop/${distribution.name}/Dockerfile.kerberos \
                                        --build-arg PATH_PREFIX=hadoop/${distribution.name} \
                                        --build-arg FROM_IMAGE=${REGISTRY_PREFIX}/${imageName}:${version} \
                                        .
                                """
                            }
                        }
                        pipelineContext.getBuildSummary().markStageSuccessful(this, buildStageName)
                    } catch (Exception e) {
                        pipelineContext.getBuildSummary().markStageFailed(this, buildStageName)
                        throw e
                    }
                }

                final String publishStageName = "Publish images for ${distribution.name.toUpperCase()} ${distribution.version}"
                stage (publishStageName) {
                    try {
                        pipelineContext.getBuildSummary().addStageSummary(this, publishStageName, '')
                        pipelineContext.getBuildSummary().setStageDetails(this, publishStageName, env.NODE_NAME, env.WORKSPACE)

                        withCredentials([usernamePassword(credentialsId: "${params.dockerRegistry}", usernameVariable: 'REGISTRY_USERNAME', passwordVariable: 'REGISTRY_PASSWORD')]) {
                            sh """
                                docker login -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD ${params.dockerRegistry}
                                docker push ${REGISTRY_PREFIX}/${IMAGE_NAME_PREFIX}-${distribution.name}-${distribution.version}:${version}
                                docker push ${REGISTRY_PREFIX}/${IMAGE_NAME_PREFIX}-${distribution.name}-${distribution.version}-krb:${version}
                            """
                            echo "###### Docker image ${IMAGE_NAME_PREFIX}-${distribution.name}-${distribution.version}:${version} built and pushed. ######"
                        }
                        pipelineContext.getBuildSummary().markStageSuccessful(this, publishStageName)
                    } catch (Exception e) {
                        pipelineContext.getBuildSummary().markStageFailed(this, publishStageName)
                        throw e
                    }
                }
            }
        }
    ]
})

parallel(INTERNAL_CLUSTER_HADOOPS.collectEntries { hdp ->
    [
        "Build images for ${hdp.distribution} edge node", {
            node (pipelineContext.getBuildConfig().getDefaultNodeLabel()) {
                final String buildStageName = "Build images for Edge node ${hdp.distribution}"
                String baseImageName = "${IMAGE_NAME_PREFIX}-${hdp.distribution}"
                if (hdp.krb) {
                    baseImageName = baseImageName + "-krb"
                }
                final String imageName = "${baseImageName}-0xd-edge"
                stage(buildStageName) {
                    try {
                        pipelineContext.getBuildSummary().addStageSummary(this, buildStageName, '')
                        pipelineContext.getBuildSummary().setStageDetails(this, buildStageName, env.NODE_NAME, env.WORKSPACE)
                        cleanWs()
    
                        final boolean conflict = pipelineContext.getUtils()
                                .dockerImageExistsInRegistry(this, params.dockerRegistry, imageName, version)
                        if (conflict && !params.force) {
                            error "Tag ${imageName}:${version} already exists in the repository"
                        }
    
                        dir("${imageName}.${version}") {
                            pipelineContext.getUtils().unstashFiles(this, DOCKER_STASH)
                            sh """
                                    cd docker

                                    docker build \
                                        ${noCache} \
                                        -t ${REGISTRY_PREFIX}/${imageName}:${version} \
                                        -f hadoop/edge_node/Dockerfile \
                                        --build-arg PATH_PREFIX=hadoop/edge_node \
                                        --build-arg KRB=${hdp.krb} \
                                        --build-arg FROM_IMAGE=${REGISTRY_PREFIX}/${baseImageName}:${version} \
                                        .
                                """
                        }
                        pipelineContext.getBuildSummary().markStageSuccessful(this, buildStageName)
                    } catch (Exception e) {
                        pipelineContext.getBuildSummary().markStageFailed(this, buildStageName)
                        throw e
                    }
                }
    
                final String publishStageName = "Publish images for Edge node ${hdp.distribution}"
                stage (publishStageName) {
                    try {
                        pipelineContext.getBuildSummary().addStageSummary(this, publishStageName, '')
                        pipelineContext.getBuildSummary().setStageDetails(this, publishStageName, env.NODE_NAME, env.WORKSPACE)
    
                        withCredentials([usernamePassword(credentialsId: "${params.dockerRegistry}", usernameVariable: 'REGISTRY_USERNAME', passwordVariable: 'REGISTRY_PASSWORD')]) {
                            sh """
                                    docker login -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD ${params.dockerRegistry}
                                    docker push ${REGISTRY_PREFIX}/${imageName}:${version}
                                """
                            echo "###### Docker image ${imageName}:${version} built and pushed. ######"
                        }
                        pipelineContext.getBuildSummary().markStageSuccessful(this, publishStageName)
                    } catch (Exception e) {
                        pipelineContext.getBuildSummary().markStageFailed(this, publishStageName)
                        throw e
                    }
                }
            }        
        }
    ]
})
