Skip to content

Commit 2c284dd

Browse files
Add retries to downloadEmulator() to mitigate transient network issues (googleapis#3719)
* Add retries to downloadEmulator() to mitigate transient network issues
1 parent 6955469 commit 2c284dd

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

google-cloud-clients/google-cloud-core/src/main/java/com/google/cloud/testing/BaseEmulatorHelper.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
package com.google.cloud.testing;
1818

19+
import com.google.api.core.CurrentMillisClock;
1920
import com.google.api.core.InternalApi;
21+
import com.google.cloud.ExceptionHandler;
22+
import com.google.cloud.RetryHelper;
2023
import com.google.cloud.ServiceOptions;
2124
import com.google.common.io.CharStreams;
2225
import com.google.common.util.concurrent.SettableFuture;
@@ -46,6 +49,7 @@
4649
import java.util.List;
4750
import java.util.Locale;
4851
import java.util.Map;
52+
import java.util.concurrent.Callable;
4953
import java.util.concurrent.ExecutionException;
5054
import java.util.concurrent.TimeUnit;
5155
import java.util.concurrent.TimeoutException;
@@ -366,7 +370,15 @@ public boolean isAvailable() {
366370

367371
@Override
368372
public void start() throws IOException {
369-
Path emulatorPath = downloadEmulator();
373+
ExceptionHandler retryOnAnythingExceptionHandler = ExceptionHandler.newBuilder().retryOn(Exception.class).build();
374+
375+
Path emulatorPath = RetryHelper.runWithRetries(new Callable<Path>() {
376+
@Override
377+
public Path call() throws IOException {
378+
return downloadEmulator();
379+
}
380+
}, ServiceOptions.getDefaultRetrySettings(), retryOnAnythingExceptionHandler,
381+
CurrentMillisClock.getDefaultClock());
370382
process = CommandWrapper.create()
371383
.setCommand(commandText)
372384
.setDirectory(emulatorPath)

google-cloud-clients/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@
2020
import com.google.cloud.ServiceOptions;
2121
import com.google.cloud.testing.BaseEmulatorHelper.EmulatorRunner;
2222
import com.google.common.collect.ImmutableList;
23+
2324
import java.io.ByteArrayInputStream;
25+
import java.io.EOFException;
26+
import java.io.File;
2427
import java.io.IOException;
2528
import java.io.InputStream;
29+
import java.net.URL;
30+
import java.net.URLConnection;
31+
import java.net.URLStreamHandler;
2632
import java.util.List;
2733
import java.util.concurrent.TimeoutException;
2834
import java.util.logging.Logger;
@@ -96,6 +102,41 @@ public void testEmulatorHelper() throws IOException, InterruptedException, Timeo
96102
EasyMock.verify();
97103
}
98104

105+
@Test
106+
public void testEmulatorHelperDownloadWithRetries() throws IOException, InterruptedException, TimeoutException {
107+
String mockExternalForm = "mockExternalForm";
108+
String mockInputStream = "mockInputStream";
109+
String mockProtocol = "mockProtocol";
110+
String mockFile = "mockFile";
111+
String mockCommandText = "mockCommandText";
112+
113+
MockURLStreamHandler mockURLStreamHandler = EasyMock.createMock(MockURLStreamHandler.class);
114+
URLConnection mockURLConnection = EasyMock.mock(URLConnection.class);
115+
116+
EasyMock.expect(mockURLStreamHandler.toExternalForm(EasyMock.anyObject(URL.class)))
117+
.andReturn(mockExternalForm).anyTimes();
118+
EasyMock.expect(mockURLConnection.getInputStream())
119+
.andReturn(new ByteArrayInputStream(mockInputStream.getBytes())).anyTimes();
120+
EasyMock.expect(mockURLStreamHandler.openConnection(EasyMock.anyObject(URL.class)))
121+
.andThrow(new EOFException()).times(1);
122+
EasyMock.expect(mockURLStreamHandler.openConnection(EasyMock.anyObject(URL.class)))
123+
.andReturn(mockURLConnection).times(1);
124+
EasyMock.replay(mockURLStreamHandler, mockURLConnection);
125+
126+
URL url = new URL(mockProtocol, null, 0, mockFile, mockURLStreamHandler);
127+
BaseEmulatorHelper.DownloadableEmulatorRunner runner =
128+
new BaseEmulatorHelper.DownloadableEmulatorRunner(ImmutableList.of(mockCommandText), url, null);
129+
130+
File cachedFile = new File(System.getProperty("java.io.tmpdir"), mockExternalForm);
131+
cachedFile.delete(); //Clear the cached version so we're always testing the download
132+
133+
runner.start();
134+
135+
EasyMock.verify();
136+
137+
cachedFile.delete(); //Cleanup
138+
}
139+
99140
@Test
100141
public void testEmulatorHelperMultipleRunners() throws IOException, InterruptedException, TimeoutException {
101142
Process process = EasyMock.createStrictMock(Process.class);
@@ -117,4 +158,19 @@ public void testEmulatorHelperMultipleRunners() throws IOException, InterruptedE
117158
helper.stop(Duration.ofMinutes(1));
118159
EasyMock.verify();
119160
}
161+
162+
/**
163+
* URLStreamHandler has a protected method which needs to be mocked, so we need our own implementation in this package
164+
*/
165+
private class MockURLStreamHandler extends URLStreamHandler {
166+
@Override
167+
protected URLConnection openConnection(URL u) throws IOException {
168+
return null;
169+
}
170+
171+
@Override
172+
protected String toExternalForm(URL u) {
173+
return null;
174+
}
175+
}
120176
}

0 commit comments

Comments
 (0)