embulk-output-gcs 0.3.0 → 0.4.0
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 +4 -4
- data/build.gradle +23 -5
- data/config/checkstyle/checkstyle.xml +130 -0
- data/config/checkstyle/default.xml +110 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +2 -2
- data/src/main/java/org/embulk/output/GcsAuthentication.java +184 -96
- data/src/main/java/org/embulk/output/GcsOutputPlugin.java +408 -278
- data/src/test/java/org/embulk/output/TestGcsAuthentication.java +161 -163
- data/src/test/java/org/embulk/output/TestGcsOutputPlugin.java +399 -399
- metadata +5 -3
@@ -1,177 +1,175 @@
|
|
1
1
|
package org.embulk.output;
|
2
2
|
|
3
|
-
import com.google.common.base.Optional;
|
4
|
-
import org.embulk.EmbulkTestRuntime;
|
5
|
-
import com.google.api.services.storage.Storage;
|
6
3
|
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
4
|
+
import com.google.api.services.storage.Storage;
|
5
|
+
import com.google.common.base.Optional;
|
7
6
|
|
8
|
-
import
|
9
|
-
import java.io.FileNotFoundException;
|
10
|
-
import java.security.GeneralSecurityException;
|
11
|
-
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
7
|
+
import org.embulk.EmbulkTestRuntime;
|
12
8
|
|
13
|
-
import
|
9
|
+
import org.embulk.config.ConfigException;
|
14
10
|
import org.junit.BeforeClass;
|
15
11
|
import org.junit.Rule;
|
16
12
|
import org.junit.Test;
|
17
13
|
import static org.junit.Assert.assertEquals;
|
18
14
|
import static org.junit.Assume.assumeNotNull;
|
19
15
|
|
16
|
+
import java.io.FileNotFoundException;
|
17
|
+
|
18
|
+
import java.io.IOException;
|
19
|
+
import java.lang.reflect.Field;
|
20
|
+
import java.security.GeneralSecurityException;
|
21
|
+
|
20
22
|
public class TestGcsAuthentication
|
21
23
|
{
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
);
|
174
|
-
|
175
|
-
Storage client = auth.getGcsClient("non-exists-bucket");
|
176
|
-
}
|
24
|
+
private static Optional<String> GCP_EMAIL;
|
25
|
+
private static Optional<String> GCP_P12_KEYFILE;
|
26
|
+
private static Optional<String> GCP_JSON_KEYFILE;
|
27
|
+
private static String GCP_BUCKET;
|
28
|
+
private static final String GCP_APPLICATION_NAME = "embulk-output-gcs";
|
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
|
+
GCP_EMAIL = Optional.of(System.getenv("GCP_EMAIL"));
|
41
|
+
GCP_P12_KEYFILE = Optional.of(System.getenv("GCP_P12_KEYFILE"));
|
42
|
+
GCP_JSON_KEYFILE = Optional.of(System.getenv("GCP_JSON_KEYFILE"));
|
43
|
+
GCP_BUCKET = System.getenv("GCP_BUCKET");
|
44
|
+
// skip test cases, if environment variables are not set.
|
45
|
+
assumeNotNull(GCP_EMAIL, GCP_P12_KEYFILE, GCP_JSON_KEYFILE, GCP_BUCKET);
|
46
|
+
}
|
47
|
+
|
48
|
+
@Rule
|
49
|
+
public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
|
50
|
+
|
51
|
+
@Test
|
52
|
+
public void testGetServiceAccountCredentialSuccess()
|
53
|
+
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
54
|
+
{
|
55
|
+
GcsAuthentication auth = new GcsAuthentication(
|
56
|
+
"private_key",
|
57
|
+
GCP_EMAIL,
|
58
|
+
GCP_P12_KEYFILE,
|
59
|
+
null,
|
60
|
+
GCP_APPLICATION_NAME
|
61
|
+
);
|
62
|
+
|
63
|
+
Field field = GcsAuthentication.class.getDeclaredField("credentials");
|
64
|
+
field.setAccessible(true);
|
65
|
+
|
66
|
+
assertEquals(GoogleCredential.class, field.get(auth).getClass());
|
67
|
+
}
|
68
|
+
|
69
|
+
@Test(expected = FileNotFoundException.class)
|
70
|
+
public void testGetServiceAccountCredentialThrowFileNotFoundException()
|
71
|
+
throws GeneralSecurityException, IOException
|
72
|
+
{
|
73
|
+
Optional<String> notFoundP12Keyfile = Optional.of("/path/to/notfound.p12");
|
74
|
+
GcsAuthentication auth = new GcsAuthentication(
|
75
|
+
"private_key",
|
76
|
+
GCP_EMAIL,
|
77
|
+
notFoundP12Keyfile,
|
78
|
+
null,
|
79
|
+
GCP_APPLICATION_NAME
|
80
|
+
);
|
81
|
+
}
|
82
|
+
|
83
|
+
@Test
|
84
|
+
public void testGetGcsClientUsingServiceAccountCredentialSuccess() throws Exception
|
85
|
+
{
|
86
|
+
GcsAuthentication auth = new GcsAuthentication(
|
87
|
+
"private_key",
|
88
|
+
GCP_EMAIL,
|
89
|
+
GCP_P12_KEYFILE,
|
90
|
+
null,
|
91
|
+
GCP_APPLICATION_NAME
|
92
|
+
);
|
93
|
+
|
94
|
+
Storage client = auth.getGcsClient(GCP_BUCKET, 3);
|
95
|
+
|
96
|
+
assertEquals(Storage.class, client.getClass());
|
97
|
+
}
|
98
|
+
|
99
|
+
@Test(expected = ConfigException.class)
|
100
|
+
public void testGetGcsClientUsingServiceAccountCredentialThrowConfigException() throws Exception
|
101
|
+
{
|
102
|
+
GcsAuthentication auth = new GcsAuthentication(
|
103
|
+
"private_key",
|
104
|
+
GCP_EMAIL,
|
105
|
+
GCP_P12_KEYFILE,
|
106
|
+
null,
|
107
|
+
GCP_APPLICATION_NAME
|
108
|
+
);
|
109
|
+
|
110
|
+
Storage client = auth.getGcsClient("non-exists-bucket", 3);
|
111
|
+
|
112
|
+
assertEquals(Storage.class, client.getClass());
|
113
|
+
}
|
114
|
+
|
115
|
+
@Test
|
116
|
+
public void testGetServiceAccountCredentialFromJsonFileSuccess()
|
117
|
+
throws NoSuchFieldException, IllegalAccessException, GeneralSecurityException, IOException
|
118
|
+
{
|
119
|
+
GcsAuthentication auth = new GcsAuthentication(
|
120
|
+
"json_key",
|
121
|
+
GCP_EMAIL,
|
122
|
+
null,
|
123
|
+
GCP_JSON_KEYFILE,
|
124
|
+
GCP_APPLICATION_NAME
|
125
|
+
);
|
126
|
+
Field field = GcsAuthentication.class.getDeclaredField("credentials");
|
127
|
+
field.setAccessible(true);
|
128
|
+
|
129
|
+
assertEquals(GoogleCredential.class, field.get(auth).getClass());
|
130
|
+
}
|
131
|
+
|
132
|
+
@Test(expected = FileNotFoundException.class)
|
133
|
+
public void testGetServiceAccountCredentialFromJsonThrowFileFileNotFoundException()
|
134
|
+
throws GeneralSecurityException, IOException
|
135
|
+
{
|
136
|
+
Optional<String> notFoundJsonKeyfile = Optional.of("/path/to/notfound.json");
|
137
|
+
GcsAuthentication auth = new GcsAuthentication(
|
138
|
+
"json_key",
|
139
|
+
GCP_EMAIL,
|
140
|
+
null,
|
141
|
+
notFoundJsonKeyfile,
|
142
|
+
GCP_APPLICATION_NAME
|
143
|
+
);
|
144
|
+
}
|
145
|
+
|
146
|
+
@Test
|
147
|
+
public void testGetServiceAccountCredentialFromJsonSuccess() throws Exception
|
148
|
+
{
|
149
|
+
GcsAuthentication auth = new GcsAuthentication(
|
150
|
+
"json_key",
|
151
|
+
GCP_EMAIL,
|
152
|
+
null,
|
153
|
+
GCP_JSON_KEYFILE,
|
154
|
+
GCP_APPLICATION_NAME
|
155
|
+
);
|
156
|
+
|
157
|
+
Storage client = auth.getGcsClient(GCP_BUCKET, 3);
|
158
|
+
|
159
|
+
assertEquals(Storage.class, client.getClass());
|
160
|
+
}
|
161
|
+
|
162
|
+
@Test(expected = ConfigException.class)
|
163
|
+
public void testGetServiceAccountCredentialFromJsonThrowConfigException() throws Exception
|
164
|
+
{
|
165
|
+
GcsAuthentication auth = new GcsAuthentication(
|
166
|
+
"json_key",
|
167
|
+
GCP_EMAIL,
|
168
|
+
null,
|
169
|
+
GCP_JSON_KEYFILE,
|
170
|
+
GCP_APPLICATION_NAME
|
171
|
+
);
|
172
|
+
|
173
|
+
Storage client = auth.getGcsClient("non-exists-bucket", 3);
|
174
|
+
}
|
177
175
|
}
|
@@ -1,426 +1,426 @@
|
|
1
1
|
package org.embulk.output;
|
2
2
|
|
3
|
-
import
|
4
|
-
import java.io.ByteArrayOutputStream;
|
5
|
-
import java.io.FileInputStream;
|
6
|
-
import java.io.InputStream;
|
7
|
-
import java.io.InputStreamReader;
|
8
|
-
import java.util.Arrays;
|
9
|
-
import java.util.List;
|
10
|
-
|
11
|
-
import com.google.common.collect.ImmutableMap;
|
3
|
+
import com.google.api.services.storage.Storage;
|
12
4
|
import com.google.common.base.Optional;
|
13
5
|
import com.google.common.collect.ImmutableList;
|
6
|
+
import com.google.common.collect.ImmutableMap;
|
14
7
|
import com.google.common.collect.Lists;
|
15
|
-
import java.io.IOException;
|
16
|
-
import java.security.GeneralSecurityException;
|
17
8
|
|
18
9
|
import org.embulk.EmbulkTestRuntime;
|
10
|
+
import org.embulk.config.ConfigException;
|
11
|
+
import org.embulk.config.ConfigSource;
|
19
12
|
import org.embulk.config.TaskReport;
|
20
13
|
import org.embulk.config.TaskSource;
|
21
|
-
import org.embulk.
|
22
|
-
import org.embulk.config.ConfigSource;
|
23
|
-
import org.embulk.config.ConfigException;
|
14
|
+
import org.embulk.output.GcsOutputPlugin.PluginTask;
|
24
15
|
import org.embulk.spi.Buffer;
|
25
16
|
import org.embulk.spi.Exec;
|
26
17
|
import org.embulk.spi.FileOutputPlugin;
|
27
18
|
import org.embulk.spi.FileOutputRunner;
|
28
19
|
import org.embulk.spi.OutputPlugin;
|
29
|
-
import org.embulk.spi.TransactionalFileOutput;
|
30
20
|
import org.embulk.spi.Schema;
|
31
|
-
import org.embulk.
|
21
|
+
import org.embulk.spi.TransactionalFileOutput;
|
32
22
|
import org.embulk.standards.CsvParserPlugin;
|
33
23
|
|
34
|
-
import org.junit.BeforeClass;
|
35
24
|
import org.junit.Before;
|
25
|
+
import org.junit.BeforeClass;
|
36
26
|
import org.junit.Rule;
|
37
27
|
import org.junit.Test;
|
38
28
|
import static org.junit.Assert.assertEquals;
|
39
29
|
import static org.junit.Assume.assumeNotNull;
|
40
|
-
import java.lang.reflect.Method;
|
41
|
-
import java.lang.reflect.InvocationTargetException;
|
42
30
|
|
43
|
-
import
|
31
|
+
import java.io.BufferedReader;
|
32
|
+
import java.io.ByteArrayOutputStream;
|
33
|
+
import java.io.FileInputStream;
|
34
|
+
import java.io.IOException;
|
35
|
+
import java.io.InputStream;
|
36
|
+
import java.io.InputStreamReader;
|
37
|
+
import java.lang.reflect.InvocationTargetException;
|
38
|
+
import java.lang.reflect.Method;
|
39
|
+
import java.security.GeneralSecurityException;
|
40
|
+
import java.util.Arrays;
|
41
|
+
import java.util.List;
|
44
42
|
|
45
43
|
public class TestGcsOutputPlugin
|
46
44
|
{
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
45
|
+
private static Optional<String> GCP_EMAIL;
|
46
|
+
private static Optional<String> GCP_P12_KEYFILE;
|
47
|
+
private static Optional<String> GCP_JSON_KEYFILE;
|
48
|
+
private static String GCP_BUCKET;
|
49
|
+
private static String GCP_BUCKET_DIRECTORY;
|
50
|
+
private static String GCP_PATH_PREFIX;
|
51
|
+
private static String LOCAL_PATH_PREFIX;
|
52
|
+
private static String GCP_APPLICATION_NAME;
|
53
|
+
private FileOutputRunner runner;
|
54
|
+
|
55
|
+
/*
|
56
|
+
* This test case requires environment variables
|
57
|
+
* GCP_EMAIL
|
58
|
+
* GCP_P12_KEYFILE
|
59
|
+
* GCP_JSON_KEYFILE
|
60
|
+
* GCP_BUCKET
|
61
|
+
*/
|
62
|
+
@BeforeClass
|
63
|
+
public static void initializeConstant()
|
64
|
+
{
|
65
|
+
GCP_EMAIL = Optional.of(System.getenv("GCP_EMAIL"));
|
66
|
+
GCP_P12_KEYFILE = Optional.of(System.getenv("GCP_P12_KEYFILE"));
|
67
|
+
GCP_JSON_KEYFILE = Optional.of(System.getenv("GCP_JSON_KEYFILE"));
|
68
|
+
GCP_BUCKET = System.getenv("GCP_BUCKET");
|
69
|
+
// skip test cases, if environment variables are not set.
|
70
|
+
assumeNotNull(GCP_EMAIL, GCP_P12_KEYFILE, GCP_JSON_KEYFILE, GCP_BUCKET);
|
71
|
+
|
72
|
+
GCP_BUCKET_DIRECTORY = System.getenv("GCP_BUCKET_DIRECTORY") != null ? getDirectory(System.getenv("GCP_BUCKET_DIRECTORY")) : getDirectory("");
|
73
|
+
GCP_PATH_PREFIX = GCP_BUCKET_DIRECTORY + "sample_";
|
74
|
+
LOCAL_PATH_PREFIX = GcsOutputPlugin.class.getClassLoader().getResource("sample_01.csv").getPath();
|
75
|
+
GCP_APPLICATION_NAME = "embulk-output-gcs";
|
76
|
+
}
|
77
|
+
|
78
|
+
@Rule
|
79
|
+
public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
|
80
|
+
private GcsOutputPlugin plugin;
|
81
|
+
|
82
|
+
@Before
|
83
|
+
public void createResources() throws GeneralSecurityException, NoSuchMethodException, IOException
|
84
|
+
{
|
85
|
+
plugin = new GcsOutputPlugin();
|
86
|
+
runner = new FileOutputRunner(runtime.getInstance(GcsOutputPlugin.class));
|
87
|
+
}
|
88
|
+
|
89
|
+
@Test
|
90
|
+
public void checkDefaultValues()
|
91
|
+
{
|
92
|
+
ConfigSource config = Exec.newConfigSource()
|
93
|
+
.set("in", inputConfig())
|
94
|
+
.set("parser", parserConfig(schemaConfig()))
|
95
|
+
.set("type", "gcs")
|
96
|
+
.set("bucket", GCP_BUCKET)
|
97
|
+
.set("path_prefix", "my-prefix")
|
98
|
+
.set("file_ext", ".csv")
|
99
|
+
.set("formatter", formatterConfig());
|
100
|
+
|
101
|
+
GcsOutputPlugin.PluginTask task = config.loadConfig(PluginTask.class);
|
102
|
+
assertEquals("private_key", task.getAuthMethod().toString());
|
103
|
+
}
|
104
|
+
|
105
|
+
// p12_keyfile is null when auth_method is private_key
|
106
|
+
@Test(expected = ConfigException.class)
|
107
|
+
public void checkDefaultValuesP12keyNull()
|
108
|
+
{
|
109
|
+
ConfigSource config = Exec.newConfigSource()
|
110
|
+
.set("in", inputConfig())
|
111
|
+
.set("parser", parserConfig(schemaConfig()))
|
112
|
+
.set("type", "gcs")
|
113
|
+
.set("bucket", GCP_BUCKET)
|
114
|
+
.set("path_prefix", "my-prefix")
|
115
|
+
.set("file_ext", ".csv")
|
116
|
+
.set("auth_method", "private_key")
|
117
|
+
.set("service_account_email", GCP_EMAIL)
|
118
|
+
.set("p12_keyfile", null)
|
119
|
+
.set("formatter", formatterConfig());
|
120
|
+
|
121
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
122
|
+
|
123
|
+
runner.transaction(config, schema, 0, new Control());
|
124
|
+
}
|
125
|
+
|
126
|
+
// both p12_keyfile and p12_keyfile_path set
|
127
|
+
@Test(expected = ConfigException.class)
|
128
|
+
public void checkDefaultValuesConflictSetting()
|
129
|
+
{
|
130
|
+
ConfigSource config = Exec.newConfigSource()
|
131
|
+
.set("in", inputConfig())
|
132
|
+
.set("parser", parserConfig(schemaConfig()))
|
133
|
+
.set("type", "gcs")
|
134
|
+
.set("bucket", GCP_BUCKET)
|
135
|
+
.set("path_prefix", "my-prefix")
|
136
|
+
.set("file_ext", ".csv")
|
137
|
+
.set("auth_method", "private_key")
|
138
|
+
.set("service_account_email", GCP_EMAIL)
|
139
|
+
.set("p12_keyfile", GCP_P12_KEYFILE)
|
140
|
+
.set("p12_keyfile_path", GCP_P12_KEYFILE)
|
141
|
+
.set("formatter", formatterConfig());
|
142
|
+
|
143
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
144
|
+
|
145
|
+
runner.transaction(config, schema, 0, new Control());
|
146
|
+
}
|
147
|
+
|
148
|
+
// invalid p12keyfile when auth_method is private_key
|
149
|
+
@Test(expected = ConfigException.class)
|
150
|
+
public void checkDefaultValuesInvalidPrivateKey()
|
151
|
+
{
|
152
|
+
ConfigSource config = Exec.newConfigSource()
|
153
|
+
.set("in", inputConfig())
|
154
|
+
.set("parser", parserConfig(schemaConfig()))
|
155
|
+
.set("type", "gcs")
|
156
|
+
.set("bucket", GCP_BUCKET)
|
157
|
+
.set("path_prefix", "my-prefix")
|
158
|
+
.set("file_ext", ".csv")
|
159
|
+
.set("auth_method", "private_key")
|
160
|
+
.set("service_account_email", GCP_EMAIL)
|
161
|
+
.set("p12_keyfile", "invalid-key.p12")
|
162
|
+
.set("formatter", formatterConfig());
|
163
|
+
|
164
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
165
|
+
|
166
|
+
runner.transaction(config, schema, 0, new Control());
|
167
|
+
}
|
168
|
+
|
169
|
+
// json_keyfile is null when auth_method is json_key
|
170
|
+
@Test(expected = ConfigException.class)
|
171
|
+
public void checkDefaultValuesJsonKeyfileNull()
|
172
|
+
{
|
173
|
+
ConfigSource config = Exec.newConfigSource()
|
174
|
+
.set("in", inputConfig())
|
175
|
+
.set("parser", parserConfig(schemaConfig()))
|
176
|
+
.set("type", "gcs")
|
177
|
+
.set("bucket", GCP_BUCKET)
|
178
|
+
.set("path_prefix", "my-prefix")
|
179
|
+
.set("file_ext", ".csv")
|
180
|
+
.set("auth_method", "json_key")
|
181
|
+
.set("service_account_email", GCP_EMAIL)
|
182
|
+
.set("json_keyfile", null)
|
183
|
+
.set("formatter", formatterConfig());
|
184
|
+
|
185
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
186
|
+
|
187
|
+
runner.transaction(config, schema, 0, new Control());
|
188
|
+
}
|
189
|
+
|
190
|
+
@Test
|
191
|
+
public void testGcsClientCreateSuccessfully()
|
192
|
+
throws GeneralSecurityException, IOException, NoSuchMethodException,
|
193
|
+
IllegalAccessException, InvocationTargetException
|
194
|
+
{
|
195
|
+
ConfigSource configSource = config();
|
196
|
+
PluginTask task = configSource.loadConfig(PluginTask.class);
|
197
|
+
Schema schema = configSource.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
198
|
+
runner.transaction(configSource, schema, 0, new Control());
|
199
|
+
|
200
|
+
Method method = GcsOutputPlugin.class.getDeclaredMethod("createClient", PluginTask.class);
|
201
|
+
method.setAccessible(true);
|
202
|
+
method.invoke(plugin, task); // no errors happens
|
203
|
+
}
|
204
|
+
|
205
|
+
@Test(expected = ConfigException.class)
|
206
|
+
public void testGcsClientCreateThrowConfigException()
|
207
|
+
throws GeneralSecurityException, IOException, NoSuchMethodException,
|
208
|
+
IllegalAccessException, InvocationTargetException
|
209
|
+
{
|
210
|
+
ConfigSource config = Exec.newConfigSource()
|
211
|
+
.set("in", inputConfig())
|
212
|
+
.set("parser", parserConfig(schemaConfig()))
|
213
|
+
.set("type", "gcs")
|
214
|
+
.set("bucket", "non-exists-bucket")
|
215
|
+
.set("path_prefix", "my-prefix")
|
216
|
+
.set("file_ext", ".csv")
|
217
|
+
.set("auth_method", "json_key")
|
218
|
+
.set("service_account_email", GCP_EMAIL)
|
219
|
+
.set("json_keyfile", GCP_JSON_KEYFILE)
|
220
|
+
.set("formatter", formatterConfig());
|
221
|
+
|
222
|
+
PluginTask task = config.loadConfig(PluginTask.class);
|
223
|
+
|
224
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
225
|
+
runner.transaction(config, schema, 0, new Control());
|
226
|
+
|
227
|
+
Method method = GcsOutputPlugin.class.getDeclaredMethod("createClient", PluginTask.class);
|
228
|
+
method.setAccessible(true);
|
229
|
+
try {
|
230
|
+
method.invoke(plugin, task);
|
231
|
+
}
|
232
|
+
catch (InvocationTargetException ex) {
|
233
|
+
throw (ConfigException) ex.getCause();
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
@Test
|
238
|
+
public void testResume()
|
239
|
+
{
|
240
|
+
PluginTask task = config().loadConfig(PluginTask.class);
|
241
|
+
plugin.resume(task.dump(), 0, new FileOutputPlugin.Control() // no errors happens
|
242
|
+
{
|
243
|
+
@Override
|
244
|
+
public List<TaskReport> run(TaskSource taskSource)
|
245
|
+
{
|
246
|
+
return Lists.newArrayList(Exec.newTaskReport());
|
247
|
+
}
|
248
|
+
});
|
249
|
+
}
|
250
|
+
|
251
|
+
@Test
|
252
|
+
public void testCleanup()
|
253
|
+
{
|
254
|
+
PluginTask task = config().loadConfig(PluginTask.class);
|
255
|
+
plugin.cleanup(task.dump(), 0, Lists.<TaskReport>newArrayList()); // no errors happens
|
256
|
+
}
|
257
|
+
|
258
|
+
@Test
|
259
|
+
public void testGcsFileOutputByOpen() throws Exception
|
260
|
+
{
|
261
|
+
ConfigSource configSource = config();
|
262
|
+
PluginTask task = configSource.loadConfig(PluginTask.class);
|
263
|
+
Schema schema = configSource.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
264
|
+
runner.transaction(configSource, schema, 0, new Control());
|
265
|
+
|
266
|
+
TransactionalFileOutput output = plugin.open(task.dump(), 0);
|
267
|
+
|
268
|
+
output.nextFile();
|
269
|
+
|
270
|
+
FileInputStream is = new FileInputStream(LOCAL_PATH_PREFIX);
|
271
|
+
byte[] bytes = convertInputStreamToByte(is);
|
272
|
+
Buffer buffer = Buffer.wrap(bytes);
|
273
|
+
output.add(buffer);
|
274
|
+
|
275
|
+
output.finish();
|
276
|
+
output.commit();
|
277
|
+
|
278
|
+
String remotePath = GCP_PATH_PREFIX + String.format(task.getSequenceFormat(), 0, 0) + task.getFileNameExtension();
|
279
|
+
assertRecords(remotePath);
|
280
|
+
}
|
281
|
+
|
282
|
+
public ConfigSource config()
|
283
|
+
{
|
284
|
+
return Exec.newConfigSource()
|
285
|
+
.set("in", inputConfig())
|
286
|
+
.set("parser", parserConfig(schemaConfig()))
|
287
|
+
.set("type", "gcs")
|
288
|
+
.set("bucket", GCP_BUCKET)
|
289
|
+
.set("path_prefix", GCP_PATH_PREFIX)
|
290
|
+
.set("last_path", "")
|
291
|
+
.set("file_ext", ".csv")
|
292
|
+
.set("auth_method", "private_key")
|
293
|
+
.set("service_account_email", GCP_EMAIL)
|
294
|
+
.set("p12_keyfile", GCP_P12_KEYFILE)
|
295
|
+
.set("json_keyfile", GCP_JSON_KEYFILE)
|
296
|
+
.set("application_name", GCP_APPLICATION_NAME)
|
297
|
+
.set("formatter", formatterConfig());
|
298
|
+
}
|
299
|
+
|
300
|
+
private class Control
|
301
|
+
implements OutputPlugin.Control
|
302
|
+
{
|
303
|
+
@Override
|
304
|
+
public List<TaskReport> run(TaskSource taskSource)
|
305
|
+
{
|
306
|
+
return Lists.newArrayList(Exec.newTaskReport());
|
307
|
+
}
|
308
|
+
}
|
309
|
+
|
310
|
+
private ImmutableMap<String, Object> inputConfig()
|
311
|
+
{
|
312
|
+
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
|
313
|
+
builder.put("type", "file");
|
314
|
+
builder.put("path_prefix", LOCAL_PATH_PREFIX);
|
315
|
+
builder.put("last_path", "");
|
316
|
+
return builder.build();
|
317
|
+
}
|
318
|
+
|
319
|
+
private ImmutableMap<String, Object> parserConfig(ImmutableList<Object> schemaConfig)
|
320
|
+
{
|
321
|
+
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
|
322
|
+
builder.put("type", "csv");
|
323
|
+
builder.put("newline", "CRLF");
|
324
|
+
builder.put("delimiter", ",");
|
325
|
+
builder.put("quote", "\"");
|
326
|
+
builder.put("escape", "\"");
|
327
|
+
builder.put("trim_if_not_quoted", false);
|
328
|
+
builder.put("skip_header_lines", 1);
|
329
|
+
builder.put("allow_extra_columns", false);
|
330
|
+
builder.put("allow_optional_columns", false);
|
331
|
+
builder.put("columns", schemaConfig);
|
332
|
+
return builder.build();
|
333
|
+
}
|
334
|
+
|
335
|
+
private ImmutableList<Object> schemaConfig()
|
336
|
+
{
|
337
|
+
ImmutableList.Builder<Object> builder = new ImmutableList.Builder<>();
|
338
|
+
builder.add(ImmutableMap.of("name", "id", "type", "long"));
|
339
|
+
builder.add(ImmutableMap.of("name", "account", "type", "long"));
|
340
|
+
builder.add(ImmutableMap.of("name", "time", "type", "timestamp", "format", "%Y-%m-%d %H:%M:%S"));
|
341
|
+
builder.add(ImmutableMap.of("name", "purchase", "type", "timestamp", "format", "%Y%m%d"));
|
342
|
+
builder.add(ImmutableMap.of("name", "comment", "type", "string"));
|
343
|
+
return builder.build();
|
344
|
+
}
|
345
|
+
|
346
|
+
private ImmutableMap<String, Object> formatterConfig()
|
347
|
+
{
|
348
|
+
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
|
349
|
+
builder.put("type", "csv");
|
350
|
+
builder.put("header_line", "false");
|
351
|
+
builder.put("timezone", "Asia/Tokyo");
|
352
|
+
return builder.build();
|
353
|
+
}
|
354
|
+
|
355
|
+
private void assertRecords(String gcsPath) throws Exception
|
356
|
+
{
|
357
|
+
ImmutableList<List<String>> records = getFileContentsFromGcs(gcsPath);
|
358
|
+
assertEquals(5, records.size());
|
359
|
+
{
|
360
|
+
List<String> record = records.get(1);
|
361
|
+
assertEquals("1", record.get(0));
|
362
|
+
assertEquals("32864", record.get(1));
|
363
|
+
assertEquals("2015-01-27 19:23:49", record.get(2));
|
364
|
+
assertEquals("20150127", record.get(3));
|
365
|
+
assertEquals("embulk", record.get(4));
|
366
|
+
}
|
367
|
+
|
368
|
+
{
|
369
|
+
List<String> record = records.get(2);
|
370
|
+
assertEquals("2", record.get(0));
|
371
|
+
assertEquals("14824", record.get(1));
|
372
|
+
assertEquals("2015-01-27 19:01:23", record.get(2));
|
373
|
+
assertEquals("20150127", record.get(3));
|
374
|
+
assertEquals("embulk jruby", record.get(4));
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
private ImmutableList<List<String>> getFileContentsFromGcs(String path) throws Exception
|
379
|
+
{
|
380
|
+
ConfigSource config = config();
|
381
|
+
|
382
|
+
PluginTask task = config.loadConfig(PluginTask.class);
|
383
|
+
|
384
|
+
Method method = GcsOutputPlugin.class.getDeclaredMethod("createClient", PluginTask.class);
|
385
|
+
method.setAccessible(true);
|
386
|
+
Storage client = (Storage) method.invoke(plugin, task);
|
387
|
+
Storage.Objects.Get getObject = client.objects().get(GCP_BUCKET, path);
|
388
|
+
|
389
|
+
ImmutableList.Builder<List<String>> builder = new ImmutableList.Builder<>();
|
390
|
+
|
391
|
+
InputStream is = getObject.executeMediaAsInputStream();
|
392
|
+
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
393
|
+
String line;
|
394
|
+
while ((line = reader.readLine()) != null) {
|
395
|
+
List<String> records = Arrays.asList(line.split(",", 0));
|
396
|
+
|
397
|
+
builder.add(records);
|
398
|
+
}
|
399
|
+
return builder.build();
|
400
|
+
}
|
401
|
+
|
402
|
+
private static String getDirectory(String dir)
|
403
|
+
{
|
404
|
+
if (dir != null && !dir.endsWith("/")) {
|
405
|
+
dir = dir + "/";
|
406
|
+
}
|
407
|
+
if (dir.startsWith("/")) {
|
408
|
+
dir = dir.replaceFirst("/", "");
|
409
|
+
}
|
410
|
+
return dir;
|
411
|
+
}
|
412
|
+
|
413
|
+
private byte[] convertInputStreamToByte(InputStream is) throws IOException
|
414
|
+
{
|
415
|
+
ByteArrayOutputStream bo = new ByteArrayOutputStream();
|
416
|
+
byte [] buffer = new byte[1024];
|
417
|
+
while (true) {
|
418
|
+
int len = is.read(buffer);
|
419
|
+
if (len < 0) {
|
420
|
+
break;
|
421
|
+
}
|
422
|
+
bo.write(buffer, 0, len);
|
423
|
+
}
|
424
|
+
return bo.toByteArray();
|
425
|
+
}
|
426
426
|
}
|