Skip to content

Commit d7fe4e5

Browse files
Make Minio Setup more Reliable (#37747)
* Retry starting Minio five times in case we run into a race between finding the free port and starting it up * Closes #37680
1 parent 169cb38 commit d7fe4e5

File tree

1 file changed

+101
-61
lines changed

1 file changed

+101
-61
lines changed

plugins/repository-s3/build.gradle

Lines changed: 101 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,90 @@ buildscript {
169169
}
170170
}
171171

172+
private static int freePort(String minioAddress) {
173+
int minioPort
174+
ServerSocket serverSocket = new ServerSocket(0, 1, InetAddress.getByName(minioAddress))
175+
try {
176+
minioPort = serverSocket.localPort
177+
} finally {
178+
serverSocket.close()
179+
}
180+
if (minioPort == 0) {
181+
throw new GradleException("Could not find a free port for Minio")
182+
}
183+
return minioPort
184+
}
185+
186+
private int getMinioPid(Process minioProcess) {
187+
int minioPid
188+
if (JavaVersion.current() <= JavaVersion.VERSION_1_8) {
189+
try {
190+
Class<?> cProcessImpl = minioProcess.getClass()
191+
Field fPid = cProcessImpl.getDeclaredField("pid")
192+
if (!fPid.isAccessible()) {
193+
fPid.setAccessible(true)
194+
}
195+
minioPid = fPid.getInt(minioProcess)
196+
} catch (Exception e) {
197+
logger.error("failed to read pid from minio process", e)
198+
minioProcess.destroyForcibly()
199+
throw e
200+
}
201+
} else {
202+
minioPid = minioProcess.pid()
203+
}
204+
return minioPid
205+
}
206+
207+
private static Process setupMinio(String minioAddress, int minioPort, String minioDataDir, String accessKey, String secretKey,
208+
String minioBinDir, String minioFileName) {
209+
// we skip these tests on Windows so we do no need to worry about compatibility here
210+
final ProcessBuilder minio = new ProcessBuilder(
211+
"${minioBinDir}/${minioFileName}",
212+
"server",
213+
"--address",
214+
minioAddress + ":" + minioPort,
215+
minioDataDir)
216+
minio.environment().put('MINIO_ACCESS_KEY', accessKey)
217+
minio.environment().put('MINIO_SECRET_KEY', secretKey)
218+
return minio.start()
219+
}
220+
221+
private void addShutdownHook(Process minioProcess, int minioPort, int minioPid) {
222+
new BufferedReader(new InputStreamReader(minioProcess.inputStream)).withReader { br ->
223+
String line
224+
int httpPort = 0
225+
while ((line = br.readLine()) != null) {
226+
logger.info(line)
227+
if (line.matches('.*Endpoint.*:\\d+$')) {
228+
assert httpPort == 0
229+
final int index = line.lastIndexOf(":")
230+
assert index >= 0
231+
httpPort = Integer.parseInt(line.substring(index + 1))
232+
if (httpPort != minioPort) {
233+
throw new IllegalStateException("Port mismatch, expected ${minioPort} but was ${httpPort}")
234+
}
235+
236+
final File script = new File(project.buildDir, "minio/minio.killer.sh")
237+
script.setText(
238+
["function shutdown {",
239+
" kill ${minioPid}",
240+
"}",
241+
"trap shutdown EXIT",
242+
// will wait indefinitely for input, but we never pass input, and the pipe is only closed when the build dies
243+
"read line\n"].join('\n'), 'UTF-8')
244+
final ProcessBuilder killer = new ProcessBuilder("bash", script.absolutePath)
245+
killer.start()
246+
break
247+
}
248+
}
249+
250+
if (httpPort <= 0) {
251+
throw new IllegalStateException("httpPort must be > 0")
252+
}
253+
}
254+
}
255+
172256
if (useFixture && minioDistribution) {
173257
apply plugin: 'de.undercouch.download'
174258

@@ -201,72 +285,28 @@ if (useFixture && minioDistribution) {
201285
ext.minioPort = 0
202286

203287
doLast {
204-
// get free port
205-
ServerSocket serverSocket = new ServerSocket(0, 1, InetAddress.getByName(minioAddress))
206-
try {
207-
minioPort = serverSocket.localPort
208-
} finally {
209-
serverSocket.close()
210-
}
211-
if (minioPort == 0) {
212-
throw new GradleException("Could not find a free port for Minio")
213-
}
214-
215288
new File("${minioDataDir}/${s3PermanentBucket}").mkdirs()
216-
// we skip these tests on Windows so we do no need to worry about compatibility here
217-
final ProcessBuilder minio = new ProcessBuilder(
218-
"${minioBinDir}/${minioFileName}",
219-
"server",
220-
"--address",
221-
minioAddress + ":" + minioPort,
222-
minioDataDir)
223-
minio.environment().put('MINIO_ACCESS_KEY', s3PermanentAccessKey)
224-
minio.environment().put('MINIO_SECRET_KEY', s3PermanentSecretKey)
225-
final Process process = minio.start()
226-
if (JavaVersion.current() <= JavaVersion.VERSION_1_8) {
289+
290+
Exception accumulatedException = null
291+
for (int i = 0; i < 5; ++i) {
227292
try {
228-
Class<?> cProcessImpl = process.getClass()
229-
Field fPid = cProcessImpl.getDeclaredField("pid")
230-
if (!fPid.isAccessible()) {
231-
fPid.setAccessible(true)
232-
}
233-
minioPid = fPid.getInt(process)
293+
minioPort = freePort(minioAddress)
294+
final Process process =
295+
setupMinio(minioAddress, minioPort, minioDataDir, s3PermanentAccessKey, s3PermanentSecretKey, minioBinDir, minioFileName)
296+
minioPid = getMinioPid(process)
297+
addShutdownHook(process, minioPort, minioPid)
298+
break
234299
} catch (Exception e) {
235-
logger.error("failed to read pid from minio process", e)
236-
process.destroyForcibly()
237-
throw e
238-
}
239-
} else {
240-
minioPid = process.pid()
241-
}
242-
243-
new BufferedReader(new InputStreamReader(process.getInputStream())).withReader { br ->
244-
String line
245-
int httpPort = 0
246-
while ((line = br.readLine()) != null) {
247-
logger.info(line)
248-
if (line.matches('.*Endpoint.*:\\d+$')) {
249-
assert httpPort == 0
250-
final int index = line.lastIndexOf(":")
251-
assert index >= 0
252-
httpPort = Integer.parseInt(line.substring(index + 1))
253-
assert httpPort == minioPort : "Port mismatch, expected ${minioPort} but was ${httpPort}"
254-
255-
final File script = new File(project.buildDir, "minio/minio.killer.sh")
256-
script.setText(
257-
["function shutdown {",
258-
" kill ${minioPid}",
259-
"}",
260-
"trap shutdown EXIT",
261-
// will wait indefinitely for input, but we never pass input, and the pipe is only closed when the build dies
262-
"read line\n"].join('\n'), 'UTF-8')
263-
final ProcessBuilder killer = new ProcessBuilder("bash", script.absolutePath)
264-
killer.start()
265-
break
300+
logger.error("Exception while trying to start Minio {}", e)
301+
if (accumulatedException == null) {
302+
accumulated = e
303+
} else {
304+
accumulatedException.addSuppressed(e)
266305
}
267306
}
268-
269-
assert httpPort > 0
307+
}
308+
if (accumulatedException != null) {
309+
throw new GradleException("Failed to start Minio", accumulatedException)
270310
}
271311
}
272312
}

0 commit comments

Comments
 (0)