embulk-output-azure_blob_storage 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8e7f5671df4655218d40ef364f9475e05f3cdaa4
4
- data.tar.gz: 5ba40cd551788bdde07a5a08ea05998a3ee7f0cb
3
+ metadata.gz: 5cc1a4b0540a0db2bb640ffff57daef127b63eec
4
+ data.tar.gz: 09115e5d83c014bac1b3d8d19b12fb8adad3a4be
5
5
  SHA512:
6
- metadata.gz: c3330eefd6eab6363a399a09905c374c0b8c427fa367f44d545d2b7eba77c91e4480b7d76b360fa06307603532de9da227e90b5ca3380aa650087165de4c5149
7
- data.tar.gz: 76c557136ad97309d1175433879ad7ae92ff2d5b826b72b772c724b624130685c2e53a748b1b079f6c3d914f90dda9ef86311a9536d2543f60cd69d546d10576
6
+ metadata.gz: 6b167e589e571d77ed3b33552505090aa58bdc1c4470fd36fc66bbc90b8dca8129a0b2f031b1832640a2b0154855fd207a2100477f26ef34fec7ac6a8c4ee818
7
+ data.tar.gz: 9aedb87b4cdb6750ded5ef47108fd9574392f3a244ac11c051c287595c04195abd54ccbccb0c84f92a961e0aa48f8f015a6bfd94110252865dc437238e2515f5
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ ## 0.1.3 - 2015-03-15
2
+
3
+ * [maintenance] Add retry logic using expotential backoff [#6](https://github.com/sakama/embulk-output-azure_blob_storage/pull/6)
4
+ * [maintenance] Change local file output path to temporary directory
5
+ [#5](https://github.com/sakama/embulk-output-azure_blob_storage/pull/5)
6
+
7
+ ## 0.1.2 - 2015-02-02
8
+
9
+ * [maintenance] Upgrade embulk version to v0.8.2 [#3](https://github.com/sakama/embulk-output-azure_blob_storage/pull/3)
10
+
11
+ ## 0.1.1 - 2015-11-16
12
+
13
+ * [maintenance] Added unit tests [#2](https://github.com/sakama/embulk-output-azure_blob_storage/pull/2)
14
+ * [maintenance] Refactored code [#1](https://github.com/sakama/embulk-output-azure_blob_storage/pull/1)
data/README.md CHANGED
@@ -70,7 +70,7 @@ AZURE_CONTAINER_DIRECTORY (optional, if needed)
70
70
  ```
71
71
 
72
72
  If you're using Mac OS X El Capitan and GUI Applications(IDE), like as follows.
73
- ```
73
+ ```xml
74
74
  $ vi ~/Library/LaunchAgents/environment.plist
75
75
  <?xml version="1.0" encoding="UTF-8"?>
76
76
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -98,4 +98,4 @@ $ launchctl load ~/Library/LaunchAgents/environment.plist
98
98
  $ launchctl getenv AZURE_ACCOUNT_NAME //try to get value.
99
99
 
100
100
  Then start your applications.
101
- ```
101
+ ```
data/build.gradle CHANGED
@@ -17,7 +17,7 @@ configurations {
17
17
  sourceCompatibility = 1.7
18
18
  targetCompatibility = 1.7
19
19
 
20
- version = "0.1.2"
20
+ version = "0.1.3"
21
21
 
22
22
  dependencies {
23
23
  compile "org.embulk:embulk-core:0.8.2"
@@ -56,6 +56,10 @@ public class AzureBlobStorageFileOutputPlugin
56
56
  @Config("sequence_format")
57
57
  @ConfigDefault("\"%03d.%02d\"")
58
58
  String getSequenceFormat();
59
+
60
+ @Config("max_connection_retry")
61
+ @ConfigDefault("5") // 5 times retry to connect sftp server if failed.
62
+ int getMaxConnectionRetry();
59
63
  }
60
64
 
61
65
  private static final Logger log = Exec.getLogger(AzureBlobStorageFileOutputPlugin.class);
@@ -71,7 +75,7 @@ public class AzureBlobStorageFileOutputPlugin
71
75
  String containerName = task.getContainer();
72
76
  CloudBlobContainer container = blobClient.getContainerReference(containerName);
73
77
  if (!container.exists()) {
74
- log.info(String.format("container [%s] is not exists and created.", containerName));
78
+ log.info("container {} doesn't exists and created.", containerName);
75
79
  container.createIfNotExists();
76
80
  }
77
81
  }
@@ -124,6 +128,7 @@ public class AzureBlobStorageFileOutputPlugin
124
128
  private final String sequenceFormat;
125
129
  private final String pathSuffix;
126
130
  private final CloudBlobClient client;
131
+ private final int maxConnectionRetry;
127
132
  private CloudBlobContainer container = null;
128
133
  private BufferedOutputStream output = null;
129
134
  private int fileIndex;
@@ -138,6 +143,7 @@ public class AzureBlobStorageFileOutputPlugin
138
143
  this.sequenceFormat = task.getSequenceFormat();
139
144
  this.pathSuffix = task.getFileNameExtension();
140
145
  this.client = newAzureClient(task.getAccountName(), task.getAccountKey());
146
+ this.maxConnectionRetry = task.getMaxConnectionRetry();
141
147
  try {
142
148
  this.container = client.getContainerReference(task.getContainer());
143
149
  }
@@ -157,17 +163,11 @@ public class AzureBlobStorageFileOutputPlugin
157
163
  suffix = "." + suffix;
158
164
  }
159
165
  filePath = pathPrefix + String.format(sequenceFormat, taskIndex, fileIndex) + suffix;
160
- file = new File(filePath);
161
-
162
- String parentPath = file.getParent();
163
- File dir = new File(parentPath);
164
- if (!dir.exists()) {
165
- dir.mkdir();
166
- }
167
- log.info(String.format("Writing local file [%s]", filePath));
168
- output = new BufferedOutputStream(new FileOutputStream(filePath));
166
+ file = File.createTempFile(filePath, ".tmp");
167
+ log.info("Writing local file {}", file.getAbsolutePath());
168
+ output = new BufferedOutputStream(new FileOutputStream(file));
169
169
  }
170
- catch (FileNotFoundException ex) {
170
+ catch (IOException ex) {
171
171
  throw Throwables.propagate(ex);
172
172
  }
173
173
  }
@@ -202,18 +202,47 @@ public class AzureBlobStorageFileOutputPlugin
202
202
  @Override
203
203
  public void finish()
204
204
  {
205
- closeFile();
205
+ close();
206
+ try {
207
+ Thread.sleep(1000 * 10);
208
+ }
209
+ catch (Exception ex) {
210
+ // null;
211
+ }
206
212
  if (filePath != null) {
207
- try {
208
- CloudBlockBlob blob = container.getBlockBlobReference(filePath);
209
- log.info(String.format("Upload start [%s]", filePath));
210
- blob.upload(new FileInputStream(file), file.length());
211
- log.info(String.format("Upload completed [%s]", filePath));
212
- file.delete();
213
- log.info(String.format("Delete completed local file [%s]", filePath));
214
- }
215
- catch (StorageException | URISyntaxException | IOException ex) {
216
- Throwables.propagate(ex);
213
+ int count = 0;
214
+ while (true) {
215
+ try {
216
+ CloudBlockBlob blob = container.getBlockBlobReference(filePath);
217
+ log.info("Upload start {} to {}", file.getAbsolutePath(), filePath);
218
+ blob.upload(new FileInputStream(file), file.length());
219
+ log.info("Upload completed {} to {}", file.getAbsolutePath(), filePath);
220
+ log.info("Delete completed local file {}", file.getAbsolutePath());
221
+ if (!file.delete()) {
222
+ throw new ConfigException("Couldn't delete local file " + file.getAbsolutePath());
223
+ }
224
+ break;
225
+ }
226
+ catch (FileNotFoundException | URISyntaxException ex) {
227
+ throw new ConfigException(ex);
228
+ }
229
+ catch (StorageException | IOException ex) {
230
+ if (++count == maxConnectionRetry) {
231
+ Throwables.propagate(ex);
232
+ }
233
+ log.warn("failed to connect SFTP server: " + ex.getMessage(), ex);
234
+
235
+ try {
236
+ long sleepTime = ((long) Math.pow(2, count) * 1000);
237
+ log.warn("sleep in next connection retry: {} milliseconds", sleepTime);
238
+ Thread.sleep(sleepTime); // milliseconds
239
+ }
240
+ catch (InterruptedException ex2) {
241
+ // Ignore this exception because this exception is just about `sleep`.
242
+ log.warn(ex2.getMessage(), ex2);
243
+ }
244
+ log.warn("retrying to connect SFTP server: " + count + " times");
245
+ }
217
246
  }
218
247
  }
219
248
  }
@@ -3,11 +3,13 @@ package org.embulk.output.azure_blob_storage;
3
3
  import com.google.common.collect.ImmutableList;
4
4
  import com.google.common.collect.ImmutableMap;
5
5
  import com.google.common.collect.Lists;
6
+ import com.google.common.io.Resources;
6
7
  import com.microsoft.azure.storage.blob.CloudBlob;
7
8
  import com.microsoft.azure.storage.blob.CloudBlobClient;
8
9
  import com.microsoft.azure.storage.blob.CloudBlobContainer;
9
10
  import org.embulk.EmbulkTestRuntime;
10
11
  import org.embulk.config.ConfigDiff;
12
+ import org.embulk.config.ConfigException;
11
13
  import org.embulk.config.ConfigSource;
12
14
  import org.embulk.config.TaskReport;
13
15
  import org.embulk.config.TaskSource;
@@ -30,10 +32,12 @@ import static org.junit.Assume.assumeNotNull;
30
32
 
31
33
  import java.io.BufferedReader;
32
34
  import java.io.ByteArrayOutputStream;
35
+ import java.io.File;
33
36
  import java.io.FileInputStream;
34
37
  import java.io.IOException;
35
38
  import java.io.InputStream;
36
39
  import java.io.InputStreamReader;
40
+ import java.lang.reflect.Field;
37
41
  import java.lang.reflect.Method;
38
42
  import java.security.GeneralSecurityException;
39
43
  import java.util.Arrays;
@@ -67,7 +71,7 @@ public class TestAzureBlobStorageFileOutputPlugin
67
71
 
68
72
  AZURE_CONTAINER_DIRECTORY = System.getenv("AZURE_CONTAINER_DIRECTORY") != null ? getDirectory(System.getenv("AZURE_CONTAINER_DIRECTORY")) : getDirectory("");
69
73
  AZURE_PATH_PREFIX = AZURE_CONTAINER_DIRECTORY + "sample_";
70
- LOCAL_PATH_PREFIX = AzureBlobStorageFileOutputPlugin.class.getClassLoader().getResource("sample_01.csv").getPath();
74
+ LOCAL_PATH_PREFIX = Resources.getResource("sample_01.csv").getPath();
71
75
  }
72
76
 
73
77
  @Rule
@@ -96,7 +100,7 @@ public class TestAzureBlobStorageFileOutputPlugin
96
100
  .set("formatter", formatterConfig());
97
101
 
98
102
  PluginTask task = config.loadConfig(PluginTask.class);
99
- assertEquals(AZURE_ACCOUNT_NAME, task.getAccountName().toString());
103
+ assertEquals(AZURE_ACCOUNT_NAME, task.getAccountName());
100
104
  }
101
105
 
102
106
  @Test
@@ -210,6 +214,64 @@ public class TestAzureBlobStorageFileOutputPlugin
210
214
  assertRecords(remotePath);
211
215
  }
212
216
 
217
+ @Test(expected = ConfigException.class)
218
+ public void testAzureFileOutputByOpenWithNonReadableFile() throws Exception
219
+ {
220
+ ConfigSource configSource = config();
221
+ PluginTask task = configSource.loadConfig(PluginTask.class);
222
+ Schema schema = configSource.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
223
+ runner.transaction(configSource, schema, 0, new Control());
224
+
225
+ TransactionalFileOutput output = plugin.open(task.dump(), 0);
226
+
227
+ output.nextFile();
228
+
229
+ FileInputStream is = new FileInputStream(LOCAL_PATH_PREFIX);
230
+ byte[] bytes = convertInputStreamToByte(is);
231
+ Buffer buffer = Buffer.wrap(bytes);
232
+ output.add(buffer);
233
+
234
+ Field field = AzureBlobStorageFileOutputPlugin.AzureFileOutput.class.getDeclaredField("file");
235
+ field.setAccessible(true);
236
+ File file = File.createTempFile("non-readable-file", ".tmp");
237
+ file.setReadable(false);
238
+ field.set(output, file);
239
+ output.finish();
240
+ }
241
+
242
+ @Test(expected = RuntimeException.class)
243
+ public void testAzureFileOutputByOpenWithRetry() throws Exception
244
+ {
245
+ ConfigSource configSource = config();
246
+ PluginTask task = configSource.loadConfig(PluginTask.class);
247
+ Schema schema = configSource.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
248
+ runner.transaction(configSource, schema, 0, new Control());
249
+
250
+ TransactionalFileOutput output = plugin.open(task.dump(), 0);
251
+
252
+ output.nextFile();
253
+
254
+ FileInputStream is = new FileInputStream(LOCAL_PATH_PREFIX);
255
+ byte[] bytes = convertInputStreamToByte(is);
256
+ Buffer buffer = Buffer.wrap(bytes);
257
+ output.add(buffer);
258
+
259
+ // set maxConnectionRetry = 1 for Test
260
+ Field maxConnectionRetry = AzureBlobStorageFileOutputPlugin.AzureFileOutput.class.getDeclaredField("maxConnectionRetry");
261
+ maxConnectionRetry.setAccessible(true);
262
+ maxConnectionRetry.set(output, 1);
263
+
264
+ // set non-existing-container for Test
265
+ Method method = AzureBlobStorageFileOutputPlugin.class.getDeclaredMethod("newAzureClient", String.class, String.class);
266
+ method.setAccessible(true);
267
+ CloudBlobClient client = (CloudBlobClient) method.invoke(plugin, AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);
268
+
269
+ Field container = AzureBlobStorageFileOutputPlugin.AzureFileOutput.class.getDeclaredField("container");
270
+ container.setAccessible(true);
271
+ container.set(output, client.getContainerReference("non-exists-container"));
272
+ output.finish();
273
+ }
274
+
213
275
  public ConfigSource config()
214
276
  {
215
277
  return Exec.newConfigSource()
@@ -268,6 +330,7 @@ public class TestAzureBlobStorageFileOutputPlugin
268
330
  builder.add(ImmutableMap.of("name", "time", "type", "timestamp", "format", "%Y-%m-%d %H:%M:%S"));
269
331
  builder.add(ImmutableMap.of("name", "purchase", "type", "timestamp", "format", "%Y%m%d"));
270
332
  builder.add(ImmutableMap.of("name", "comment", "type", "string"));
333
+ builder.add(ImmutableMap.of("name", "json_column", "type", "json"));
271
334
  return builder.build();
272
335
  }
273
336
 
@@ -283,7 +346,7 @@ public class TestAzureBlobStorageFileOutputPlugin
283
346
  private void assertRecords(String azurePath) throws Exception
284
347
  {
285
348
  ImmutableList<List<String>> records = getFileContentsFromAzure(azurePath);
286
- assertEquals(5, records.size());
349
+ assertEquals(6, records.size());
287
350
  {
288
351
  List<String> record = records.get(1);
289
352
  assertEquals("1", record.get(0));
@@ -291,6 +354,7 @@ public class TestAzureBlobStorageFileOutputPlugin
291
354
  assertEquals("2015-01-27 19:23:49", record.get(2));
292
355
  assertEquals("20150127", record.get(3));
293
356
  assertEquals("embulk", record.get(4));
357
+ assertEquals("{\"k\":true}", record.get(5));
294
358
  }
295
359
 
296
360
  {
@@ -300,6 +364,22 @@ public class TestAzureBlobStorageFileOutputPlugin
300
364
  assertEquals("2015-01-27 19:01:23", record.get(2));
301
365
  assertEquals("20150127", record.get(3));
302
366
  assertEquals("embulk jruby", record.get(4));
367
+ assertEquals("{\"k\":1}", record.get(5));
368
+ }
369
+
370
+ {
371
+ List<String> record = records.get(3);
372
+ assertEquals("{\"k\":1.23}", record.get(5));
373
+ }
374
+
375
+ {
376
+ List<String> record = records.get(4);
377
+ assertEquals("{\"k\":\"v\"}", record.get(5));
378
+ }
379
+
380
+ {
381
+ List<String> record = records.get(5);
382
+ assertEquals("{\"k\":\"2015-02-03 08:13:45\"}", record.get(5));
303
383
  }
304
384
  }
305
385
 
@@ -330,11 +410,7 @@ public class TestAzureBlobStorageFileOutputPlugin
330
410
  method.setAccessible(true);
331
411
  CloudBlobClient client = (CloudBlobClient) method.invoke(plugin, AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);
332
412
  CloudBlobContainer container = client.getContainerReference(containerName);
333
- if (container.exists()) {
334
- return true;
335
- }
336
-
337
- return false;
413
+ return container.exists();
338
414
  }
339
415
 
340
416
  private void deleteContainerIfExists(String containerName) throws Exception
@@ -352,7 +428,7 @@ public class TestAzureBlobStorageFileOutputPlugin
352
428
 
353
429
  private static String getDirectory(String dir)
354
430
  {
355
- if (dir != null && !dir.endsWith("/")) {
431
+ if (!dir.isEmpty() && !dir.endsWith("/")) {
356
432
  dir = dir + "/";
357
433
  }
358
434
  if (dir.startsWith("/")) {
@@ -1,5 +1,6 @@
1
- id,account,time,purchase,comment
2
- 1,32864,2015-01-27 19:23:49,20150127,embulk
3
- 2,14824,2015-01-27 19:01:23,20150127,embulk jruby
4
- 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
5
- 4,11270,2015-01-29 11:54:36,20150129,NULL
1
+ id,account,time,purchase,comment,json_column
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk,{"k":true}
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby,{"k":1}
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin",{"k":1.23}
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL,{"k":"v"}
6
+ 5,53231,2015-01-30 13:48:12,20150130,NULL,{"k":"2015-02-03 08:13:45"}
@@ -1,5 +1,6 @@
1
- id,account,time,purchase,comment
2
- 1,32864,2015-01-27 19:23:49,20150127,embulk
3
- 2,14824,2015-01-27 19:01:23,20150127,embulk jruby
4
- 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
5
- 4,11270,2015-01-29 11:54:36,20150129,NULL
1
+ id,account,time,purchase,comment,json_column
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk,{"k":true}
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby,{"k":1}
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin",{"k":1.23}
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL,{"k":"v"}
6
+ 5,53231,2015-01-30 13:48:12,20150130,NULL,{"k":"2015-02-03 08:13:45"}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-azure_blob_storage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Satoshi Akama
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-02 00:00:00.000000000 Z
11
+ date: 2016-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -47,7 +47,7 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - .gitignore
49
49
  - .travis.yml
50
- - ChangeLog
50
+ - CHANGELOG.md
51
51
  - README.md
52
52
  - build.gradle
53
53
  - config/checkstyle/checkstyle.xml
@@ -63,7 +63,7 @@ files:
63
63
  - src/test/resources/sample_02.csv
64
64
  - classpath/azure-storage-4.0.0.jar
65
65
  - classpath/commons-lang3-3.4.jar
66
- - classpath/embulk-output-azure_blob_storage-0.1.2.jar
66
+ - classpath/embulk-output-azure_blob_storage-0.1.3.jar
67
67
  - classpath/jackson-core-2.6.0.jar
68
68
  homepage: https://github.com/sakama/embulk-output-azure_blob_storage
69
69
  licenses:
data/ChangeLog DELETED
@@ -1,4 +0,0 @@
1
- Release 0.1.1 - 2015-11-16
2
-
3
- * Added unit tests
4
- * Refactored code