/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.elasticsearch.gradle.FileContentsTask
import org.elasticsearch.gradle.vagrant.BatsOverVagrantTask
import org.elasticsearch.gradle.vagrant.VagrantCommandTask

String testScripts = '*.bats'
String testCommand = "cd \$TESTROOT && sudo bats --tap \$BATS/$testScripts"
String smokeTestCommand = 'echo I work'

// the images we allow testing with
List<String> availableBoxes = [
    'centos-6',
    'centos-7',
    'debian-8',
    'fedora-22',
    'oel-6',
    'oel-7',
    'opensuse-13',
    'sles-12',
    'ubuntu-1204',
    'ubuntu-1404',
    'ubuntu-1504'
]

List<String> boxes = []
for (String box : getProperties().get('vagrant.boxes', 'sample').split(',')) {
  if (box == 'sample') {
    boxes.add('centos-7')
    boxes.add('ubuntu-1404')
  } else if (box == 'all') {
    boxes = availableBoxes
    break
  } else {
    if (availableBoxes.contains(box) == false) {
      throw new IllegalArgumentException("Unknown vagrant box '${box}'")
    }
    boxes.add(box)
  }
}

/* The version of elasticsearch that we upgrade *from* as part of testing
 * upgrades. */
String upgradeFromVersion = '2.0.0'

configurations {
  test
}

repositories {
  mavenCentral()
}

dependencies {
  test project(path: ':distribution:tar', configuration: 'archives')
  test project(path: ':distribution:rpm', configuration: 'archives')
  test project(path: ':distribution:deb', configuration: 'archives')

  // Collect all the plugins
  for (Project subproj : project.rootProject.subprojects) {
    if (subproj.path.startsWith(':plugins:')) {
      test project("${subproj.path}")
    }
  }

  // The version of elasticsearch that we upgrade *from*
  test "org.elasticsearch.distribution.deb:elasticsearch:$upgradeFromVersion@deb"
  test "org.elasticsearch.distribution.rpm:elasticsearch:$upgradeFromVersion@rpm"
}

task clean(type: Delete) {
  group 'Build'
  delete buildDir
}

task stop {
  group 'Verification'
  description 'Stop any tasks from tests that still may be running'
}

File testRoot = new File("$buildDir/testroot")
task createTestRoot {
  outputs.dir testRoot
  doLast {
    testRoot.mkdirs()
  }
}

task createVersionFile(type: FileContentsTask) {
  dependsOn createTestRoot
  file "${testRoot}/version"
  contents = version
}

task createUpgradeFromFile(type: FileContentsTask) {
  dependsOn createTestRoot
  file "${testRoot}/upgrade_from_version"
  contents = upgradeFromVersion
}

task prepareTestRoot(type: Copy) {
  description 'Dump bats test dependencies into the $TESTROOT'
  into testRoot
  from configurations.test

  dependsOn createVersionFile, createUpgradeFromFile
}

task checkVagrantVersion(type: Exec) {
  commandLine 'vagrant', '--version'
  standardOutput = new ByteArrayOutputStream()
  doLast {
    String version = standardOutput.toString().trim()
    if ((version ==~ /Vagrant 1\.[789]\..+/) == false) {
      throw new InvalidUserDataException(
        "Illegal version of vagrant [${version}]. Need [Vagrant 1.7+]")
    }
  }
}

task vagrantSmokeTest {
  group 'Verification'
  description 'Smoke test the specified vagrant boxes'
}

task packagingTest {
  group 'Verification'
  description "Tests yum/apt packages using vagrant and bats.\n" +
              "    Specify the vagrant boxes to test using the gradle property 'vagrant.boxes'.\n" +
              "    'sample' can be used to test a single yum and apt box. 'all' can be used to\n" +
              "    test all available boxes. The available boxes are: \n" +
              "    ${availableBoxes}"
}

// Each box gets it own set of tasks
for (String box : availableBoxes) {
  String boxTask = box.capitalize().replace('-', '')

  // always add a halt task for all boxes, so clean makes sure they are all shutdown
  Task halt = tasks.create("vagrant${boxTask}#halt", VagrantCommandTask) {
    boxName box
    args 'halt', box
  }
  stop.dependsOn(halt)
  if (boxes.contains(box) == false) {
    // we only need a halt task if this box was not specified
    continue;
  }

  Task up = tasks.create("vagrant${boxTask}#up", VagrantCommandTask) {
    boxName box
    /* Its important that we try to reprovision the box even if it already
      exists. That way updates to the vagrant configuration take automatically.
      That isn't to say that the updates will always be compatible. Its ok to
      just destroy the boxes if they get busted but that is a manual step
      because its slow-ish. */
    /* We lock the provider to virtualbox because the Vagrantfile specifies
      lots of boxes that only work properly in virtualbox. Virtualbox is
      vagrant's default but its possible to change that default and folks do.
      But the boxes that we use are unlikely to work properly with other
      virtualization providers. Thus the lock. */
    args 'up', box, '--provision', '--provider', 'virtualbox'
    /* It'd be possible to check if the box is already up here and output
      SKIPPED but that would require running vagrant status which is slow! */
    dependsOn checkVagrantVersion
  }

  Task smoke = tasks.create("vagrant${boxTask}#smoketest", Exec) {
    dependsOn up
    finalizedBy halt
    commandLine 'vagrant', 'ssh', box, '--command',
      "set -o pipefail && ${smokeTestCommand} | sed -ue 's/^/    ${box}: /'"
  }
  vagrantSmokeTest.dependsOn(smoke) 

  Task packaging = tasks.create("packagingTest${boxTask}", BatsOverVagrantTask) {
    dependsOn up
    finalizedBy halt
    boxName box
    command testCommand
    dependsOn prepareTestRoot
  }
  packagingTest.dependsOn(packaging)
}