import org.elasticsearch.gradle.Version
import org.elasticsearch.gradle.test.NodeInfo
import org.elasticsearch.gradle.test.RestIntegTestTask

import java.nio.charset.StandardCharsets

apply plugin: 'elasticsearch.standalone-test'

dependencies {
    testCompile project(path: xpackModule('core'), configuration: 'runtime')
    testCompile project(path: xpackModule('security'), configuration: 'runtime')
    testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') // to be moved in a later commit
}

Closure waitWithAuth = { NodeInfo node, AntBuilder ant ->
    File tmpFile = new File(node.cwd, 'wait.success')

    // wait up to two minutes
    final long stopTime = System.currentTimeMillis() + (2 * 60000L);
    Exception lastException = null;
    int lastResponseCode = 0

    while (System.currentTimeMillis() < stopTime) {

        lastException = null;
        // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned
        HttpURLConnection httpURLConnection = null;
        try {
            // TODO this sucks having to hardcode number of nodes, but node.config.numNodes isn't necessarily accurate for rolling
            httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=2&wait_for_status=yellow").openConnection();
            httpURLConnection.setRequestProperty("Authorization", "Basic " +
                    Base64.getEncoder().encodeToString("test_user:x-pack-test-password".getBytes(StandardCharsets.UTF_8)));
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setConnectTimeout(1000);
            httpURLConnection.setReadTimeout(30000); // read needs to wait for nodes!
            httpURLConnection.connect();
            lastResponseCode = httpURLConnection.getResponseCode()
            if (lastResponseCode == 200) {
                tmpFile.withWriter StandardCharsets.UTF_8.name(), {
                    it.write(httpURLConnection.getInputStream().getText(StandardCharsets.UTF_8.name()))
                }
                break;
            }
        } catch (Exception e) {
            logger.debug("failed to call cluster health", e)
            lastException = e
        } finally {
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }

        // did not start, so wait a bit before trying again
        Thread.sleep(500L);
    }
    if (tmpFile.exists() == false) {
        final String message = "final attempt of calling cluster health failed [lastResponseCode=${lastResponseCode}]"
        if (lastException != null) {
            logger.error(message, lastException)
        } else {
            logger.error(message + " [no exception]")
        }
    }
    return tmpFile.exists()
}

// This is a top level task which we will add dependencies to below.
// It is a single task that can be used to backcompat tests against all versions.
task bwcTest {
    description = 'Runs backwards compatibility tests.'
    group = 'verification'
}

String outputDir = "generated-resources/${project.name}"

for (Version version : bwcVersions.wireCompatible) {
    String baseName = "v${version}"

    Task oldClusterTest = tasks.create(name: "${baseName}#oldClusterTest", type: RestIntegTestTask) {
        mustRunAfter(precommit)
    }

    configure(extensions.findByName("${baseName}#oldClusterTestCluster")) {
        plugin xpackProject('plugin').path
        bwcVersion = version
        numBwcNodes = 2
        numNodes = 2
        minimumMasterNodes = { 2 }
        clusterName = 'rolling-upgrade-basic'
        waitCondition = waitWithAuth
        setting 'xpack.security.enabled', 'false'
        setting 'xpack.monitoring.enabled', 'false'
        setting 'xpack.ml.enabled', 'false'
        setting 'xpack.watcher.enabled', 'false'
        setting 'xpack.license.self_generated.type', 'basic'
    }

    Task oldClusterTestRunner = tasks.getByName("${baseName}#oldClusterTestRunner")
    oldClusterTestRunner.configure {
        systemProperty 'tests.rest.suite', 'old_cluster'
    }

    Task mixedClusterTest = tasks.create(name: "${baseName}#mixedClusterTest", type: RestIntegTestTask)

    configure(extensions.findByName("${baseName}#mixedClusterTestCluster")) {
        dependsOn oldClusterTestRunner, "${baseName}#oldClusterTestCluster#node1.stop"
        plugin xpackProject('plugin').path
        clusterName = 'rolling-upgrade-basic'
        unicastTransportUri = { seedNode, node, ant -> oldClusterTest.nodes.get(0).transportUri() }
        minimumMasterNodes = { 2 }
        dataDir = { nodeNumber -> oldClusterTest.nodes[1].dataDir }
        waitCondition = waitWithAuth
        setting 'xpack.security.enabled', 'false'
        setting 'xpack.monitoring.enabled', 'false'
        setting 'xpack.ml.enabled', 'false'
        setting 'xpack.watcher.enabled', 'false'
        setting 'xpack.license.self_generated.type', 'basic'
        setting 'node.name', 'mixed-node-0'
    }

    Task mixedClusterTestRunner = tasks.getByName("${baseName}#mixedClusterTestRunner")
    mixedClusterTestRunner.configure {
        systemProperty 'tests.rest.suite', 'mixed_cluster'
        finalizedBy "${baseName}#oldClusterTestCluster#node0.stop"
    }

    Task upgradedClusterTest = tasks.create(name: "${baseName}#upgradedClusterTest", type: RestIntegTestTask)

    configure(extensions.findByName("${baseName}#upgradedClusterTestCluster")) {
        dependsOn(mixedClusterTestRunner, "${baseName}#oldClusterTestCluster#node0.stop")
        plugin xpackProject('plugin').path
        clusterName = 'rolling-upgrade-basic'
        unicastTransportUri = { seedNode, node, ant -> mixedClusterTest.nodes.get(0).transportUri() }
        minimumMasterNodes = { 2 }
        dataDir = { nodeNumber -> oldClusterTest.nodes[0].dataDir }
        waitCondition = waitWithAuth
        setting 'xpack.security.enabled', 'false'
        setting 'xpack.monitoring.enabled', 'false'
        setting 'xpack.ml.enabled', 'false'
        setting 'xpack.watcher.enabled', 'false'
        setting 'xpack.license.self_generated.type', 'basic'
        setting 'node.name', 'upgraded-node-0'
    }

    Task upgradedClusterTestRunner = tasks.getByName("${baseName}#upgradedClusterTestRunner")
    upgradedClusterTestRunner.configure {
        systemProperty 'tests.rest.suite', 'upgraded_cluster'
        // only need to kill the mixed cluster tests node here because we explicitly told it to not stop nodes upon completion
        finalizedBy "${baseName}#mixedClusterTestCluster#stop"
    }

    Task versionBwcTest = tasks.create(name: "${baseName}#bwcTest") {
        dependsOn = [upgradedClusterTest]
    }

    if (project.bwc_tests_enabled) {
        bwcTest.dependsOn(versionBwcTest)
    }
}

test.enabled = false // no unit tests for rolling upgrades, only the rest integration test

// basic integ tests includes testing bwc against the most recent version
task integTest {
    if (project.bwc_tests_enabled) {
        for (final def version : bwcVersions.snapshotsWireCompatible) {
            dependsOn "v${version}#bwcTest"
        }
    }
}
check.dependsOn(integTest)

dependencies {
    testCompile project(path: xpackModule('core'), configuration: 'runtime')
    testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
}

compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked"

// copy x-pack plugin info so it is on the classpath and security manager has the right permissions
task copyXPackRestSpec(type: Copy) {
    dependsOn(project.configurations.restSpec, 'processTestResources')
    from project(xpackProject('plugin').path).sourceSets.test.resources
    include 'rest-api-spec/api/**'
    into project.sourceSets.test.output.resourcesDir
}

task copyXPackPluginProps(type: Copy) {
    dependsOn(copyXPackRestSpec)
    from project(xpackModule('core')).file('src/main/plugin-metadata')
    from project(xpackModule('core')).tasks.pluginProperties
    into outputDir
}
project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)

repositories {
    maven {
        url "https://artifacts.elastic.co/maven"
    }
    maven {
        url "https://snapshots.elastic.co/maven"
    }
}