From d32c16afe61d01141ae7d4cc130c446d92aa138a Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Wed, 4 Apr 2018 17:42:04 -0700 Subject: [PATCH] [test] packaging: add windows boxes Adds windows server 2012r2 and 2016 vagrant boxes to packaging tests. They can only be used if IDs for their images are specified, which are passed to gradle and then to vagrant via env variables. Adds options to the project property `vagrant.boxes` to choose between linux and windows boxes Bats tests are run only on linux boxes, and portable packaging tests run on all boxes. Platform tests are only run on linux boxes since they are not being maintained. For #26741 --- TESTING.asciidoc | 145 ++++++++----- Vagrantfile | 39 ++++ .../gradle/vagrant/VagrantTestPlugin.groovy | 200 ++++++++++++------ 3 files changed, 274 insertions(+), 110 deletions(-) diff --git a/TESTING.asciidoc b/TESTING.asciidoc index 2e4a6ede754ad..1df7520d666f9 100644 --- a/TESTING.asciidoc +++ b/TESTING.asciidoc @@ -303,15 +303,16 @@ comma separated list of nodes to connect to (e.g. localhost:9300). A transport c be created based on that and used for all the before|after test operations, and to extract the http addresses of the nodes so that REST requests can be sent to them. -== Testing scripts +== Testing packaging -The simplest way to test scripts and the packaged distributions is to use -Vagrant. You can get started by following there five easy steps: +The packaging tests use Vagrant virtual machines to verify that installing +and running elasticsearch distributions works correctly on supported operating systems. +These tests should really only be run in vagrant vms because they're destructive. . Install Virtual Box and Vagrant. -. (Optional) Install vagrant-cachier to squeeze a bit more performance out of -the process: +. (Optional) Install https://github.com/fgrehm/vagrant-cachier[vagrant-cachier] to squeeze +a bit more performance out of the process: -------------------------------------- vagrant plugin install vagrant-cachier @@ -325,26 +326,37 @@ vagrant plugin install vagrant-cachier . Download and smoke test the VMs with `./gradlew vagrantSmokeTest` or `./gradlew -Pvagrant.boxes=all vagrantSmokeTest`. The first time you run this it will -download the base images and provision the boxes and immediately quit. If you -you this again it'll skip the download step. +download the base images and provision the boxes and immediately quit. Downloading all +the images may take a long time. After the images are already on your machine, they won't +be downloaded again unless they have been updated to a new version. . Run the tests with `./gradlew packagingTest`. This will cause Gradle to build the tar, zip, and deb packages and all the plugins. It will then run the tests on ubuntu-1404 and centos-7. We chose those two distributions as the default because they cover deb and rpm packaging and SyvVinit and systemd. -You can run on all the VMs by running `./gradlew -Pvagrant.boxes=all -packagingTest`. You can run a particular VM with a command like `./gradlew --Pvagrant.boxes=oel-7 packagingTest`. See `./gradlew tasks` for a complete list -of available vagrant boxes for testing. It's important to know that if you -interrupt any of these Gradle commands then the boxes will remain running and -you'll have to terminate them with `./gradlew stop`. +You can choose which boxes to test by setting the `-Pvagrant.boxes` project property. For +example, to test all boxes, run `./gradlew -Pvagrant.boxes=all`. For a complete list of +available boxes, run `./gradlew :qa:vagrant:tasks --all`. All of the following values are +supported by the `vagrant.boxes` property + +* `sample` - The default, only chooses ubuntu-1404 and centos-7 +* `all` - All configured boxes, i.e. all linux boxes and any configured Windows +boxes. +* `linux-all` - All linux boxes. +* `windows-all` - All configured Windows boxes. If there are none configured when this +value is set, the build will fail. +* List of box names, comma separated (e.g. `oel-7,fedora-26`) - Chooses exactly the boxes listed. + +Note that if you interrupt gradle in the middle of running these tasks, any boxes started +will remain running and you'll have to stop them manually with `./gradlew stop` or +`vagrant halt`. All the regular vagrant commands should just work so you can get a shell in a VM running trusty by running `vagrant up ubuntu-1404 --provider virtualbox && vagrant ssh ubuntu-1404`. -These are the linux flavors the Vagrantfile currently supports: +These are the linux flavors supported, all of which we provide images for * ubuntu-1404 aka trusty * ubuntu-1604 aka xenial @@ -364,9 +376,34 @@ quality boxes available in vagrant atlas: * sles-11 -We're missing the following because our tests are very linux/bash centric: +=== Testing packaging on Windows + +The packaging tests also support Windows Server 2012R2 and Windows Server 2016. +Unfortunately we're not able to provide boxes for them in open source use +because of licensing issues. Any Virtualbox image that has WinRM and Powershell +enabled for remote users should work. + +Testing on Windows requires the https://github.com/criteo/vagrant-winrm[vagrant-winrm] plugin. + +------------------------------------ +vagrant plugin install vagrant-winrm +------------------------------------ + +Specify the image IDs of the Windows boxes to gradle with the following environment +variables. These are used by both the gradle build and Vagrant, so if they're exported +the Windows boxes will be recognized when you run gradle commands, or Vagrant commands +directly. -* Windows Server 2012 +* `VAGRANT_WINDOWS_2012R2_BOX` +* `VAGRANT_WINDOWS_2016_BOX` + +These variables are required for Windows support in all gradle tasks that +handle packaging tests. Either or both may be specified. Remember that to run tests +on these boxes, they still need to be included in the value of `-Pvagrant.boxes`. +When these properties are present, passing `-Pvagrant.boxes=all` will include the +Windows boxes. + +=== Testing VMs are disposable It's important to think of VMs like cattle. If they become lame you just shoot them and let vagrant reprovision them. Say you've hosed your precise VM: @@ -399,54 +436,62 @@ vagrant destroy -f `vagrant up` would normally start all the VMs but we've prevented that because that'd consume a ton of ram. -== Testing scripts more directly +=== Iterating on packaging tests -In general its best to stick to testing in vagrant because the bats scripts are -destructive. When working with a single package it's generally faster to run its -tests in a tighter loop than Gradle provides. In one window: +Running the packaging tests through gradle can take a while because it will start +and stop the VM each time. You can iterate faster by keeping the VM up and running +the tests directly. --------------------------------- -./gradlew :distribution:packages:rpm:assemble --------------------------------- +The packaging tests use a random seed to determine which past version to use for +testing upgrades. To use a single past version fix the test seed when running +the commands below (see <>) -and in another window: +First build the packaging tests and their dependencies ----------------------------------------------------- -vagrant up centos-7 --provider virtualbox && vagrant ssh centos-7 -cd $PACKAGING_ARCHIVES -sudo -E bats $BATS_TESTS/*rpm*.bats ----------------------------------------------------- +-------------------------------------------- +./gradlew :qa:vagrant:setupPackagingTest +-------------------------------------------- -If you wanted to retest all the release artifacts on a single VM you could: +Then choose the VM you want to test on and bring it up. For example, to bring +up Debian 9 use the gradle command below. Bringing the box up with vagrant directly +may not mount the packaging test project in the right place. Once the VM is up, ssh +into it -------------------------------------------------- -./gradlew setupPackagingTest -cd qa/vagrant; vagrant up ubuntu-1404 --provider virtualbox && vagrant ssh ubuntu-1404 +-------------------------------------------- +./gradlew :qa:vagrant:vagrantDebian9#up +vagrant ssh debian-9 +-------------------------------------------- + +Now inside the VM, to run the https://github.com/sstephenson/bats[bats] packaging tests + +-------------------------------------------- cd $PACKAGING_ARCHIVES -sudo -E bats $BATS_TESTS/*.bats -------------------------------------------------- -You can also use Gradle to prepare the test environment and then starts a single VM: +# runs all bats tests +sudo bats $BATS_TESTS/*.bats -------------------------------------------------- -./gradlew vagrantFedora27#up -------------------------------------------------- +# you can also pass specific test files +sudo bats $BATS_TESTS/20_tar_package.bats $BATS_TESTS/25_tar_plugins.bats +-------------------------------------------- -Or any of vagrantCentos6#up, vagrantCentos7#up, vagrantDebian8#up, -vagrantDebian9#up, vagrantFedora26#up, vagrantFedora27#up, vagrantOel6#up, vagrantOel7#up, -vagrantOpensuse42#up,vagrantSles12#up, vagrantUbuntu1404#up, vagrantUbuntu1604#up. +To run the Java packaging tests, again inside the VM -Once up, you can then connect to the VM using SSH from the elasticsearch directory: +-------------------------------------------- +bash $PACKAGING_TESTS/run-tests.sh +-------------------------------------------- -------------------------------------------------- -vagrant ssh fedora-27 -------------------------------------------------- +or on Windows -Or from another directory: +-------------------------------------------- +powershell -File $Env:PACKAGING_TESTS/run-tests.ps1 +-------------------------------------------- -------------------------------------------------- -VAGRANT_CWD=/path/to/elasticsearch vagrant ssh fedora-27 -------------------------------------------------- +When you've made changes you want to test, keep the VM up and reload the tests and +distributions inside by running (on the host) + +-------------------------------------------- +./gradlew :qa:vagrant:clean :qa:vagrant:setupPackagingTest +-------------------------------------------- Note: Starting vagrant VM outside of the elasticsearch folder requires to indicates the folder that contains the Vagrantfile using the VAGRANT_CWD diff --git a/Vagrantfile b/Vagrantfile index 6761fec07dab2..1c259c1125f00 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -121,6 +121,26 @@ Vagrant.configure(2) do |config| sles_common config, box end end + + windows_2012r2_box = ENV['VAGRANT_WINDOWS_2012R2_BOX'] + if windows_2012r2_box && windows_2012r2_box.empty? == false + 'windows-2012r2'.tap do |box| + config.vm.define box, define_opts do |config| + config.vm.box = windows_2012r2_box + windows_common config, box + end + end + end + + windows_2016_box = ENV['VAGRANT_WINDOWS_2016_BOX'] + if windows_2016_box && windows_2016_box.empty? == false + 'windows-2016'.tap do |box| + config.vm.define box, define_opts do |config| + config.vm.box = windows_2016_box + windows_common config, box + end + end + end end def deb_common(config, name, extra: '') @@ -353,3 +373,22 @@ SUDOERS_VARS chmod 0440 /etc/sudoers.d/elasticsearch_vars SHELL end + +def windows_common(config, name) + config.vm.provision 'markerfile', type: 'shell', inline: <<-SHELL + $ErrorActionPreference = "Stop" + New-Item C:/is_vagrant_vm -ItemType file -Force | Out-Null + SHELL + + config.vm.provision 'set prompt', type: 'shell', inline: <<-SHELL + $ErrorActionPreference = "Stop" + $ps_prompt = 'function Prompt { "#{name}:$($ExecutionContext.SessionState.Path.CurrentLocation)>" }' + $ps_prompt | Out-File $PsHome/Microsoft.PowerShell_profile.ps1 + SHELL + + config.vm.provision 'set env variables', type: 'shell', inline: <<-SHELL + $ErrorActionPreference = "Stop" + [Environment]::SetEnvironmentVariable("PACKAGING_ARCHIVES", "C:/project/build/packaging/archives", "Machine") + [Environment]::SetEnvironmentVariable("PACKAGING_TESTS", "C:/project/build/packaging/tests", "Machine") + SHELL +end diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy index bb85359ae3f07..e0a1532cbfc80 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy @@ -16,7 +16,7 @@ import org.gradle.api.tasks.TaskState class VagrantTestPlugin implements Plugin { /** All available boxes **/ - static List BOXES = [ + static List LINUX_BOXES = [ 'centos-6', 'centos-7', 'debian-8', @@ -31,12 +31,21 @@ class VagrantTestPlugin implements Plugin { 'ubuntu-1604' ] + /** Windows boxes that are available - map of box names to image IDs **/ + static Map WINDOWS_BOXES = [:] + + /** All available boxes **/ + static List BOXES + /** Boxes used when sampling the tests **/ static List SAMPLE = [ 'centos-7', 'ubuntu-1404', ] + /** extra env vars to pass to vagrant for box configuration **/ + static Map VAGRANT_ENV_VARS = [:] + /** All distributions to bring into test VM, whether or not they are used **/ static List DISTRIBUTIONS = [ 'archives:tar', @@ -59,8 +68,10 @@ class VagrantTestPlugin implements Plugin { @Override void apply(Project project) { + collectAvailableBoxes(project) + // Creates the Vagrant extension for the project - project.extensions.create('esvagrant', VagrantPropertiesExtension, listVagrantBoxes(project)) + project.extensions.create('esvagrant', VagrantPropertiesExtension, listSelectedBoxes(project)) // Add required repositories for packaging tests configurePackagingArchiveRepositories(project) @@ -86,14 +97,41 @@ class VagrantTestPlugin implements Plugin { createVagrantBoxesTasks(project) } - private List listVagrantBoxes(Project project) { + /** + * Enumerate all the boxes that we know about and could possibly choose to test + */ + private static void collectAvailableBoxes(Project project) { + String windows_2012r2_box = System.getenv('VAGRANT_WINDOWS_2012R2_BOX') + if (windows_2012r2_box != null && windows_2012r2_box.isEmpty() == false) { + WINDOWS_BOXES['windows-2012r2'] = windows_2012r2_box + VAGRANT_ENV_VARS['VAGRANT_WINDOWS_2012R2_BOX'] = windows_2012r2_box + } + + String windows_2016_box = System.getenv('VAGRANT_WINDOWS_2016_BOX') + if (windows_2016_box != null && windows_2016_box.isEmpty() == false) { + WINDOWS_BOXES['windows-2016'] = windows_2016_box + VAGRANT_ENV_VARS['VAGRANT_WINDOWS_2016_BOX'] = windows_2016_box + } + + BOXES = LINUX_BOXES + WINDOWS_BOXES.keySet() + } + + /** + * Enumerate all the boxes that we have chosen to test + */ + private List listSelectedBoxes(Project project) { String vagrantBoxes = project.getProperties().get('vagrant.boxes', 'sample') - if (vagrantBoxes == 'sample') { - return SAMPLE - } else if (vagrantBoxes == 'all') { - return BOXES - } else { - return vagrantBoxes.split(',') + switch (vagrantBoxes) { + case 'sample': + return SAMPLE + case 'linux-all': + return LINUX_BOXES + case 'windows-all': + return WINDOWS_BOXES.keySet().toList() + case 'all': + return BOXES + default: + return vagrantBoxes.split(',') } } @@ -184,11 +222,19 @@ class VagrantTestPlugin implements Plugin { from project.configurations[PACKAGING_TEST_CONFIGURATION] } - Task createTestRunnerScript = project.tasks.create('createTestRunnerScript', FileContentsTask) { + Task createLinuxRunnerScript = project.tasks.create('createLinuxRunnerScript', FileContentsTask) { dependsOn copyPackagingTests file "${testsDir}/run-tests.sh" contents "java -cp \"\$PACKAGING_TESTS/*\" org.junit.runner.JUnitCore ${-> project.extensions.esvagrant.testClass}" } + Task createWindowsRunnerScript = project.tasks.create('createWindowsRunnerScript', FileContentsTask) { + dependsOn copyPackagingTests + file "${testsDir}/run-tests.ps1" + contents """\ + java -cp "\$Env:PACKAGING_TESTS/*" org.junit.runner.JUnitCore ${-> project.extensions.esvagrant.testClass} + exit \$LASTEXITCODE + """ + } Task createVersionFile = project.tasks.create('createVersionFile', FileContentsTask) { dependsOn copyPackagingArchives @@ -249,10 +295,18 @@ class VagrantTestPlugin implements Plugin { } Task vagrantSetUpTask = project.tasks.create('setupPackagingTest') - vagrantSetUpTask.dependsOn 'vagrantCheckVersion' - vagrantSetUpTask.dependsOn copyPackagingArchives, copyPackagingTests, createTestRunnerScript - vagrantSetUpTask.dependsOn createVersionFile, createUpgradeFromFile, createUpgradeIsOssFile - vagrantSetUpTask.dependsOn copyBatsTests, copyBatsUtils + vagrantSetUpTask.dependsOn( + 'vagrantCheckVersion', + copyPackagingArchives, + copyPackagingTests, + createLinuxRunnerScript, + createWindowsRunnerScript, + createVersionFile, + createUpgradeFromFile, + createUpgradeIsOssFile, + copyBatsTests, + copyBatsUtils + ) } private static void createPackagingTestTask(Project project) { @@ -287,6 +341,20 @@ class VagrantTestPlugin implements Plugin { createPlatformTestTask(project) } + private static Map vagrantEnvVars(Project project) { + /* + * We always use the main project.rootDir as Vagrant's current working directory (VAGRANT_CWD) + * so that boxes are not duplicated for every Gradle project that use this VagrantTestPlugin. + */ + def vagrantEnvVars = [ + 'VAGRANT_CWD' : "${project.rootDir.absolutePath}", + 'VAGRANT_VAGRANTFILE': 'Vagrantfile', + 'VAGRANT_PROJECT_DIR': "${project.projectDir.absolutePath}" + ] + vagrantEnvVars.putAll(VAGRANT_ENV_VARS) + return vagrantEnvVars + } + private static void createVagrantBoxesTasks(Project project) { assert project.extensions.esvagrant.boxes != null @@ -311,15 +379,7 @@ class VagrantTestPlugin implements Plugin { assert project.tasks.platformTest != null Task platformTest = project.tasks.platformTest - /* - * We always use the main project.rootDir as Vagrant's current working directory (VAGRANT_CWD) - * so that boxes are not duplicated for every Gradle project that use this VagrantTestPlugin. - */ - def vagrantEnvVars = [ - 'VAGRANT_CWD' : "${project.rootDir.absolutePath}", - 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', - 'VAGRANT_PROJECT_DIR' : "${project.projectDir.absolutePath}" - ] + def vagrantEnvVars = vagrantEnvVars(project) // Each box gets it own set of tasks for (String box : BOXES) { @@ -363,6 +423,7 @@ class VagrantTestPlugin implements Plugin { final Task destroy = project.tasks.create("vagrant${boxTask}#destroy", LoggedExec) { commandLine "bash", "-c", "vagrant status ${box} | grep -q \"${box}\\s\\+not created\" || vagrant destroy ${box} --force" workingDir project.rootProject.rootDir + environment vagrantEnvVars } destroy.onlyIf { vagrantDestroy } update.mustRunAfter(destroy) @@ -386,37 +447,42 @@ class VagrantTestPlugin implements Plugin { environment vagrantEnvVars dependsOn up finalizedBy halt - commandLine 'vagrant', 'ssh', box, '--command', - "set -o pipefail && echo 'Hello from ${project.path}' | sed -ue 's/^/ ${box}: /'" } vagrantSmokeTest.dependsOn(smoke) - - Task batsPackagingTest = project.tasks.create("vagrant${boxTask}#batsPackagingTest", BatsOverVagrantTask) { - remoteCommand BATS_TEST_COMMAND - boxName box - environmentVars vagrantEnvVars - dependsOn up, setupPackagingTest - finalizedBy halt + if (box in LINUX_BOXES) { + smoke.commandLine = ['vagrant', 'ssh', box, '--command', + "set -o pipefail && echo 'Hello from ${project.path}' | sed -ue 's/^/ ${box}: /'"] + } else { + smoke.commandLine = ['vagrant', 'winrm', box, '--command', + "Write-Host ' ${box}: Hello from ${project.path}'"] } - TaskExecutionAdapter batsPackagingReproListener = createReproListener(project, batsPackagingTest.path) - batsPackagingTest.doFirst { - project.gradle.addListener(batsPackagingReproListener) - } - batsPackagingTest.doLast { - project.gradle.removeListener(batsPackagingReproListener) - } - if (project.extensions.esvagrant.boxes.contains(box)) { - packagingTest.dependsOn(batsPackagingTest) + if (box in LINUX_BOXES) { + Task batsPackagingTest = project.tasks.create("vagrant${boxTask}#batsPackagingTest", BatsOverVagrantTask) { + remoteCommand BATS_TEST_COMMAND + boxName box + environmentVars vagrantEnvVars + dependsOn up, setupPackagingTest + finalizedBy halt + } + + TaskExecutionAdapter batsPackagingReproListener = createReproListener(project, batsPackagingTest.path) + batsPackagingTest.doFirst { + project.gradle.addListener(batsPackagingReproListener) + } + batsPackagingTest.doLast { + project.gradle.removeListener(batsPackagingReproListener) + } + if (project.extensions.esvagrant.boxes.contains(box)) { + packagingTest.dependsOn(batsPackagingTest) + } } Task javaPackagingTest = project.tasks.create("vagrant${boxTask}#javaPackagingTest", VagrantCommandTask) { - command 'ssh' boxName box environmentVars vagrantEnvVars dependsOn up, setupPackagingTest finalizedBy halt - args '--command', "bash \"\$PACKAGING_TESTS/run-tests.sh\"" } // todo remove this onlyIf after all packaging tests are consolidated @@ -424,6 +490,14 @@ class VagrantTestPlugin implements Plugin { project.extensions.esvagrant.testClass != null } + if (box in LINUX_BOXES) { + javaPackagingTest.command = 'ssh' + javaPackagingTest.args = ['--command', 'bash "$PACKAGING_TESTS/run-tests.sh"'] + } else { + javaPackagingTest.command = 'winrm' + javaPackagingTest.args = ['--command', 'powershell -File "$Env:PACKAGING_TESTS/run-tests.ps1"'] + } + TaskExecutionAdapter javaPackagingReproListener = createReproListener(project, javaPackagingTest.path) javaPackagingTest.doFirst { project.gradle.addListener(javaPackagingReproListener) @@ -435,23 +509,29 @@ class VagrantTestPlugin implements Plugin { packagingTest.dependsOn(javaPackagingTest) } - Task platform = project.tasks.create("vagrant${boxTask}#platformTest", VagrantCommandTask) { - command 'ssh' - boxName box - environmentVars vagrantEnvVars - dependsOn up - finalizedBy halt - args '--command', PLATFORM_TEST_COMMAND + " -Dtests.seed=${-> project.testSeed}" - } - TaskExecutionAdapter platformReproListener = createReproListener(project, platform.path) - platform.doFirst { - project.gradle.addListener(platformReproListener) - } - platform.doLast { - project.gradle.removeListener(platformReproListener) - } - if (project.extensions.esvagrant.boxes.contains(box)) { - platformTest.dependsOn(platform) + /* + * This test is unmaintained and was created to run on Linux. We won't allow it to run on Windows + * until it's been brought back into maintenance + */ + if (box in LINUX_BOXES) { + Task platform = project.tasks.create("vagrant${boxTask}#platformTest", VagrantCommandTask) { + command 'ssh' + boxName box + environmentVars vagrantEnvVars + dependsOn up + finalizedBy halt + args '--command', PLATFORM_TEST_COMMAND + " -Dtests.seed=${-> project.testSeed}" + } + TaskExecutionAdapter platformReproListener = createReproListener(project, platform.path) + platform.doFirst { + project.gradle.addListener(platformReproListener) + } + platform.doLast { + project.gradle.removeListener(platformReproListener) + } + if (project.extensions.esvagrant.boxes.contains(box)) { + platformTest.dependsOn(platform) + } } } }