embulk-output-azure_blob_storage 0.1.2 → 0.1.3

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 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