embulk-input-sftp 0.2.3 → 0.2.4
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/CHANGELOG.md +4 -0
- data/README.md +1 -0
- data/build.gradle +62 -2
- data/classpath/embulk-input-sftp-0.2.4.jar +0 -0
- data/src/main/java/org/embulk/input/sftp/SftpFileInput.java +26 -67
- data/src/test/java/org/embulk/input/sftp/TestSftpFileInputPlugin.java +54 -10
- metadata +13 -13
- data/classpath/embulk-input-sftp-0.2.3.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df31c9ece3349f98ed7d6cb077ea54d93eb03983
|
4
|
+
data.tar.gz: 86a2988731cc55a81ed7e191eb453b952a3d13a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/build.gradle
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
plugins {
|
2
|
-
id "com.jfrog.bintray" version "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
|
-
|
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'
|
Binary file
|
@@ -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
|
-
|
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 (
|
160
|
-
throw new ConfigException("Failed to generate last_path due to
|
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
|
384
|
+
public void testGetRelativePathWithPassword()
|
378
385
|
{
|
379
|
-
ConfigSource conf = config();
|
380
|
-
String expected = "/path/to/sample
|
386
|
+
ConfigSource conf = config.deepCopy();
|
387
|
+
String expected = "/path/to/sample !@#.csv";
|
381
388
|
|
382
389
|
conf.set("password", "ABCDE");
|
383
|
-
PluginTask task =
|
384
|
-
String uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample
|
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 =
|
389
|
-
uri = SftpFileInput.getSftpFileUri(task, "/path/to/sample
|
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.
|
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:
|
11
|
+
date: 2017-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|