Skip to content

Commit 32d67ef

Browse files
authored
Fixture for Minio testing (#31688)
Adds a Minio fixture to run the S3 repository tests against Minio. Also collapses the single qa subproject into the s3-repository project, which simplifies the code structure (having it all in one place) and helps to avoid having too many Gradle subprojects.
1 parent 42ff8aa commit 32d67ef

File tree

7 files changed

+257
-158
lines changed

7 files changed

+257
-158
lines changed

plugins/repository-s3/build.gradle

+245-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import org.apache.tools.ant.taskdefs.condition.Os
2+
import org.elasticsearch.gradle.LoggedExec
3+
import org.elasticsearch.gradle.MavenFilteringHack
4+
import org.elasticsearch.gradle.test.AntFixture
5+
import org.elasticsearch.gradle.test.ClusterConfiguration
6+
import org.elasticsearch.gradle.test.RestIntegTestTask
7+
8+
import java.lang.reflect.Field
9+
110
/*
211
* Licensed to Elasticsearch under one or more contributor
312
* license agreements. See the NOTICE file distributed with
@@ -64,14 +73,245 @@ test {
6473
exclude '**/*CredentialsTests.class'
6574
}
6675

67-
check {
68-
// also execute the QA tests when testing the plugin
69-
dependsOn 'qa:amazon-s3:check'
76+
boolean useFixture = false
77+
78+
// We test against two repositories, one which uses the usual two-part "permanent" credentials and
79+
// the other which uses three-part "temporary" or "session" credentials.
80+
81+
String s3PermanentAccessKey = System.getenv("amazon_s3_access_key")
82+
String s3PermanentSecretKey = System.getenv("amazon_s3_secret_key")
83+
String s3PermanentBucket = System.getenv("amazon_s3_bucket")
84+
String s3PermanentBasePath = System.getenv("amazon_s3_base_path")
85+
86+
String s3TemporaryAccessKey = System.getenv("amazon_s3_access_key_temporary")
87+
String s3TemporarySecretKey = System.getenv("amazon_s3_secret_key_temporary")
88+
String s3TemporarySessionToken = System.getenv("amazon_s3_session_token_temporary")
89+
String s3TemporaryBucket = System.getenv("amazon_s3_bucket_temporary")
90+
String s3TemporaryBasePath = System.getenv("amazon_s3_base_path_temporary")
91+
92+
// If all these variables are missing then we are testing against the internal fixture instead, which has the following
93+
// credentials hard-coded in.
94+
95+
if (!s3PermanentAccessKey && !s3PermanentSecretKey && !s3PermanentBucket && !s3PermanentBasePath
96+
&& !s3TemporaryAccessKey && !s3TemporarySecretKey && !s3TemporaryBucket && !s3TemporaryBasePath && !s3TemporarySessionToken) {
97+
98+
s3PermanentAccessKey = 's3_integration_test_permanent_access_key'
99+
s3PermanentSecretKey = 's3_integration_test_permanent_secret_key'
100+
s3PermanentBucket = 'permanent-bucket-test'
101+
s3PermanentBasePath = 'integration_test'
102+
103+
s3TemporaryAccessKey = 's3_integration_test_temporary_access_key'
104+
s3TemporarySecretKey = 's3_integration_test_temporary_secret_key'
105+
s3TemporaryBucket = 'temporary-bucket-test'
106+
s3TemporaryBasePath = 'integration_test'
107+
s3TemporarySessionToken = 's3_integration_test_temporary_session_token'
108+
109+
useFixture = true
110+
} else if (!s3PermanentAccessKey || !s3PermanentSecretKey || !s3PermanentBucket || !s3PermanentBasePath
111+
|| !s3TemporaryAccessKey || !s3TemporarySecretKey || !s3TemporaryBucket || !s3TemporaryBasePath || !s3TemporarySessionToken) {
112+
throw new IllegalArgumentException("not all options specified to run against external S3 service")
113+
}
114+
115+
final String minioVersion = 'RELEASE.2018-06-22T23-48-46Z'
116+
final String minioBinDir = "${buildDir}/minio/bin"
117+
final String minioDataDir = "${buildDir}/minio/data"
118+
final String minioAddress = "127.0.0.1:60920"
119+
120+
final String minioDistribution
121+
final String minioCheckSum
122+
if (Os.isFamily(Os.FAMILY_MAC)) {
123+
minioDistribution = 'darwin-amd64'
124+
minioCheckSum = '96b0bcb2f590e8e65fb83d5c3e221f9bd1106b49fa6f22c6b726b80b845d7c60'
125+
} else if (Os.isFamily(Os.FAMILY_UNIX)) {
126+
minioDistribution = 'linux-amd64'
127+
minioCheckSum = '713dac7c105285eab3b92649be92b5e793b29d3525c7929fa7aaed99374fad99'
128+
} else {
129+
minioDistribution = null
130+
minioCheckSum = null
131+
}
132+
133+
buildscript {
134+
repositories {
135+
maven {
136+
url 'https://plugins.gradle.org/m2/'
137+
}
138+
}
139+
dependencies {
140+
classpath 'de.undercouch:gradle-download-task:3.4.3'
141+
}
142+
}
143+
144+
if (useFixture && minioDistribution) {
145+
apply plugin: 'de.undercouch.download'
146+
147+
final String minioFileName = "minio.${minioVersion}"
148+
final String minioDownloadURL = "https://dl.minio.io/server/minio/release/${minioDistribution}/archive/${minioFileName}"
149+
final String minioFilePath = "${gradle.gradleUserHomeDir}/downloads/minio/${minioDistribution}/${minioFileName}"
150+
151+
task downloadMinio(type: Download) {
152+
src minioDownloadURL
153+
dest minioFilePath
154+
onlyIfModified true
155+
}
156+
157+
task verifyMinioChecksum(type: Verify, dependsOn: downloadMinio) {
158+
src minioFilePath
159+
algorithm 'SHA-256'
160+
checksum minioCheckSum
161+
}
162+
163+
task installMinio(type: Sync, dependsOn: verifyMinioChecksum) {
164+
from minioFilePath
165+
into minioBinDir
166+
fileMode 0755
167+
}
168+
169+
task startMinio {
170+
dependsOn installMinio
171+
172+
ext.minioPid = 0L
173+
174+
doLast {
175+
new File("${minioDataDir}/${s3PermanentBucket}").mkdirs()
176+
// we skip these tests on Windows so we do no need to worry about compatibility here
177+
final ProcessBuilder minio = new ProcessBuilder(
178+
"${minioBinDir}/${minioFileName}",
179+
"server",
180+
"--address",
181+
minioAddress,
182+
minioDataDir)
183+
minio.environment().put('MINIO_ACCESS_KEY', s3PermanentAccessKey)
184+
minio.environment().put('MINIO_SECRET_KEY', s3PermanentSecretKey)
185+
final Process process = minio.start()
186+
if (JavaVersion.current() <= JavaVersion.VERSION_1_8) {
187+
try {
188+
Class<?> cProcessImpl = process.getClass()
189+
Field fPid = cProcessImpl.getDeclaredField("pid")
190+
if (!fPid.isAccessible()) {
191+
fPid.setAccessible(true)
192+
}
193+
minioPid = fPid.getInt(process)
194+
} catch (Exception e) {
195+
logger.error("failed to read pid from minio process", e)
196+
process.destroyForcibly()
197+
throw e
198+
}
199+
} else {
200+
minioPid = process.pid()
201+
}
202+
203+
new BufferedReader(new InputStreamReader(process.getInputStream())).withReader { br ->
204+
String line
205+
int httpPort = 0
206+
while ((line = br.readLine()) != null) {
207+
logger.info(line)
208+
if (line.matches('.*Endpoint.*:\\d+$')) {
209+
assert httpPort == 0
210+
final int index = line.lastIndexOf(":")
211+
assert index >= 0
212+
httpPort = Integer.parseInt(line.substring(index + 1))
213+
214+
final File script = new File(project.buildDir, "minio/minio.killer.sh")
215+
script.setText(
216+
["function shutdown {",
217+
" kill ${minioPid}",
218+
"}",
219+
"trap shutdown EXIT",
220+
// will wait indefinitely for input, but we never pass input, and the pipe is only closed when the build dies
221+
"read line\n"].join('\n'), 'UTF-8')
222+
final ProcessBuilder killer = new ProcessBuilder("bash", script.absolutePath)
223+
killer.start()
224+
break
225+
}
226+
}
227+
228+
assert httpPort > 0
229+
}
230+
}
231+
}
232+
233+
task stopMinio(type: LoggedExec) {
234+
onlyIf { startMinio.minioPid > 0 }
235+
236+
doFirst {
237+
logger.info("Shutting down minio with pid ${startMinio.minioPid}")
238+
}
239+
240+
final Object pid = "${ -> startMinio.minioPid }"
241+
242+
// we skip these tests on Windows so we do no need to worry about compatibility here
243+
executable = 'kill'
244+
args('-9', pid)
245+
}
246+
247+
RestIntegTestTask integTestMinio = project.tasks.create('integTestMinio', RestIntegTestTask.class) {
248+
description = "Runs REST tests using the Minio repository."
249+
}
250+
251+
// The following closure must execute before the afterEvaluate block in the constructor of the following integrationTest tasks:
252+
project.afterEvaluate {
253+
ClusterConfiguration cluster = project.extensions.getByName('integTestMinioCluster') as ClusterConfiguration
254+
cluster.dependsOn(project.bundlePlugin)
255+
cluster.keystoreSetting 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey
256+
cluster.keystoreSetting 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey
257+
258+
cluster.setting 's3.client.integration_test_permanent.endpoint', "http://${minioAddress}"
259+
260+
Task restIntegTestTask = project.tasks.getByName('integTestMinio')
261+
restIntegTestTask.clusterConfig.plugin(project.path)
262+
263+
// Default jvm arguments for all test clusters
264+
String jvmArgs = "-Xms" + System.getProperty('tests.heap.size', '512m') +
265+
" " + "-Xmx" + System.getProperty('tests.heap.size', '512m') +
266+
" " + System.getProperty('tests.jvm.argline', '')
267+
268+
restIntegTestTask.clusterConfig.jvmArgs = jvmArgs
269+
}
270+
271+
integTestMinioRunner.dependsOn(startMinio)
272+
integTestMinioRunner.finalizedBy(stopMinio)
273+
// Minio only supports a single access key, see https://github.com/minio/minio/pull/5968
274+
integTestMinioRunner.systemProperty 'tests.rest.blacklist', 'repository_s3/30_repository_temporary_credentials/*'
275+
276+
project.check.dependsOn(integTestMinio)
277+
}
278+
279+
/** A task to start the AmazonS3Fixture which emulates an S3 service **/
280+
task s3Fixture(type: AntFixture) {
281+
dependsOn testClasses
282+
env 'CLASSPATH', "${ -> project.sourceSets.test.runtimeClasspath.asPath }"
283+
executable = new File(project.runtimeJavaHome, 'bin/java')
284+
args 'org.elasticsearch.repositories.s3.AmazonS3Fixture', baseDir, s3PermanentBucket, s3TemporaryBucket
285+
}
286+
287+
Map<String, Object> expansions = [
288+
'permanent_bucket': s3PermanentBucket,
289+
'permanent_base_path': s3PermanentBasePath,
290+
'temporary_bucket': s3TemporaryBucket,
291+
'temporary_base_path': s3TemporaryBasePath
292+
]
293+
294+
processTestResources {
295+
inputs.properties(expansions)
296+
MavenFilteringHack.filter(it, expansions)
70297
}
71298

72299
integTestCluster {
73-
keystoreSetting 's3.client.integration_test.access_key', "s3_integration_test_access_key"
74-
keystoreSetting 's3.client.integration_test.secret_key', "s3_integration_test_secret_key"
300+
keystoreSetting 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey
301+
keystoreSetting 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey
302+
303+
keystoreSetting 's3.client.integration_test_temporary.access_key', s3TemporaryAccessKey
304+
keystoreSetting 's3.client.integration_test_temporary.secret_key', s3TemporarySecretKey
305+
keystoreSetting 's3.client.integration_test_temporary.session_token', s3TemporarySessionToken
306+
307+
if (useFixture) {
308+
dependsOn s3Fixture
309+
/* Use a closure on the string to delay evaluation until tests are executed */
310+
setting 's3.client.integration_test_permanent.endpoint', "http://${-> s3Fixture.addressAndPort}"
311+
setting 's3.client.integration_test_temporary.endpoint', "http://${-> s3Fixture.addressAndPort}"
312+
} else {
313+
println "Using an external service to test the repository-s3 plugin"
314+
}
75315
}
76316

77317
thirdPartyAudit.excludes = [

plugins/repository-s3/qa/amazon-s3/build.gradle

-112
This file was deleted.

0 commit comments

Comments
 (0)