embulk-input-gcs 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +4 -4
- data/CHANGELOG.md +4 -0
- data/build.gradle +41 -12
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +2 -2
- data/gradlew +41 -33
- data/gradlew.bat +4 -10
- data/src/main/java/org/embulk/input/gcs/AuthUtils.java +108 -0
- data/src/main/java/org/embulk/input/gcs/FileList.java +12 -9
- data/src/main/java/org/embulk/input/gcs/GcsFileInput.java +46 -126
- data/src/main/java/org/embulk/input/gcs/GcsFileInputPlugin.java +3 -38
- data/src/main/java/org/embulk/input/gcs/PluginTask.java +1 -29
- data/src/main/java/org/embulk/input/gcs/RetryUtils.java +153 -0
- data/src/main/java/org/embulk/input/gcs/SingleFileProvider.java +30 -63
- data/src/test/java/org/embulk/input/gcs/TestAuthUtils.java +153 -0
- data/src/test/java/org/embulk/input/gcs/TestGcsFileInputPlugin.java +42 -94
- data/src/test/java/org/embulk/input/gcs/TestInputStreamReopener.java +145 -0
- data/src/test/java/org/embulk/input/gcs/TestRetryUtils.java +164 -0
- data/src/test/resources/secrets.tar.enc +0 -0
- metadata +53 -28
- data/secretkeys.tar +0 -0
- data/secretkeys.tar.enc +0 -0
- data/src/main/java/org/embulk/input/gcs/GcsAuthentication.java +0 -206
- data/src/test/java/org/embulk/input/gcs/TestGcsAuthentication.java +0 -185
- data/src/test/resources/secretkeys.tar.enc +0 -0
@@ -1,34 +1,26 @@
|
|
1
1
|
package org.embulk.input.gcs;
|
2
2
|
|
3
|
-
import com.google.api.
|
4
|
-
import com.google.
|
5
|
-
import com.google.
|
6
|
-
import com.google.
|
7
|
-
import com.google.
|
3
|
+
import com.google.api.gax.paging.Page;
|
4
|
+
import com.google.cloud.storage.Blob;
|
5
|
+
import com.google.cloud.storage.Storage;
|
6
|
+
import com.google.cloud.storage.StorageException;
|
7
|
+
import com.google.common.annotations.VisibleForTesting;
|
8
8
|
import com.google.common.base.Charsets;
|
9
9
|
import com.google.common.io.BaseEncoding;
|
10
10
|
import org.embulk.config.ConfigException;
|
11
11
|
import org.embulk.config.TaskReport;
|
12
12
|
import org.embulk.spi.Exec;
|
13
13
|
import org.embulk.spi.TransactionalFileInput;
|
14
|
-
import org.embulk.spi.unit.LocalFile;
|
15
14
|
import org.embulk.spi.util.InputStreamFileInput;
|
16
15
|
import org.slf4j.Logger;
|
17
16
|
|
18
|
-
import java.io.IOException;
|
19
|
-
import java.math.BigInteger;
|
20
|
-
import java.security.GeneralSecurityException;
|
21
|
-
import java.util.List;
|
22
|
-
import java.util.Optional;
|
23
|
-
import java.util.function.Function;
|
24
|
-
|
25
17
|
public class GcsFileInput
|
26
18
|
extends InputStreamFileInput
|
27
19
|
implements TransactionalFileInput
|
28
20
|
{
|
29
|
-
private static final Logger
|
21
|
+
private static final Logger LOG = Exec.getLogger(org.embulk.input.gcs.GcsFileInput.class);
|
30
22
|
|
31
|
-
|
23
|
+
GcsFileInput(PluginTask task, int taskIndex)
|
32
24
|
{
|
33
25
|
super(task.getBufferAllocator(), new SingleFileProvider(task, taskIndex));
|
34
26
|
}
|
@@ -47,149 +39,77 @@ public class GcsFileInput
|
|
47
39
|
{
|
48
40
|
}
|
49
41
|
|
50
|
-
public static GcsAuthentication newGcsAuth(PluginTask task)
|
51
|
-
{
|
52
|
-
try {
|
53
|
-
return new GcsAuthentication(
|
54
|
-
task.getAuthMethod().getString(),
|
55
|
-
task.getServiceAccountEmail(),
|
56
|
-
task.getP12Keyfile().map(localFileToPathString()),
|
57
|
-
task.getJsonKeyfile().map(localFileToPathString()),
|
58
|
-
task.getApplicationName()
|
59
|
-
);
|
60
|
-
}
|
61
|
-
catch (GeneralSecurityException | IOException ex) {
|
62
|
-
throw new ConfigException(ex);
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
protected static Storage newGcsClient(final PluginTask task, final GcsAuthentication auth)
|
67
|
-
{
|
68
|
-
Storage client = null;
|
69
|
-
try {
|
70
|
-
client = auth.getGcsClient(task.getBucket(), task.getMaxConnectionRetry());
|
71
|
-
}
|
72
|
-
catch (IOException ex) {
|
73
|
-
throw new ConfigException(ex);
|
74
|
-
}
|
75
|
-
|
76
|
-
return client;
|
77
|
-
}
|
78
|
-
|
79
|
-
private static Function<LocalFile, String> localFileToPathString()
|
80
|
-
{
|
81
|
-
return new Function<LocalFile, String>()
|
82
|
-
{
|
83
|
-
public String apply(LocalFile file)
|
84
|
-
{
|
85
|
-
return file.getPath().toString();
|
86
|
-
}
|
87
|
-
};
|
88
|
-
}
|
89
|
-
|
90
|
-
public static FileList listFiles(PluginTask task, Storage client)
|
91
|
-
{
|
92
|
-
String bucket = task.getBucket();
|
93
|
-
|
94
|
-
FileList.Builder builder = new FileList.Builder(task);
|
95
|
-
listGcsFilesByPrefix(builder, client, bucket, task.getPathPrefix().get(), task.getLastPath());
|
96
|
-
return builder.build();
|
97
|
-
}
|
98
|
-
|
99
42
|
/**
|
100
43
|
* Lists GCS filenames filtered by prefix.
|
101
44
|
*
|
102
45
|
* The resulting list does not include the file that's size == 0.
|
103
46
|
*/
|
104
|
-
|
105
|
-
String prefix, Optional<String> lastPath)
|
47
|
+
static FileList listFiles(PluginTask task)
|
106
48
|
{
|
107
|
-
|
108
|
-
|
109
|
-
// @see https://cloud.google.com/storage/docs/json_api/v1/objects#resource
|
110
|
-
if (log.isDebugEnabled()) {
|
111
|
-
try {
|
112
|
-
Storage.Buckets.Get getBucket = client.buckets().get(bucket);
|
113
|
-
getBucket.setProjection("full");
|
114
|
-
Bucket bk = getBucket.execute();
|
49
|
+
Storage client = AuthUtils.newClient(task);
|
50
|
+
String bucket = task.getBucket();
|
115
51
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
log.debug("bucket owner: " + bk.getOwner());
|
120
|
-
}
|
121
|
-
catch (IOException e) {
|
122
|
-
log.warn("Could not access to bucket:" + bucket);
|
123
|
-
log.warn(e.getMessage());
|
124
|
-
}
|
52
|
+
// @see https://cloud.google.com/storage/docs/json_api/v1/buckets/get
|
53
|
+
if (LOG.isDebugEnabled()) {
|
54
|
+
printBucketInfo(client, bucket);
|
125
55
|
}
|
126
56
|
|
57
|
+
String prefix = task.getPathPrefix().orElse("");
|
58
|
+
String lastKey = task.getLastPath().isPresent() ? base64Encode(task.getLastPath().get()) : "";
|
59
|
+
FileList.Builder builder = new FileList.Builder(task);
|
60
|
+
|
127
61
|
try {
|
128
62
|
// @see https://cloud.google.com/storage/docs/json_api/v1/objects/list
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
Objects objects = listObjects.execute();
|
134
|
-
List<StorageObject> items = objects.getItems();
|
135
|
-
if (items == null) {
|
136
|
-
log.info(String.format("No file was found in bucket:%s prefix:%s", bucket, prefix));
|
137
|
-
break;
|
138
|
-
}
|
139
|
-
for (StorageObject o : items) {
|
140
|
-
if (o.getSize().compareTo(BigInteger.ZERO) > 0) {
|
141
|
-
builder.add(o.getName(), o.getSize().longValue());
|
142
|
-
}
|
143
|
-
log.debug("filename: " + o.getName());
|
144
|
-
log.debug("updated: " + o.getUpdated());
|
63
|
+
Page<Blob> blobs = client.list(bucket, Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.pageToken(lastKey));
|
64
|
+
for (Blob blob : blobs.iterateAll()) {
|
65
|
+
if (blob.getSize() > 0) {
|
66
|
+
builder.add(blob.getName(), blob.getSize());
|
145
67
|
}
|
146
|
-
|
147
|
-
|
148
|
-
}
|
68
|
+
LOG.debug("filename: {}", blob.getName());
|
69
|
+
LOG.debug("updated: {}", blob.getUpdateTime());
|
70
|
+
}
|
149
71
|
}
|
150
|
-
catch (
|
151
|
-
if ((e instanceof
|
72
|
+
catch (RuntimeException e) {
|
73
|
+
if ((e instanceof StorageException) && ((StorageException) e).getCode() == 400) {
|
152
74
|
throw new ConfigException(String.format("Files listing failed: bucket:%s, prefix:%s, last_path:%s", bucket, prefix, lastKey), e);
|
153
75
|
}
|
154
76
|
|
155
|
-
|
156
|
-
|
77
|
+
LOG.warn(String.format("Could not get file list from bucket:%s", bucket));
|
78
|
+
LOG.warn(e.getMessage());
|
157
79
|
}
|
80
|
+
return builder.build();
|
158
81
|
}
|
159
82
|
|
160
83
|
// String nextToken = base64Encode(0x0a + 0x01~0x27 + filePath);
|
161
|
-
|
84
|
+
@VisibleForTesting
|
85
|
+
static String base64Encode(String path)
|
162
86
|
{
|
163
87
|
byte[] encoding;
|
164
88
|
byte[] utf8 = path.getBytes(Charsets.UTF_8);
|
165
|
-
|
89
|
+
LOG.debug("path string: {} ,path length:{} \" + ", path, utf8.length);
|
166
90
|
|
167
91
|
encoding = new byte[utf8.length + 2];
|
168
92
|
encoding[0] = 0x0a;
|
169
|
-
encoding[1] =
|
93
|
+
encoding[1] = Byte.valueOf(String.valueOf(path.length()));
|
170
94
|
System.arraycopy(utf8, 0, encoding, 2, utf8.length);
|
171
95
|
|
172
96
|
String s = BaseEncoding.base64().encode(encoding);
|
173
|
-
|
97
|
+
LOG.debug("last_path(base64 encoded): {}", s);
|
174
98
|
return s;
|
175
99
|
}
|
176
100
|
|
177
|
-
|
101
|
+
private static void printBucketInfo(Storage client, String bucket)
|
178
102
|
{
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
{
|
187
|
-
|
188
|
-
}
|
189
|
-
|
190
|
-
public String getString()
|
191
|
-
{
|
192
|
-
return string;
|
193
|
-
}
|
103
|
+
// get Bucket
|
104
|
+
Storage.BucketGetOption fields = Storage.BucketGetOption.fields(
|
105
|
+
Storage.BucketField.LOCATION,
|
106
|
+
Storage.BucketField.TIME_CREATED,
|
107
|
+
Storage.BucketField.OWNER
|
108
|
+
);
|
109
|
+
com.google.cloud.storage.Bucket bk = client.get(bucket, fields);
|
110
|
+
LOG.debug("bucket name: {}", bk.getName());
|
111
|
+
LOG.debug("bucket location: {}", bk.getLocation());
|
112
|
+
LOG.debug("bucket timeCreated: {}", bk.getCreateTime());
|
113
|
+
LOG.debug("bucket owner: {}", bk.getOwner());
|
194
114
|
}
|
195
115
|
}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
package org.embulk.input.gcs;
|
2
2
|
|
3
|
-
import com.google.api.services.storage.Storage;
|
4
3
|
import com.google.common.base.Throwables;
|
5
4
|
import org.embulk.config.ConfigDiff;
|
6
5
|
import org.embulk.config.ConfigException;
|
@@ -11,19 +10,14 @@ import org.embulk.spi.Exec;
|
|
11
10
|
import org.embulk.spi.FileInputPlugin;
|
12
11
|
import org.embulk.spi.TransactionalFileInput;
|
13
12
|
import org.embulk.spi.unit.LocalFile;
|
14
|
-
import org.slf4j.Logger;
|
15
13
|
|
16
14
|
import java.io.IOException;
|
17
|
-
import java.security.GeneralSecurityException;
|
18
15
|
import java.util.List;
|
19
16
|
import java.util.Optional;
|
20
|
-
import java.util.function.Function;
|
21
17
|
|
22
18
|
public class GcsFileInputPlugin
|
23
19
|
implements FileInputPlugin
|
24
20
|
{
|
25
|
-
private static final Logger log = Exec.getLogger(GcsFileInputPlugin.class);
|
26
|
-
|
27
21
|
@Override
|
28
22
|
public ConfigDiff transaction(ConfigSource config,
|
29
23
|
FileInputPlugin.Control control)
|
@@ -42,12 +36,12 @@ public class GcsFileInputPlugin
|
|
42
36
|
}
|
43
37
|
}
|
44
38
|
|
45
|
-
if (task.getAuthMethod()
|
39
|
+
if (AuthUtils.AuthMethod.json_key.equals(task.getAuthMethod())) {
|
46
40
|
if (!task.getJsonKeyfile().isPresent()) {
|
47
41
|
throw new ConfigException("If auth_method is json_key, you have to set json_keyfile");
|
48
42
|
}
|
49
43
|
}
|
50
|
-
else if (task.getAuthMethod()
|
44
|
+
else if (AuthUtils.AuthMethod.private_key.equals(task.getAuthMethod())) {
|
51
45
|
if (!task.getP12Keyfile().isPresent() || !task.getServiceAccountEmail().isPresent()) {
|
52
46
|
throw new ConfigException("If auth_method is private_key, you have to set both service_account_email and p12_keyfile");
|
53
47
|
}
|
@@ -60,11 +54,9 @@ public class GcsFileInputPlugin
|
|
60
54
|
}
|
61
55
|
}
|
62
56
|
|
63
|
-
Storage client = GcsFileInput.newGcsClient(task, newGcsAuth(task));
|
64
|
-
|
65
57
|
// list files recursively if path_prefix is specified
|
66
58
|
if (task.getPathPrefix().isPresent()) {
|
67
|
-
task.setFiles(GcsFileInput.listFiles(task
|
59
|
+
task.setFiles(GcsFileInput.listFiles(task));
|
68
60
|
}
|
69
61
|
else {
|
70
62
|
if (task.getPathFiles().isEmpty()) {
|
@@ -80,22 +72,6 @@ public class GcsFileInputPlugin
|
|
80
72
|
return resume(task.dump(), task.getFiles().getTaskCount(), control);
|
81
73
|
}
|
82
74
|
|
83
|
-
private GcsAuthentication newGcsAuth(PluginTask task)
|
84
|
-
{
|
85
|
-
try {
|
86
|
-
return new GcsAuthentication(
|
87
|
-
task.getAuthMethod().getString(),
|
88
|
-
task.getServiceAccountEmail(),
|
89
|
-
task.getP12Keyfile().map(localFileToPathString()),
|
90
|
-
task.getJsonKeyfile().map(localFileToPathString()),
|
91
|
-
task.getApplicationName()
|
92
|
-
);
|
93
|
-
}
|
94
|
-
catch (GeneralSecurityException | IOException ex) {
|
95
|
-
throw new ConfigException(ex);
|
96
|
-
}
|
97
|
-
}
|
98
|
-
|
99
75
|
@Override
|
100
76
|
public ConfigDiff resume(TaskSource taskSource,
|
101
77
|
int taskCount,
|
@@ -121,17 +97,6 @@ public class GcsFileInputPlugin
|
|
121
97
|
{
|
122
98
|
}
|
123
99
|
|
124
|
-
private Function<LocalFile, String> localFileToPathString()
|
125
|
-
{
|
126
|
-
return new Function<LocalFile, String>()
|
127
|
-
{
|
128
|
-
public String apply(LocalFile file)
|
129
|
-
{
|
130
|
-
return file.getPath().toString();
|
131
|
-
}
|
132
|
-
};
|
133
|
-
}
|
134
|
-
|
135
100
|
@Override
|
136
101
|
public TransactionalFileInput open(TaskSource taskSource, int taskIndex)
|
137
102
|
{
|
@@ -5,13 +5,12 @@ import org.embulk.config.ConfigDefault;
|
|
5
5
|
import org.embulk.config.ConfigInject;
|
6
6
|
import org.embulk.config.Task;
|
7
7
|
import org.embulk.spi.BufferAllocator;
|
8
|
-
import org.embulk.spi.unit.LocalFile;
|
9
8
|
|
10
9
|
import java.util.List;
|
11
10
|
import java.util.Optional;
|
12
11
|
|
13
12
|
public interface PluginTask
|
14
|
-
extends Task, FileList.Task
|
13
|
+
extends Task, AuthUtils.Task, FileList.Task, RetryUtils.Task
|
15
14
|
{
|
16
15
|
@Config("bucket")
|
17
16
|
String getBucket();
|
@@ -28,44 +27,17 @@ public interface PluginTask
|
|
28
27
|
@ConfigDefault("true")
|
29
28
|
boolean getIncremental();
|
30
29
|
|
31
|
-
@Config("auth_method")
|
32
|
-
@ConfigDefault("\"private_key\"")
|
33
|
-
GcsFileInput.AuthMethod getAuthMethod();
|
34
|
-
|
35
|
-
@Config("service_account_email")
|
36
|
-
@ConfigDefault("null")
|
37
|
-
Optional<String> getServiceAccountEmail();
|
38
|
-
|
39
30
|
@Config("application_name")
|
40
31
|
@ConfigDefault("\"Embulk GCS input plugin\"")
|
41
32
|
String getApplicationName();
|
42
33
|
|
43
|
-
// kept for backward compatibility
|
44
|
-
@Config("p12_keyfile_fullpath")
|
45
|
-
@ConfigDefault("null")
|
46
|
-
Optional<String> getP12KeyfileFullpath();
|
47
|
-
|
48
|
-
@Config("p12_keyfile")
|
49
|
-
@ConfigDefault("null")
|
50
|
-
Optional<LocalFile> getP12Keyfile();
|
51
|
-
void setP12Keyfile(Optional<LocalFile> p12Keyfile);
|
52
|
-
|
53
|
-
@Config("json_keyfile")
|
54
|
-
@ConfigDefault("null")
|
55
|
-
Optional<LocalFile> getJsonKeyfile();
|
56
|
-
|
57
34
|
@Config("paths")
|
58
35
|
@ConfigDefault("[]")
|
59
36
|
List<String> getPathFiles();
|
60
|
-
void setPathFiles(List<String> files);
|
61
37
|
|
62
38
|
FileList getFiles();
|
63
39
|
void setFiles(FileList files);
|
64
40
|
|
65
|
-
@Config("max_connection_retry")
|
66
|
-
@ConfigDefault("10") // 10 times retry to connect GCS server if failed.
|
67
|
-
int getMaxConnectionRetry();
|
68
|
-
|
69
41
|
@ConfigInject
|
70
42
|
BufferAllocator getBufferAllocator();
|
71
43
|
}
|
@@ -0,0 +1,153 @@
|
|
1
|
+
package org.embulk.input.gcs;
|
2
|
+
|
3
|
+
import com.google.api.client.auth.oauth2.TokenErrorResponse;
|
4
|
+
import com.google.api.client.auth.oauth2.TokenResponseException;
|
5
|
+
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
6
|
+
import com.google.cloud.storage.Blob;
|
7
|
+
import com.google.cloud.storage.Storage;
|
8
|
+
import org.embulk.config.Config;
|
9
|
+
import org.embulk.config.ConfigDefault;
|
10
|
+
import org.embulk.spi.Exec;
|
11
|
+
import org.embulk.spi.util.RetryExecutor;
|
12
|
+
import org.slf4j.Logger;
|
13
|
+
|
14
|
+
import java.util.Optional;
|
15
|
+
import java.util.function.Predicate;
|
16
|
+
|
17
|
+
class RetryUtils
|
18
|
+
{
|
19
|
+
interface Task extends org.embulk.config.Task
|
20
|
+
{
|
21
|
+
@Config("max_connection_retry")
|
22
|
+
@ConfigDefault("10") // 10 times retry to connect GCS server if failed.
|
23
|
+
int getMaxConnectionRetry();
|
24
|
+
|
25
|
+
@Config("initial_retry_interval_millis")
|
26
|
+
@ConfigDefault("1000")
|
27
|
+
int getInitialRetryIntervalMillis();
|
28
|
+
|
29
|
+
@Config("maximum_retry_interval_millis")
|
30
|
+
@ConfigDefault("300000")
|
31
|
+
int getMaximumRetryIntervalMillis();
|
32
|
+
}
|
33
|
+
|
34
|
+
private RetryUtils()
|
35
|
+
{
|
36
|
+
}
|
37
|
+
|
38
|
+
private static final Logger LOG = Exec.getLogger(RetryUtils.class);
|
39
|
+
|
40
|
+
/**
|
41
|
+
* A utility predicate to detect status code 4xx of `GoogleJsonResponseException`
|
42
|
+
*/
|
43
|
+
private static final Predicate<GoogleJsonResponseException> API_ERROR_NOT_RETRY_4XX = e -> {
|
44
|
+
if (e.getDetails() == null && e.getContent() != null) {
|
45
|
+
LOG.warn("Invalid response was returned : {}", e.getContent());
|
46
|
+
return true;
|
47
|
+
}
|
48
|
+
int statusCode = e.getDetails().getCode();
|
49
|
+
return statusCode / 100 != 4;
|
50
|
+
};
|
51
|
+
|
52
|
+
/**
|
53
|
+
* A utility predicate to detect status code 4xx of `TokenResponseException`
|
54
|
+
* But will retry 400 "Invalid JWS..."
|
55
|
+
*/
|
56
|
+
private static final Predicate<TokenResponseException> TOKEN_ERROR_NOT_RETRY_4XX = e -> {
|
57
|
+
Optional<String> errDesc = Optional.ofNullable(e.getDetails()).map(TokenErrorResponse::getErrorDescription);
|
58
|
+
if (errDesc.isPresent()) {
|
59
|
+
// Retry: 400 BadRequest "Invalid JWT..."
|
60
|
+
// Caused by: com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
|
61
|
+
// {
|
62
|
+
// "error" : "invalid_grant",
|
63
|
+
// "error_description" : "Invalid JWT: No valid verifier found for issuer."
|
64
|
+
// }
|
65
|
+
if (errDesc.get().contains("Invalid JWT")) {
|
66
|
+
LOG.warn("Invalid response was returned : {}", errDesc.get());
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return e.getStatusCode() / 100 != 4;
|
71
|
+
};
|
72
|
+
|
73
|
+
/**
|
74
|
+
* A default (abstract) retryable impl, which makes use of above 2 predicates
|
75
|
+
* With default behaviors onRetry, etc.
|
76
|
+
*
|
77
|
+
* @param <T>
|
78
|
+
*/
|
79
|
+
public abstract static class DefaultRetryable<T> implements RetryExecutor.Retryable<T>
|
80
|
+
{
|
81
|
+
@Override
|
82
|
+
public boolean isRetryableException(Exception exception)
|
83
|
+
{
|
84
|
+
if (exception instanceof GoogleJsonResponseException) {
|
85
|
+
return API_ERROR_NOT_RETRY_4XX.test((GoogleJsonResponseException) exception);
|
86
|
+
}
|
87
|
+
else if (exception instanceof TokenResponseException) {
|
88
|
+
return TOKEN_ERROR_NOT_RETRY_4XX.test((TokenResponseException) exception);
|
89
|
+
}
|
90
|
+
return true;
|
91
|
+
}
|
92
|
+
|
93
|
+
@Override
|
94
|
+
public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
|
95
|
+
{
|
96
|
+
String message = String.format("GCS GET request failed. Retrying %d/%d after %d seconds. Message: %s: %s",
|
97
|
+
retryCount, retryLimit, retryWait / 1000, exception.getClass(), exception.getMessage());
|
98
|
+
if (retryCount % 3 == 0) {
|
99
|
+
LOG.warn(message, exception);
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
LOG.warn(message);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
@Override
|
107
|
+
public void onGiveup(Exception firstException, Exception lastException)
|
108
|
+
{
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Return Blob GET op that is ready for {@code withRetry}
|
114
|
+
*
|
115
|
+
* @param client
|
116
|
+
* @param bucket
|
117
|
+
* @param key
|
118
|
+
* @return
|
119
|
+
*/
|
120
|
+
static DefaultRetryable<Blob> get(Storage client, String bucket, String key)
|
121
|
+
{
|
122
|
+
return new DefaultRetryable<Blob>()
|
123
|
+
{
|
124
|
+
@Override
|
125
|
+
public Blob call()
|
126
|
+
{
|
127
|
+
return client.get(bucket, key);
|
128
|
+
}
|
129
|
+
};
|
130
|
+
}
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Utility method
|
134
|
+
*
|
135
|
+
* @param task
|
136
|
+
* @param op
|
137
|
+
* @param <T>
|
138
|
+
* @return
|
139
|
+
*/
|
140
|
+
static <T> T withRetry(Task task, RetryExecutor.Retryable<T> op)
|
141
|
+
{
|
142
|
+
try {
|
143
|
+
return RetryExecutor.retryExecutor()
|
144
|
+
.withInitialRetryWait(task.getInitialRetryIntervalMillis())
|
145
|
+
.withMaxRetryWait(task.getMaximumRetryIntervalMillis())
|
146
|
+
.withRetryLimit(task.getMaxConnectionRetry())
|
147
|
+
.runInterruptible(op);
|
148
|
+
}
|
149
|
+
catch (RetryExecutor.RetryGiveupException | InterruptedException e) {
|
150
|
+
throw new RuntimeException(e);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|