From be6bdab346fa5058361ed18cbd3cc29ff9052b4f Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Fri, 25 Jan 2019 12:56:40 +0100 Subject: [PATCH] Use TestFixturesPlugin to Run Minio in Tests (#37852) * Use TestFixturesPlugin to Run Minio in Tests * Closes #37680 * Closes #37783 --- plugins/repository-s3/build.gradle | 204 +++-------------------- plugins/repository-s3/docker-compose.yml | 9 + 2 files changed, 31 insertions(+), 182 deletions(-) create mode 100644 plugins/repository-s3/docker-compose.yml diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index 541e6dc42f5..49c60d2edd7 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -1,13 +1,10 @@ -import org.apache.tools.ant.taskdefs.condition.Os -import org.elasticsearch.gradle.LoggedExec +import org.elasticsearch.gradle.BuildPlugin import org.elasticsearch.gradle.MavenFilteringHack import org.elasticsearch.gradle.test.AntFixture import org.elasticsearch.gradle.test.ClusterConfiguration import org.elasticsearch.gradle.test.RestIntegTestTask import com.carrotsearch.gradle.junit4.RandomizedTestingTask -import java.lang.reflect.Field - /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with @@ -139,25 +136,6 @@ if (!s3EC2Bucket && !s3EC2BasePath && !s3ECSBucket && !s3ECSBasePath) { throw new IllegalArgumentException("not all options specified to run EC2/ECS tests are present") } - -final String minioVersion = 'RELEASE.2018-06-22T23-48-46Z' -final String minioBinDir = "${buildDir}/minio/bin" -final String minioDataDir = "${buildDir}/minio/data" -final String minioAddress = "127.0.0.1" - -String minioDistribution -String minioCheckSum -if (Os.isFamily(Os.FAMILY_MAC)) { - minioDistribution = 'darwin-amd64' - minioCheckSum = '96b0bcb2f590e8e65fb83d5c3e221f9bd1106b49fa6f22c6b726b80b845d7c60' -} else if (Os.isFamily(Os.FAMILY_UNIX)) { - minioDistribution = 'linux-amd64' - minioCheckSum = '713dac7c105285eab3b92649be92b5e793b29d3525c7929fa7aaed99374fad99' -} else { - minioDistribution = null - minioCheckSum = null -} - buildscript { repositories { maven { @@ -169,177 +147,39 @@ buildscript { } } -private static int freePort(String minioAddress) { - int minioPort - ServerSocket serverSocket = new ServerSocket(0, 1, InetAddress.getByName(minioAddress)) - try { - minioPort = serverSocket.localPort - } finally { - serverSocket.close() - } - if (minioPort == 0) { - throw new GradleException("Could not find a free port for Minio") - } - return minioPort -} +if (useFixture) { -private int getMinioPid(Process minioProcess) { - int minioPid - if (JavaVersion.current() <= JavaVersion.VERSION_1_8) { - try { - Class cProcessImpl = minioProcess.getClass() - Field fPid = cProcessImpl.getDeclaredField("pid") - if (!fPid.isAccessible()) { - fPid.setAccessible(true) - } - minioPid = fPid.getInt(minioProcess) - } catch (Exception e) { - logger.error("failed to read pid from minio process", e) - minioProcess.destroyForcibly() - throw e - } - } else { - minioPid = minioProcess.pid() - } - return minioPid -} - -private static Process setupMinio(String minioAddress, int minioPort, String minioDataDir, String accessKey, String secretKey, - String minioBinDir, String minioFileName) { - // we skip these tests on Windows so we do no need to worry about compatibility here - final ProcessBuilder minio = new ProcessBuilder( - "${minioBinDir}/${minioFileName}", - "server", - "--address", - minioAddress + ":" + minioPort, - minioDataDir) - minio.environment().put('MINIO_ACCESS_KEY', accessKey) - minio.environment().put('MINIO_SECRET_KEY', secretKey) - return minio.start() -} - -private void addShutdownHook(Process minioProcess, int minioPort, int minioPid) { - new BufferedReader(new InputStreamReader(minioProcess.inputStream)).withReader { br -> - String line - int httpPort = 0 - while ((line = br.readLine()) != null) { - logger.info(line) - if (line.matches('.*Endpoint.*:\\d+$')) { - assert httpPort == 0 - final int index = line.lastIndexOf(":") - assert index >= 0 - httpPort = Integer.parseInt(line.substring(index + 1)) - if (httpPort != minioPort) { - throw new IllegalStateException("Port mismatch, expected ${minioPort} but was ${httpPort}") - } - - final File script = new File(project.buildDir, "minio/minio.killer.sh") - script.setText( - ["function shutdown {", - " kill ${minioPid}", - "}", - "trap shutdown EXIT", - // will wait indefinitely for input, but we never pass input, and the pipe is only closed when the build dies - "read line\n"].join('\n'), 'UTF-8') - final ProcessBuilder killer = new ProcessBuilder("bash", script.absolutePath) - killer.start() - break - } - } - - if (httpPort <= 0) { - throw new IllegalStateException("httpPort must be > 0") - } - } -} - -if (useFixture && minioDistribution) { - apply plugin: 'de.undercouch.download' - - final String minioFileName = "minio.${minioVersion}" - final String minioDownloadURL = "https://dl.minio.io/server/minio/release/${minioDistribution}/archive/${minioFileName}" - final String minioFilePath = "${gradle.gradleUserHomeDir}/downloads/minio/${minioDistribution}/${minioFileName}" - - task downloadMinio(type: Download) { - src minioDownloadURL - dest minioFilePath - onlyIfModified true - } - - task verifyMinioChecksum(type: Verify, dependsOn: downloadMinio) { - src minioFilePath - algorithm 'SHA-256' - checksum minioCheckSum - } - - task installMinio(type: Sync, dependsOn: verifyMinioChecksum) { - from minioFilePath - into minioBinDir - fileMode 0755 - } - - task startMinio { - dependsOn installMinio - - ext.minioPid = 0L - ext.minioPort = 0 - - doLast { - new File("${minioDataDir}/${s3PermanentBucket}").mkdirs() - - Exception accumulatedException = null - for (int i = 0; i < 5; ++i) { - try { - minioPort = freePort(minioAddress) - final Process process = - setupMinio(minioAddress, minioPort, minioDataDir, s3PermanentAccessKey, s3PermanentSecretKey, minioBinDir, minioFileName) - minioPid = getMinioPid(process) - addShutdownHook(process, minioPort, minioPid) - break - } catch (Exception e) { - logger.error("Exception while trying to start Minio {}", e) - if (accumulatedException == null) { - accumulated = e - } else { - accumulatedException.addSuppressed(e) - } - } - } - if (accumulatedException != null) { - throw new GradleException("Failed to start Minio", accumulatedException) - } - } - } - - task stopMinio(type: LoggedExec) { - onlyIf { startMinio.minioPid > 0 } - - doFirst { - logger.info("Shutting down minio with pid ${startMinio.minioPid}") - } - - final Object pid = "${ -> startMinio.minioPid }" - - // we skip these tests on Windows so we do no need to worry about compatibility here - executable = 'kill' - args('-9', pid) - } + apply plugin: 'elasticsearch.test.fixtures' RestIntegTestTask integTestMinio = project.tasks.create('integTestMinio', RestIntegTestTask.class) { description = "Runs REST tests using the Minio repository." } + Task writeDockerFile = project.tasks.create('writeDockerFile') { + File minioDockerfile = new File("${project.buildDir}/minio-docker/Dockerfile") + outputs.file(minioDockerfile) + doLast { + minioDockerfile.parentFile.mkdirs() + minioDockerfile.text = "FROM minio/minio:RELEASE.2019-01-23T23-18-58Z\n" + + "RUN mkdir -p /minio/data/${s3PermanentBucket}\n" + + "ENV MINIO_ACCESS_KEY ${s3PermanentAccessKey}\n" + + "ENV MINIO_SECRET_KEY ${s3PermanentSecretKey}" + } + } + + preProcessFixture.dependsOn(writeDockerFile) // The following closure must execute before the afterEvaluate block in the constructor of the following integrationTest tasks: project.afterEvaluate { ClusterConfiguration cluster = project.extensions.getByName('integTestMinioCluster') as ClusterConfiguration cluster.dependsOn(project.bundlePlugin) - cluster.dependsOn(startMinio) // otherwise we don't know the Minio port + cluster.dependsOn(postProcessFixture) cluster.keystoreSetting 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey cluster.keystoreSetting 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey Closure minioAddressAndPort = { - assert startMinio.minioPort > 0 - return 'http://' + minioAddress + ':' + startMinio.minioPort + int minioPort = postProcessFixture.ext."test.fixtures.minio-fixture.tcp.9000" + assert minioPort > 0 + return 'http://127.0.0.1:' + minioPort } cluster.setting 's3.client.integration_test_permanent.endpoint', "${ -> minioAddressAndPort.call()}" @@ -354,8 +194,7 @@ if (useFixture && minioDistribution) { restIntegTestTask.clusterConfig.jvmArgs = jvmArgs } - integTestMinioRunner.dependsOn(startMinio) - integTestMinioRunner.finalizedBy(stopMinio) + integTestMinioRunner.dependsOn(postProcessFixture) // Minio only supports a single access key, see https://github.com/minio/minio/pull/5968 integTestMinioRunner.systemProperty 'tests.rest.blacklist', [ 'repository_s3/30_repository_temporary_credentials/*', @@ -364,6 +203,7 @@ if (useFixture && minioDistribution) { ].join(",") project.check.dependsOn(integTestMinio) + BuildPlugin.requireDocker(integTestMinio) } File parentFixtures = new File(project.buildDir, "fixtures") diff --git a/plugins/repository-s3/docker-compose.yml b/plugins/repository-s3/docker-compose.yml new file mode 100644 index 00000000000..e44750550e2 --- /dev/null +++ b/plugins/repository-s3/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' +services: + minio-fixture: + build: + context: ./build/minio-docker + dockerfile: Dockerfile + ports: + - "9000" + command: ["server", "/minio/data"] \ No newline at end of file