embulk-input-sftp 0.2.3 → 0.2.4

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: 20b62b0b1e6694bdaa818d0a993c69a62762dfe0
4
- data.tar.gz: d2cc4ef2c4eb0605cbd5df857840f13559d98112
3
+ metadata.gz: df31c9ece3349f98ed7d6cb077ea54d93eb03983
4
+ data.tar.gz: 86a2988731cc55a81ed7e191eb453b952a3d13a4
5
5
  SHA512:
6
- metadata.gz: 50e595f0263b65e5e360128730ca743ef8d66c2ee0002219e24bd4ff18b55c99516de51399fa4e4b2b9a86998cc0403623d52182bedb8226e95d59bbab82a404
7
- data.tar.gz: 67182a732de5b5d001630e99724b9b7b7c24383c17dab5628c67c61e41f3b8fe3bf4904227a881d65aedd962e9a3422f581fdecc8a1b8bd23a9520b54cbe7e84
6
+ metadata.gz: 5200a610d8dbcde6e46597d9749504da4e1afdeb44f1803c1ff162204ccb96f4eab73397ec889046111b6adf6677653521696fa4e88f4d35e5a0b9c4e3998fe2
7
+ data.tar.gz: 33a363400d78fd010f238a0270f7e086610ba9a9bc9ea25cf59dcf2d03fb66f09198f5df0aa35ea46545fea2dded4eaa5264cc9a56aaf2c604cb49b424617171
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.2.4 - 2017-06-05
2
+
3
+ * [maintenance] Improve logic for remote file search with path_prefix [#22](https://github.com/sakama/embulk-input-sftp/pull/22)
4
+
1
5
  ## 0.2.3 - 2016-09-30
2
6
 
3
7
  * [maintenance] Fix auth failure while generating last_path under limited case [#20](https://github.com/sakama/embulk-input-sftp/pull/20)
data/README.md CHANGED
@@ -135,6 +135,7 @@ in:
135
135
 
136
136
  ```
137
137
  $ ./gradlew gem # -t to watch change of files and rebuild continuously
138
+ $ ./gradlew bintrayUpload # release embulk-input-sftp to Bintray maven repo
138
139
  ```
139
140
 
140
141
  ## Test
data/build.gradle CHANGED
@@ -1,5 +1,6 @@
1
1
  plugins {
2
- id "com.jfrog.bintray" version "1.1"
2
+ id "com.jfrog.bintray" version "1.7"
3
+ id "maven-publish"
3
4
  id "com.github.jruby-gradle.base" version "0.1.5"
4
5
  id "java"
5
6
  id "checkstyle"
@@ -14,7 +15,8 @@ configurations {
14
15
  provided
15
16
  }
16
17
 
17
- version = "0.2.3"
18
+ group = "org.embulk.input.sftp"
19
+ version = "0.2.4"
18
20
 
19
21
  sourceCompatibility = 1.7
20
22
  targetCompatibility = 1.7
@@ -33,6 +35,50 @@ dependencies {
33
35
  testCompile "io.netty:netty-all:4.0.34.Final"
34
36
  }
35
37
 
38
+ javadoc {
39
+ options {
40
+ locale = 'en_US'
41
+ encoding = 'UTF-8'
42
+ }
43
+ }
44
+
45
+ // bintray
46
+ bintray {
47
+ // write at your bintray user name and api key to ~/.gradle/gradle.properties file:
48
+ user = project.hasProperty('bintray_user') ? bintray_user : ''
49
+ key = project.hasProperty('bintray_api_key') ? bintray_api_key : ''
50
+
51
+ publications = ['bintrayMavenRelease']
52
+ publish = true
53
+
54
+ pkg {
55
+ userOrg = 'embulk-input-sftp'
56
+ repo = 'maven'
57
+ name = project.name
58
+ desc = 'SFTP file input plugin for Embulk'
59
+ websiteUrl = 'https://github.com/embulk/embulk-input-sftp'
60
+ issueTrackerUrl = 'https://github.com/embulk/embulk-input-sftp/issues'
61
+ vcsUrl = 'https://github.com/embulk/embulk-input-sftp.git'
62
+ licenses = ['Apache-2.0']
63
+ labels = ['embulk', 'java']
64
+ publicDownloadNumbers = true
65
+
66
+ version {
67
+ name = project.version
68
+ }
69
+ }
70
+ }
71
+ publishing {
72
+ publications {
73
+ bintrayMavenRelease(MavenPublication) {
74
+ from components.java
75
+ artifact testsJar
76
+ artifact sourcesJar
77
+ artifact javadocJar
78
+ }
79
+ }
80
+ }
81
+
36
82
  task classpath(type: Copy, dependsOn: ["jar"]) {
37
83
  doFirst { file("classpath").deleteDir() }
38
84
  from (configurations.runtime - configurations.provided + files(jar.archivePath))
@@ -40,6 +86,20 @@ task classpath(type: Copy, dependsOn: ["jar"]) {
40
86
  }
41
87
  clean { delete "classpath" }
42
88
 
89
+ // add tests/javadoc/source jar tasks as artifacts to be released
90
+ task testsJar(type: Jar, dependsOn: classes) {
91
+ classifier = 'tests'
92
+ from sourceSets.test.output
93
+ }
94
+ task sourcesJar(type: Jar, dependsOn: classes) {
95
+ classifier = 'sources'
96
+ from sourceSets.main.allSource
97
+ }
98
+ task javadocJar(type: Jar, dependsOn: javadoc) {
99
+ classifier = 'javadoc'
100
+ from javadoc.destinationDir
101
+ }
102
+
43
103
  checkstyle {
44
104
  configFile = file("${project.rootDir}/config/checkstyle/checkstyle.xml")
45
105
  toolVersion = '6.14.1'
@@ -4,11 +4,15 @@ import com.google.common.base.Function;
4
4
  import com.google.common.base.Optional;
5
5
  import com.google.common.base.Throwables;
6
6
  import org.apache.commons.io.FilenameUtils;
7
+ import org.apache.commons.lang3.StringUtils;
7
8
  import org.apache.commons.vfs2.FileObject;
8
9
  import org.apache.commons.vfs2.FileSystemException;
9
10
  import org.apache.commons.vfs2.FileSystemOptions;
10
11
  import org.apache.commons.vfs2.impl.StandardFileSystemManager;
12
+ import org.apache.commons.vfs2.provider.UriParser;
13
+ import org.apache.commons.vfs2.provider.local.GenericFileNameParser;
11
14
  import org.apache.commons.vfs2.provider.sftp.IdentityInfo;
15
+ import org.apache.commons.vfs2.provider.sftp.SftpFileNameParser;
12
16
  import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
13
17
  import org.embulk.config.ConfigException;
14
18
  import org.embulk.config.TaskReport;
@@ -19,7 +23,6 @@ import org.embulk.spi.util.InputStreamFileInput;
19
23
  import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
20
24
  import org.embulk.spi.util.RetryExecutor.Retryable;
21
25
  import org.slf4j.Logger;
22
- import static org.embulk.spi.util.RetryExecutor.retryExecutor;
23
26
 
24
27
  import java.io.File;
25
28
  import java.io.IOException;
@@ -27,6 +30,8 @@ import java.net.URI;
27
30
  import java.net.URISyntaxException;
28
31
  import java.util.Arrays;
29
32
 
33
+ import static org.embulk.spi.util.RetryExecutor.retryExecutor;
34
+
30
35
  public class SftpFileInput
31
36
  extends InputStreamFileInput
32
37
  implements TransactionalFileInput
@@ -149,76 +154,23 @@ public class SftpFileInput
149
154
  if (!uri.isPresent()) {
150
155
  return null;
151
156
  }
152
- else if (!task.getSecretKeyFile().isPresent() && task.getPassword().isPresent()) {
153
- return getRelativePathFromURIwithPassword(task, uri);
154
- }
155
157
  else {
156
- return new URI(uri.get()).getPath();
158
+ String uriString = uri.get();
159
+ String scheme = UriParser.extractScheme(uriString);
160
+ if (StringUtils.isEmpty(scheme)) {
161
+ return GenericFileNameParser.getInstance().parseUri(null, null, uriString).getPath();
162
+ }
163
+ else if (scheme.equals("sftp")) {
164
+ return SftpFileNameParser.getInstance().parseUri(null, null, uriString).getPath();
165
+ }
166
+ else {
167
+ throw new ConfigException("SFTP Plugin only support SFTP scheme");
168
+ }
157
169
  }
158
170
  }
159
- catch (URISyntaxException ex) {
160
- throw new ConfigException("Failed to generate last_path due to URI parse failure that contains invalid file path.", ex);
161
- }
162
- }
163
-
164
- private static String getRelativePathFromURIwithPassword(final PluginTask task, final Optional<String> uri)
165
- {
166
- try {
167
- return retryExecutor()
168
- .withRetryLimit(task.getMaxConnectionRetry())
169
- .withInitialRetryWait(500)
170
- .withMaxRetryWait(30 * 1000)
171
- .runInterruptible(new Retryable<String>() {
172
- @Override
173
- public String call() throws URISyntaxException, IOException
174
- {
175
- log.info("Creating last_path from URI contains password in FileList.");
176
- StandardFileSystemManager manager = initializeStandardFileSystemManager();
177
-
178
- String prefix = new URI("sftp", initializeUserInfo(task), task.getHost(), task.getPort(), null, null, null).toString();
179
- prefix = manager.resolveFile(prefix).toString();
180
- // To avoid URI parse failure when password contains special characters
181
- String newUri = uri.get().replace(prefix, "sftp://user:password@example.com/");
182
-
183
- return new URI(newUri).getPath();
184
- }
185
-
186
- @Override
187
- public boolean isRetryableException(Exception exception)
188
- {
189
- if (exception instanceof URISyntaxException) {
190
- // Don't throw cause because URISyntaxException shows password
191
- throw new ConfigException("Failed to generate last_path due to URI parse failure that contains invalid file path or password.");
192
- }
193
- return true;
194
- }
195
-
196
- @Override
197
- public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait) throws RetryGiveupException
198
- {
199
- String message = String.format("SFTP List request failed. Retrying %d/%d after %d seconds. Message: %s",
200
- retryCount, retryLimit, retryWait / 1000, exception.getMessage());
201
- if (retryCount % 3 == 0) {
202
- log.warn(message, exception);
203
- }
204
- else {
205
- log.warn(message);
206
- }
207
- }
208
-
209
- @Override
210
- public void onGiveup(Exception firstException, Exception lastException) throws RetryGiveupException
211
- {
212
- }
213
- });
214
- }
215
- catch (RetryGiveupException ex) {
216
- throw new ConfigException("Failed to generate last_path due to SFTP connection failure");
217
- }
218
- catch (InterruptedException ex) {
219
- Throwables.propagate(ex);
171
+ catch (FileSystemException ex) {
172
+ throw new ConfigException("Failed to generate last_path due to sftp file name parse failure", ex);
220
173
  }
221
- return null;
222
174
  }
223
175
 
224
176
  public static FileList listFilesByPrefix(final PluginTask task)
@@ -247,6 +199,7 @@ public class SftpFileInput
247
199
  FileObject files = manager.resolveFile(getSftpFileUri(task, task.getPathPrefix()), fsOptions);
248
200
  String basename = FilenameUtils.getBaseName(task.getPathPrefix());
249
201
  if (files.isFolder()) {
202
+ //path_prefix is a folder, we add everything in that folder
250
203
  FileObject[] children = files.getChildren();
251
204
  Arrays.sort(children);
252
205
  for (FileObject f : children) {
@@ -255,7 +208,13 @@ public class SftpFileInput
255
208
  }
256
209
  }
257
210
  }
211
+ else if (files.isFile()) {
212
+ //path_prefix is a file then we just need to add that file
213
+ addFileToList(builder, files.toString(), files.getContent().getSize(), "", lastKey);
214
+ }
258
215
  else {
216
+ // path_prefix is neither file or folder, then we scan the parent folder to file path
217
+ // that match the path_prefix basename
259
218
  FileObject parent = files.getParent();
260
219
  FileObject[] children = parent.getChildren();
261
220
  Arrays.sort(children);
@@ -68,6 +68,9 @@ public class TestSftpFileInputPlugin
68
68
  @Rule
69
69
  public TemporaryFolder testFolder = new TemporaryFolder();
70
70
 
71
+ @Rule
72
+ public TemporaryFolder sshDirFolder = new TemporaryFolder();
73
+
71
74
  private Logger log = runtime.getExec().getLogger(TestSftpFileInputPlugin.class);
72
75
  private ConfigSource config;
73
76
  private SftpFileInputPlugin plugin;
@@ -87,11 +90,12 @@ public class TestSftpFileInputPlugin
87
90
  @Before
88
91
  public void createResources() throws Exception
89
92
  {
93
+ // set system property for ssh_dir
94
+ System.setProperty("vfs.sftp.sshdir", sshDirFolder.getRoot().getPath());
90
95
  config = config();
91
96
  plugin = new SftpFileInputPlugin();
92
97
  runner = new FileInputRunner(runtime.getInstance(SftpFileInputPlugin.class));
93
98
  output = new MockPageOutput();
94
-
95
99
  if (!log.isDebugEnabled()) {
96
100
  // TODO: change logging format: org.apache.commons.logging.Log
97
101
  System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");
@@ -162,7 +166,7 @@ public class TestSftpFileInputPlugin
162
166
  public void testResume()
163
167
  {
164
168
  PluginTask task = config.loadConfig(PluginTask.class);
165
- task.setFiles(createFileList(Arrays.asList("in/aa/a"), task));
169
+ task.setFiles(createFileList(Arrays.asList("/in/aa/a"), task));
166
170
  ConfigDiff configDiff = plugin.resume(task.dump(), 0, new FileInputPlugin.Control()
167
171
  {
168
172
  @Override
@@ -171,7 +175,7 @@ public class TestSftpFileInputPlugin
171
175
  return emptyTaskReports(taskCount);
172
176
  }
173
177
  });
174
- assertEquals("in/aa/a", configDiff.get(String.class, "last_path"));
178
+ assertEquals("/in/aa/a", configDiff.get(String.class, "last_path"));
175
179
  }
176
180
 
177
181
  @Test
@@ -373,23 +377,63 @@ public class TestSftpFileInputPlugin
373
377
  assertEquals(SftpFileSystemConfigBuilder.PROXY_STREAM, builder.getProxyType(fsOptions));
374
378
  }
375
379
 
380
+ /**
381
+ * Test get relative path with special character password
382
+ */
376
383
  @Test
377
- public void testGetRelativePath()
384
+ public void testGetRelativePathWithPassword()
378
385
  {
379
- ConfigSource conf = config();
380
- String expected = "/path/to/sample.csv";
386
+ ConfigSource conf = config.deepCopy();
387
+ String expected = "/path/to/sample !@#.csv";
381
388
 
382
389
  conf.set("password", "ABCDE");
383
- PluginTask task = config.loadConfig(PluginTask.class);
384
- String uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample.csv");
390
+ PluginTask task = conf.loadConfig(PluginTask.class);
391
+ String uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample !@#.csv");
385
392
  assertEquals(expected, SftpFileInput.getRelativePath(task, Optional.of(uri)));
386
393
 
387
394
  conf.set("password", "ABCD#$¥!%'\"@?<>\\&/_^~|-=+-,{}[]()");
388
- task = config.loadConfig(PluginTask.class);
389
- uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample.csv");
395
+ task = conf.loadConfig(PluginTask.class);
396
+ uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample !@#.csv");
390
397
  assertEquals(expected, SftpFileInput.getRelativePath(task, Optional.of(uri)));
391
398
  }
392
399
 
400
+ @Test
401
+ public void testGetRelativePath()
402
+ {
403
+ String expected = "/path/to/sample !@#.csv";
404
+ String path = "/path/to/sample !@#.csv";
405
+ config.loadConfig(PluginTask.class);
406
+ assertEquals(expected, SftpFileInput.getRelativePath(null, Optional.of(path)));
407
+ }
408
+
409
+ @Test(expected = ConfigException.class)
410
+ public void testGetRelativePathWithHttpScheme()
411
+ {
412
+ String path = "http://host/path/to/sample !@#.csv";
413
+ config.loadConfig(PluginTask.class);
414
+ SftpFileInput.getRelativePath(null, Optional.of(path));
415
+ }
416
+
417
+ /**
418
+ * When user explicitly set path_prefix to a single file. we should add that file only
419
+ * @throws Exception
420
+ */
421
+ @Test
422
+ public void testListByPrefixWithSpecificPathPrefix() throws Exception
423
+ {
424
+ ConfigSource conf = config.deepCopy();
425
+ conf.set("path_prefix", REMOTE_DIRECTORY + "sample_01.csv");
426
+ PluginTask pluginTask = conf.loadConfig(PluginTask.class);
427
+ uploadFile(Resources.getResource("sample_01.csv").getPath(), REMOTE_DIRECTORY + "sample_01.csv", true);
428
+ uploadFile(Resources.getResource("sample_01.csv").getPath(), REMOTE_DIRECTORY + "sample_01 .csv", true);
429
+ uploadFile(Resources.getResource("sample_01.csv").getPath(), REMOTE_DIRECTORY + "sample_01ABC.csv", true);
430
+ uploadFile(Resources.getResource("sample_01.csv").getPath(), REMOTE_DIRECTORY + "sample_01DEF!@#.csv", true);
431
+ FileList fileList = SftpFileInput.listFilesByPrefix(pluginTask);
432
+ assertEquals(1, fileList.getTaskCount());
433
+ assertEquals("sftp://username:password@127.0.0.1:20022/home/username/unittest/sample_01.csv", fileList.get
434
+ (0).get(0));
435
+ }
436
+
393
437
  private SshServer createSshServer(String host, int port, final String sshUsername, final String sshPassword)
394
438
  {
395
439
  // setup a mock sftp server
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-input-sftp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
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-09-30 00:00:00.000000000 Z
11
+ date: 2017-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- requirement: !ruby/object:Gem::Requirement
14
+ name: bundler
15
+ version_requirements: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - ~>
17
18
  - !ruby/object:Gem::Version
18
19
  version: '1.0'
19
- name: bundler
20
- prerelease: false
21
- type: :development
22
- version_requirements: !ruby/object:Gem::Requirement
20
+ requirement: !ruby/object:Gem::Requirement
23
21
  requirements:
24
22
  - - ~>
25
23
  - !ruby/object:Gem::Version
26
24
  version: '1.0'
25
+ prerelease: false
26
+ type: :development
27
27
  - !ruby/object:Gem::Dependency
28
- requirement: !ruby/object:Gem::Requirement
28
+ name: rake
29
+ version_requirements: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - '>='
31
32
  - !ruby/object:Gem::Version
32
33
  version: '10.0'
33
- name: rake
34
- prerelease: false
35
- type: :development
36
- version_requirements: !ruby/object:Gem::Requirement
34
+ requirement: !ruby/object:Gem::Requirement
37
35
  requirements:
38
36
  - - '>='
39
37
  - !ruby/object:Gem::Version
40
38
  version: '10.0'
39
+ prerelease: false
40
+ type: :development
41
41
  description: Reads files stored on remote server using SFTP.
42
42
  email:
43
43
  - satoshiakama@gmail.com
@@ -72,7 +72,7 @@ files:
72
72
  - classpath/commons-io-1.3.2.jar
73
73
  - classpath/commons-logging-1.2.jar
74
74
  - classpath/commons-vfs2-2.1.jar
75
- - classpath/embulk-input-sftp-0.2.3.jar
75
+ - classpath/embulk-input-sftp-0.2.4.jar
76
76
  - classpath/jsch-0.1.53.jar
77
77
  homepage: https://github.com/embulk/embulk-input-sftp
78
78
  licenses:
Binary file