embulk-input-gcs 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/secretkeys.tar
DELETED
Binary file
|
data/secretkeys.tar.enc
DELETED
Binary file
|
@@ -1,206 +0,0 @@
|
|
1
|
-
package org.embulk.input.gcs;
|
2
|
-
|
3
|
-
import com.google.api.client.auth.oauth2.TokenResponseException;
|
4
|
-
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
5
|
-
import com.google.api.client.googleapis.compute.ComputeCredential;
|
6
|
-
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
|
7
|
-
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
8
|
-
import com.google.api.client.http.HttpRequestInitializer;
|
9
|
-
import com.google.api.client.http.HttpTransport;
|
10
|
-
import com.google.api.client.json.JsonFactory;
|
11
|
-
import com.google.api.client.json.jackson2.JacksonFactory;
|
12
|
-
import com.google.api.services.storage.Storage;
|
13
|
-
import com.google.api.services.storage.StorageScopes;
|
14
|
-
import com.google.common.base.Throwables;
|
15
|
-
import com.google.common.collect.ImmutableList;
|
16
|
-
import org.embulk.config.ConfigException;
|
17
|
-
import org.embulk.spi.Exec;
|
18
|
-
import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
|
19
|
-
import org.embulk.spi.util.RetryExecutor.Retryable;
|
20
|
-
import org.slf4j.Logger;
|
21
|
-
import static org.embulk.spi.util.RetryExecutor.retryExecutor;
|
22
|
-
|
23
|
-
import java.io.File;
|
24
|
-
import java.io.FileInputStream;
|
25
|
-
|
26
|
-
import java.io.IOException;
|
27
|
-
import java.io.InterruptedIOException;
|
28
|
-
import java.security.GeneralSecurityException;
|
29
|
-
import java.util.Collections;
|
30
|
-
import java.util.Optional;
|
31
|
-
|
32
|
-
public class GcsAuthentication
|
33
|
-
{
|
34
|
-
private final Logger log = Exec.getLogger(GcsAuthentication.class);
|
35
|
-
private final Optional<String> serviceAccountEmail;
|
36
|
-
private final Optional<String> p12KeyFilePath;
|
37
|
-
private final Optional<String> jsonKeyFilePath;
|
38
|
-
private final String applicationName;
|
39
|
-
private final HttpTransport httpTransport;
|
40
|
-
private final JsonFactory jsonFactory;
|
41
|
-
private final HttpRequestInitializer credentials;
|
42
|
-
|
43
|
-
public GcsAuthentication(String authMethod, Optional<String> serviceAccountEmail,
|
44
|
-
Optional<String> p12KeyFilePath, Optional<String> jsonKeyFilePath, String applicationName)
|
45
|
-
throws IOException, GeneralSecurityException
|
46
|
-
{
|
47
|
-
this.serviceAccountEmail = serviceAccountEmail;
|
48
|
-
this.p12KeyFilePath = p12KeyFilePath;
|
49
|
-
this.jsonKeyFilePath = jsonKeyFilePath;
|
50
|
-
this.applicationName = applicationName;
|
51
|
-
|
52
|
-
this.httpTransport = GoogleNetHttpTransport.newTrustedTransport();
|
53
|
-
this.jsonFactory = new JacksonFactory();
|
54
|
-
|
55
|
-
if (authMethod.equals("compute_engine")) {
|
56
|
-
this.credentials = getComputeCredential();
|
57
|
-
}
|
58
|
-
else if (authMethod.toLowerCase().equals("json_key")) {
|
59
|
-
this.credentials = getServiceAccountCredentialFromJsonFile();
|
60
|
-
}
|
61
|
-
else {
|
62
|
-
this.credentials = getServiceAccountCredential();
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
/**
|
67
|
-
* @see https://developers.google.com/accounts/docs/OAuth2ServiceAccount#authorizingrequests
|
68
|
-
*/
|
69
|
-
private GoogleCredential getServiceAccountCredential() throws IOException, GeneralSecurityException
|
70
|
-
{
|
71
|
-
// @see https://cloud.google.com/compute/docs/api/how-tos/authorization
|
72
|
-
// @see https://developers.google.com/resources/api-libraries/documentation/storage/v1/java/latest/com/google/api/services/storage/STORAGE_SCOPE.html
|
73
|
-
// @see https://developers.google.com/resources/api-libraries/documentation/bigquery/v2/java/latest/com/google/api/services/bigquery/BigqueryScopes.html
|
74
|
-
return new GoogleCredential.Builder()
|
75
|
-
.setTransport(httpTransport)
|
76
|
-
.setJsonFactory(jsonFactory)
|
77
|
-
.setServiceAccountId(serviceAccountEmail.orElseGet(null))
|
78
|
-
.setServiceAccountScopes(
|
79
|
-
ImmutableList.of(
|
80
|
-
StorageScopes.DEVSTORAGE_READ_ONLY
|
81
|
-
)
|
82
|
-
)
|
83
|
-
.setServiceAccountPrivateKeyFromP12File(new File(p12KeyFilePath.get()))
|
84
|
-
.build();
|
85
|
-
}
|
86
|
-
|
87
|
-
private GoogleCredential getServiceAccountCredentialFromJsonFile() throws IOException
|
88
|
-
{
|
89
|
-
FileInputStream stream = new FileInputStream(jsonKeyFilePath.get());
|
90
|
-
|
91
|
-
return GoogleCredential.fromStream(stream, httpTransport, jsonFactory)
|
92
|
-
.createScoped(Collections.singleton(StorageScopes.DEVSTORAGE_READ_ONLY));
|
93
|
-
}
|
94
|
-
|
95
|
-
/**
|
96
|
-
* @see http://developers.guge.io/accounts/docs/OAuth2ServiceAccount#creatinganaccount
|
97
|
-
* @see https://developers.google.com/accounts/docs/OAuth2
|
98
|
-
*/
|
99
|
-
private ComputeCredential getComputeCredential() throws IOException
|
100
|
-
{
|
101
|
-
ComputeCredential credential = new ComputeCredential.Builder(httpTransport, jsonFactory)
|
102
|
-
.build();
|
103
|
-
credential.refreshToken();
|
104
|
-
|
105
|
-
return credential;
|
106
|
-
}
|
107
|
-
|
108
|
-
public Storage getGcsClient(final String bucket, int maxConnectionRetry) throws ConfigException, IOException
|
109
|
-
{
|
110
|
-
try {
|
111
|
-
return retryExecutor()
|
112
|
-
.withRetryLimit(maxConnectionRetry)
|
113
|
-
.withInitialRetryWait(500)
|
114
|
-
.withMaxRetryWait(30 * 1000)
|
115
|
-
.runInterruptible(new Retryable<Storage>() {
|
116
|
-
@Override
|
117
|
-
public Storage call() throws IOException, RetryGiveupException
|
118
|
-
{
|
119
|
-
Storage client = new Storage.Builder(httpTransport, jsonFactory, credentials)
|
120
|
-
.setApplicationName(applicationName)
|
121
|
-
.build();
|
122
|
-
|
123
|
-
// For throw ConfigException when authentication is fail.
|
124
|
-
long maxResults = 1;
|
125
|
-
client.objects().list(bucket).setMaxResults(maxResults).execute();
|
126
|
-
|
127
|
-
return client;
|
128
|
-
}
|
129
|
-
|
130
|
-
@Override
|
131
|
-
public boolean isRetryableException(Exception exception)
|
132
|
-
{
|
133
|
-
if (exception instanceof GoogleJsonResponseException) {
|
134
|
-
if (((GoogleJsonResponseException) exception).getDetails() == null) {
|
135
|
-
if (((GoogleJsonResponseException) exception).getContent() != null) {
|
136
|
-
String content = ((GoogleJsonResponseException) exception).getContent();
|
137
|
-
log.warn("Invalid response was returned : {}", content);
|
138
|
-
return true;
|
139
|
-
}
|
140
|
-
}
|
141
|
-
int statusCode = ((GoogleJsonResponseException) exception).getDetails().getCode();
|
142
|
-
return !(statusCode / 100 == 4);
|
143
|
-
}
|
144
|
-
else if (exception instanceof TokenResponseException) {
|
145
|
-
TokenResponseException ex = (TokenResponseException) exception;
|
146
|
-
if (ex.getDetails() != null && ex.getDetails().getErrorDescription() != null) {
|
147
|
-
String errorDescription = ex.getDetails().getErrorDescription();
|
148
|
-
// Retry: 400 BadRequest "Invalid JWT..."
|
149
|
-
// Caused by: com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
|
150
|
-
// {
|
151
|
-
// "error" : "invalid_grant",
|
152
|
-
// "error_description" : "Invalid JWT: No valid verifier found for issuer."
|
153
|
-
// }
|
154
|
-
if (errorDescription.contains("Invalid JWT")) {
|
155
|
-
log.warn("Invalid response was returned : {}", errorDescription);
|
156
|
-
return true;
|
157
|
-
}
|
158
|
-
}
|
159
|
-
return !(ex.getStatusCode() / 100 == 4);
|
160
|
-
}
|
161
|
-
return true;
|
162
|
-
}
|
163
|
-
|
164
|
-
@Override
|
165
|
-
public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
|
166
|
-
throws RetryGiveupException
|
167
|
-
{
|
168
|
-
String message = String.format("GCS GET request failed. Retrying %d/%d after %d seconds. Message: %s: %s",
|
169
|
-
retryCount, retryLimit, retryWait / 1000, exception.getClass(), exception.getMessage());
|
170
|
-
if (retryCount % 3 == 0) {
|
171
|
-
log.warn(message, exception);
|
172
|
-
}
|
173
|
-
else {
|
174
|
-
log.warn(message);
|
175
|
-
}
|
176
|
-
}
|
177
|
-
|
178
|
-
@Override
|
179
|
-
public void onGiveup(Exception firstException, Exception lastException)
|
180
|
-
throws RetryGiveupException
|
181
|
-
{
|
182
|
-
}
|
183
|
-
});
|
184
|
-
}
|
185
|
-
catch (RetryGiveupException ex) {
|
186
|
-
if (ex.getCause() instanceof GoogleJsonResponseException || ex.getCause() instanceof TokenResponseException) {
|
187
|
-
int statusCode = 0;
|
188
|
-
if (ex.getCause() instanceof GoogleJsonResponseException) {
|
189
|
-
if (((GoogleJsonResponseException) ex.getCause()).getDetails() != null) {
|
190
|
-
statusCode = ((GoogleJsonResponseException) ex.getCause()).getDetails().getCode();
|
191
|
-
}
|
192
|
-
}
|
193
|
-
else if (ex.getCause() instanceof TokenResponseException) {
|
194
|
-
statusCode = ((TokenResponseException) ex.getCause()).getStatusCode();
|
195
|
-
}
|
196
|
-
if (statusCode / 100 == 4) {
|
197
|
-
throw new ConfigException(ex);
|
198
|
-
}
|
199
|
-
}
|
200
|
-
throw Throwables.propagate(ex);
|
201
|
-
}
|
202
|
-
catch (InterruptedException ex) {
|
203
|
-
throw new InterruptedIOException();
|
204
|
-
}
|
205
|
-
}
|
206
|
-
}
|
@@ -1,185 +0,0 @@
|
|
1
|
-
package org.embulk.input.gcs;
|
2
|
-
|
3
|
-
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
4
|
-
import com.google.api.services.storage.Storage;
|
5
|
-
import org.embulk.EmbulkTestRuntime;
|
6
|
-
import org.embulk.config.ConfigException;
|
7
|
-
import org.junit.BeforeClass;
|
8
|
-
import org.junit.Rule;
|
9
|
-
import org.junit.Test;
|
10
|
-
|
11
|
-
import java.io.FileNotFoundException;
|
12
|
-
|
13
|
-
import java.io.IOException;
|
14
|
-
import java.lang.reflect.Field;
|
15
|
-
import java.security.GeneralSecurityException;
|
16
|
-
import java.util.Optional;
|
17
|
-
|
18
|
-
import static org.junit.Assert.assertEquals;
|
19
|
-
import static org.junit.Assume.assumeNotNull;
|
20
|
-
|
21
|
-
public class TestGcsAuthentication
|
22
|
-
{
|
23
|
-
private static Optional<String> GCP_EMAIL;
|
24
|
-
private static Optional<String> GCP_P12_KEYFILE;
|
25
|
-
private static Optional<String> GCP_JSON_KEYFILE;
|
26
|
-
private static String GCP_BUCKET;
|
27
|
-
private static final String GCP_APPLICATION_NAME = "embulk-input-gcs";
|
28
|
-
private static int MAX_CONNECTION_RETRY = 3;
|
29
|
-
|
30
|
-
/*
|
31
|
-
* This test case requires environment variables
|
32
|
-
* GCP_EMAIL
|
33
|
-
* GCP_P12_KEYFILE
|
34
|
-
* GCP_JSON_KEYFILE
|
35
|
-
* GCP_BUCKET
|
36
|
-
*/
|
37
|
-
@BeforeClass
|
38
|
-
public static void initializeConstant()
|
39
|
-
{
|
40
|
-
String gcpEmail = System.getenv("GCP_EMAIL");
|
41
|
-
String gcpP12KeyFile = System.getenv("GCP_P12_KEYFILE");
|
42
|
-
String gcpJsonKeyFile = System.getenv("GCP_JSON_KEYFILE");
|
43
|
-
String gcpBucket = System.getenv("GCP_BUCKET");
|
44
|
-
|
45
|
-
// skip test cases, if environment variables are not set.
|
46
|
-
assumeNotNull(gcpEmail, gcpP12KeyFile, gcpJsonKeyFile, gcpBucket);
|
47
|
-
|
48
|
-
GCP_EMAIL = Optional.of(gcpEmail);
|
49
|
-
GCP_P12_KEYFILE = Optional.of(gcpP12KeyFile);
|
50
|
-
GCP_JSON_KEYFILE = Optional.of(gcpJsonKeyFile);
|
51
|
-
GCP_BUCKET = gcpBucket;
|
52
|
-
}
|
53
|
-
|
54
|
-
@Rule
|
55
|
-
public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
|
56
|
-
|
57
|
-
@Test
|
58
|
-
public void testGetServiceAccountCredentialSuccess()
|
59
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
60
|
-
{
|
61
|
-
GcsAuthentication auth = new GcsAuthentication(
|
62
|
-
"private_key",
|
63
|
-
GCP_EMAIL,
|
64
|
-
GCP_P12_KEYFILE,
|
65
|
-
null,
|
66
|
-
GCP_APPLICATION_NAME
|
67
|
-
);
|
68
|
-
|
69
|
-
Field field = GcsAuthentication.class.getDeclaredField("credentials");
|
70
|
-
field.setAccessible(true);
|
71
|
-
|
72
|
-
assertEquals(GoogleCredential.class, field.get(auth).getClass());
|
73
|
-
}
|
74
|
-
|
75
|
-
@Test(expected = FileNotFoundException.class)
|
76
|
-
public void testGetServiceAccountCredentialThrowFileNotFoundException()
|
77
|
-
throws GeneralSecurityException, IOException
|
78
|
-
{
|
79
|
-
Optional<String> notFoundP12Keyfile = Optional.of("/path/to/notfound.p12");
|
80
|
-
GcsAuthentication auth = new GcsAuthentication(
|
81
|
-
"private_key",
|
82
|
-
GCP_EMAIL,
|
83
|
-
notFoundP12Keyfile,
|
84
|
-
null,
|
85
|
-
GCP_APPLICATION_NAME
|
86
|
-
);
|
87
|
-
}
|
88
|
-
|
89
|
-
@Test
|
90
|
-
public void testGetGcsClientUsingServiceAccountCredentialSuccess()
|
91
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
92
|
-
{
|
93
|
-
GcsAuthentication auth = new GcsAuthentication(
|
94
|
-
"private_key",
|
95
|
-
GCP_EMAIL,
|
96
|
-
GCP_P12_KEYFILE,
|
97
|
-
null,
|
98
|
-
GCP_APPLICATION_NAME
|
99
|
-
);
|
100
|
-
|
101
|
-
Storage client = auth.getGcsClient(GCP_BUCKET, MAX_CONNECTION_RETRY);
|
102
|
-
|
103
|
-
assertEquals(Storage.class, client.getClass());
|
104
|
-
}
|
105
|
-
|
106
|
-
@Test(expected = ConfigException.class)
|
107
|
-
public void testGetGcsClientUsingServiceAccountCredentialThrowJsonResponseException()
|
108
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
109
|
-
{
|
110
|
-
GcsAuthentication auth = new GcsAuthentication(
|
111
|
-
"private_key",
|
112
|
-
GCP_EMAIL,
|
113
|
-
GCP_P12_KEYFILE,
|
114
|
-
null,
|
115
|
-
GCP_APPLICATION_NAME
|
116
|
-
);
|
117
|
-
|
118
|
-
Storage client = auth.getGcsClient("non-exists-bucket", MAX_CONNECTION_RETRY);
|
119
|
-
|
120
|
-
assertEquals(Storage.class, client.getClass());
|
121
|
-
}
|
122
|
-
|
123
|
-
@Test
|
124
|
-
public void testGetServiceAccountCredentialFromJsonFileSuccess()
|
125
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
126
|
-
{
|
127
|
-
GcsAuthentication auth = new GcsAuthentication(
|
128
|
-
"json_key",
|
129
|
-
GCP_EMAIL,
|
130
|
-
null,
|
131
|
-
GCP_JSON_KEYFILE,
|
132
|
-
GCP_APPLICATION_NAME
|
133
|
-
);
|
134
|
-
Field field = GcsAuthentication.class.getDeclaredField("credentials");
|
135
|
-
field.setAccessible(true);
|
136
|
-
|
137
|
-
assertEquals(GoogleCredential.class, field.get(auth).getClass());
|
138
|
-
}
|
139
|
-
|
140
|
-
@Test(expected = FileNotFoundException.class)
|
141
|
-
public void testGetServiceAccountCredentialFromJsonThrowFileFileNotFoundException()
|
142
|
-
throws GeneralSecurityException, IOException
|
143
|
-
{
|
144
|
-
Optional<String> notFoundJsonKeyfile = Optional.of("/path/to/notfound.json");
|
145
|
-
GcsAuthentication auth = new GcsAuthentication(
|
146
|
-
"json_key",
|
147
|
-
GCP_EMAIL,
|
148
|
-
null,
|
149
|
-
notFoundJsonKeyfile,
|
150
|
-
GCP_APPLICATION_NAME
|
151
|
-
);
|
152
|
-
}
|
153
|
-
|
154
|
-
@Test
|
155
|
-
public void testGetServiceAccountCredentialFromJsonSuccess()
|
156
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
157
|
-
{
|
158
|
-
GcsAuthentication auth = new GcsAuthentication(
|
159
|
-
"json_key",
|
160
|
-
GCP_EMAIL,
|
161
|
-
null,
|
162
|
-
GCP_JSON_KEYFILE,
|
163
|
-
GCP_APPLICATION_NAME
|
164
|
-
);
|
165
|
-
|
166
|
-
Storage client = auth.getGcsClient(GCP_BUCKET, MAX_CONNECTION_RETRY);
|
167
|
-
|
168
|
-
assertEquals(Storage.class, client.getClass());
|
169
|
-
}
|
170
|
-
|
171
|
-
@Test(expected = ConfigException.class)
|
172
|
-
public void testGetServiceAccountCredentialFromJsonThrowGoogleJsonResponseException()
|
173
|
-
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
174
|
-
{
|
175
|
-
GcsAuthentication auth = new GcsAuthentication(
|
176
|
-
"json_key",
|
177
|
-
GCP_EMAIL,
|
178
|
-
null,
|
179
|
-
GCP_JSON_KEYFILE,
|
180
|
-
GCP_APPLICATION_NAME
|
181
|
-
);
|
182
|
-
|
183
|
-
Storage client = auth.getGcsClient("non-exists-bucket", MAX_CONNECTION_RETRY);
|
184
|
-
}
|
185
|
-
}
|
Binary file
|