embulk-input-remote 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 305d88a19ed8811959b2b5fe1f28ccd0f0c0470f
4
+ data.tar.gz: 267dcf87dbc85df3abc4d79d04d710a7a894a2a5
5
+ SHA512:
6
+ metadata.gz: f45656d575e8801e2b8f10901bdfeb2bfdcd91e1edc58242e4505954b58d96ca1d58b0b3f81b1c803982c9b9519f7539cc41e8dc49d578d8bf494f2003fb608d
7
+ data.tar.gz: 6126dd4f0f71de003097de04f643d24672ea6ed95490705ff01a81a5c74dc187d63b20703de1542f7fa6cc1d20aa096f0a7a430c34541bdbed0a78963cb6bbfa
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *~
2
+ /pkg/
3
+ /tmp/
4
+ .gradle/
5
+ /classpath/
6
+ build/
7
+ .idea
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+
2
+ MIT License
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # Remote file input plugin for Embulk
2
+
3
+ This plugin load data from Remote hosts by SCP
4
+
5
+ ## Overview
6
+
7
+ * **Plugin type**: file input
8
+ * **Resume supported**: yes
9
+ * **Cleanup supported**: yes
10
+
11
+ ## Configuration
12
+
13
+ - **hosts**: Target hosts (list, default: [])
14
+ - **hosts_command**: Command to getting hosts(Windows not supported). If given the option, "hosts" is overwritten. (string, default: null)
15
+ - **hosts_separator**: Separator of "hosts_command" result (string, default: " ")
16
+ - **path**: Path of remote host (File or Directory) (string, default: "")
17
+ - **path_command**: Command to getting hosts (Windows not supported). If given the option "path" is overwritten. (string, default: null)
18
+ - **auth**: SSH authentication setting (map, default: {})
19
+ - **user**: SSH username (string, default: executing username)
20
+ - **type**: public_key or password (string, default: public_key)
21
+ - **key_path**: Path of secret key (If you choose type "public_key") (string, default: "~/.ssh/id_rsa or id_dsa")
22
+ - **password**: SSH password (If you choose type "password") (string)
23
+
24
+ ## Example
25
+
26
+ ```yaml
27
+ in:
28
+ type: remote
29
+ hosts:
30
+ - host1
31
+ - host2
32
+ # hosts_command: echo 'host1,host2'
33
+ # hosts_separator: ','
34
+ path: echo /some/path/20150414125923
35
+ # path_command: echo /some/path/`date "+%Y%m%d%H%M%S"`
36
+ auth:
37
+ user: {username}
38
+ type: public_key
39
+ key_path: /usr/home/.ssh/id_rsa
40
+ # type: password
41
+ # password: {password}
42
+ ```
43
+
44
+
45
+ ## Build
46
+
47
+ ```
48
+ $ ./gradlew gem
49
+ ```
data/build.gradle ADDED
@@ -0,0 +1,57 @@
1
+ plugins {
2
+ id "com.jfrog.bintray" version "1.1"
3
+ id "com.github.jruby-gradle.base" version "0.1.5"
4
+ id "java"
5
+ id "idea"
6
+ }
7
+ import com.github.jrubygradle.JRubyExec
8
+ repositories {
9
+ mavenCentral()
10
+ jcenter()
11
+ }
12
+ configurations {
13
+ provided
14
+ }
15
+
16
+ version = "0.1.0"
17
+
18
+ dependencies {
19
+ compile "org.embulk:embulk-core:0.6.1"
20
+ compile "com.hierynomus:sshj:0.11.0"
21
+ provided "org.embulk:embulk-core:0.6.1"
22
+ testCompile "junit:junit:4.+"
23
+ }
24
+
25
+ task classpath(type: Copy, dependsOn: ["jar"]) {
26
+ doFirst { file("classpath").deleteDir() }
27
+ from (configurations.runtime - configurations.provided + files(jar.archivePath))
28
+ into "classpath"
29
+ }
30
+ clean { delete 'classpath' }
31
+
32
+ task gem(type: JRubyExec, dependsOn: ["build", "gemspec", "classpath"]) {
33
+ jrubyArgs "-rrubygems/gem_runner", "-eGem::GemRunner.new.run(ARGV)", "build"
34
+ script "build/gemspec"
35
+ doLast { ant.move(file: "${project.name}-${project.version}.gem", todir: "pkg") }
36
+ }
37
+
38
+ task gemspec << { file("build/gemspec").write($/
39
+ Gem::Specification.new do |spec|
40
+ spec.name = "${project.name}"
41
+ spec.version = "${project.version}"
42
+ spec.authors = ["Shinichi ISHIMURA"]
43
+ spec.summary = %[Remote file input plugin for Embulk]
44
+ spec.description = %[Reads files stored on Remote hosts.]
45
+ spec.email = ["shiketaudonko41@gmail.com"]
46
+ spec.licenses = ["MIT"]
47
+ spec.homepage = "https://github.com/kamatama41/embulk-input-remote"
48
+
49
+ spec.files = `git ls-files`.split("\n") + Dir["classpath/*.jar"]
50
+ spec.test_files = spec.files.grep(%r"^(test|spec)/")
51
+ spec.require_paths = ["lib"]
52
+
53
+ spec.add_development_dependency 'bundler', ['~> 1.0']
54
+ spec.add_development_dependency 'rake', ['>= 10.0']
55
+ end
56
+ /$)
57
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,6 @@
1
+ #Mon Apr 13 17:43:10 JST 2015
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionPath=wrapper/dists
4
+ zipStoreBase=GRADLE_USER_HOME
5
+ zipStorePath=wrapper/dists
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
data/gradlew ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ##############################################################################
4
+ ##
5
+ ## Gradle start up script for UN*X
6
+ ##
7
+ ##############################################################################
8
+
9
+ # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10
+ DEFAULT_JVM_OPTS=""
11
+
12
+ APP_NAME="Gradle"
13
+ APP_BASE_NAME=`basename "$0"`
14
+
15
+ # Use the maximum available, or set MAX_FD != -1 to use that value.
16
+ MAX_FD="maximum"
17
+
18
+ warn ( ) {
19
+ echo "$*"
20
+ }
21
+
22
+ die ( ) {
23
+ echo
24
+ echo "$*"
25
+ echo
26
+ exit 1
27
+ }
28
+
29
+ # OS specific support (must be 'true' or 'false').
30
+ cygwin=false
31
+ msys=false
32
+ darwin=false
33
+ case "`uname`" in
34
+ CYGWIN* )
35
+ cygwin=true
36
+ ;;
37
+ Darwin* )
38
+ darwin=true
39
+ ;;
40
+ MINGW* )
41
+ msys=true
42
+ ;;
43
+ esac
44
+
45
+ # For Cygwin, ensure paths are in UNIX format before anything is touched.
46
+ if $cygwin ; then
47
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48
+ fi
49
+
50
+ # Attempt to set APP_HOME
51
+ # Resolve links: $0 may be a link
52
+ PRG="$0"
53
+ # Need this for relative symlinks.
54
+ while [ -h "$PRG" ] ; do
55
+ ls=`ls -ld "$PRG"`
56
+ link=`expr "$ls" : '.*-> \(.*\)$'`
57
+ if expr "$link" : '/.*' > /dev/null; then
58
+ PRG="$link"
59
+ else
60
+ PRG=`dirname "$PRG"`"/$link"
61
+ fi
62
+ done
63
+ SAVED="`pwd`"
64
+ cd "`dirname \"$PRG\"`/" >&-
65
+ APP_HOME="`pwd -P`"
66
+ cd "$SAVED" >&-
67
+
68
+ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69
+
70
+ # Determine the Java command to use to start the JVM.
71
+ if [ -n "$JAVA_HOME" ] ; then
72
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73
+ # IBM's JDK on AIX uses strange locations for the executables
74
+ JAVACMD="$JAVA_HOME/jre/sh/java"
75
+ else
76
+ JAVACMD="$JAVA_HOME/bin/java"
77
+ fi
78
+ if [ ! -x "$JAVACMD" ] ; then
79
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80
+
81
+ Please set the JAVA_HOME variable in your environment to match the
82
+ location of your Java installation."
83
+ fi
84
+ else
85
+ JAVACMD="java"
86
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87
+
88
+ Please set the JAVA_HOME variable in your environment to match the
89
+ location of your Java installation."
90
+ fi
91
+
92
+ # Increase the maximum file descriptors if we can.
93
+ if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94
+ MAX_FD_LIMIT=`ulimit -H -n`
95
+ if [ $? -eq 0 ] ; then
96
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97
+ MAX_FD="$MAX_FD_LIMIT"
98
+ fi
99
+ ulimit -n $MAX_FD
100
+ if [ $? -ne 0 ] ; then
101
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
102
+ fi
103
+ else
104
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105
+ fi
106
+ fi
107
+
108
+ # For Darwin, add options to specify how the application appears in the dock
109
+ if $darwin; then
110
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111
+ fi
112
+
113
+ # For Cygwin, switch paths to Windows format before running java
114
+ if $cygwin ; then
115
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117
+
118
+ # We build the pattern for arguments to be converted via cygpath
119
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120
+ SEP=""
121
+ for dir in $ROOTDIRSRAW ; do
122
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
123
+ SEP="|"
124
+ done
125
+ OURCYGPATTERN="(^($ROOTDIRS))"
126
+ # Add a user-defined pattern to the cygpath arguments
127
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129
+ fi
130
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
131
+ i=0
132
+ for arg in "$@" ; do
133
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135
+
136
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138
+ else
139
+ eval `echo args$i`="\"$arg\""
140
+ fi
141
+ i=$((i+1))
142
+ done
143
+ case $i in
144
+ (0) set -- ;;
145
+ (1) set -- "$args0" ;;
146
+ (2) set -- "$args0" "$args1" ;;
147
+ (3) set -- "$args0" "$args1" "$args2" ;;
148
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154
+ esac
155
+ fi
156
+
157
+ # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158
+ function splitJvmOpts() {
159
+ JVM_OPTS=("$@")
160
+ }
161
+ eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162
+ JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163
+
164
+ exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
data/gradlew.bat ADDED
@@ -0,0 +1,90 @@
1
+ @if "%DEBUG%" == "" @echo off
2
+ @rem ##########################################################################
3
+ @rem
4
+ @rem Gradle startup script for Windows
5
+ @rem
6
+ @rem ##########################################################################
7
+
8
+ @rem Set local scope for the variables with windows NT shell
9
+ if "%OS%"=="Windows_NT" setlocal
10
+
11
+ @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12
+ set DEFAULT_JVM_OPTS=
13
+
14
+ set DIRNAME=%~dp0
15
+ if "%DIRNAME%" == "" set DIRNAME=.
16
+ set APP_BASE_NAME=%~n0
17
+ set APP_HOME=%DIRNAME%
18
+
19
+ @rem Find java.exe
20
+ if defined JAVA_HOME goto findJavaFromJavaHome
21
+
22
+ set JAVA_EXE=java.exe
23
+ %JAVA_EXE% -version >NUL 2>&1
24
+ if "%ERRORLEVEL%" == "0" goto init
25
+
26
+ echo.
27
+ echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28
+ echo.
29
+ echo Please set the JAVA_HOME variable in your environment to match the
30
+ echo location of your Java installation.
31
+
32
+ goto fail
33
+
34
+ :findJavaFromJavaHome
35
+ set JAVA_HOME=%JAVA_HOME:"=%
36
+ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37
+
38
+ if exist "%JAVA_EXE%" goto init
39
+
40
+ echo.
41
+ echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42
+ echo.
43
+ echo Please set the JAVA_HOME variable in your environment to match the
44
+ echo location of your Java installation.
45
+
46
+ goto fail
47
+
48
+ :init
49
+ @rem Get command-line arguments, handling Windowz variants
50
+
51
+ if not "%OS%" == "Windows_NT" goto win9xME_args
52
+ if "%@eval[2+2]" == "4" goto 4NT_args
53
+
54
+ :win9xME_args
55
+ @rem Slurp the command line arguments.
56
+ set CMD_LINE_ARGS=
57
+ set _SKIP=2
58
+
59
+ :win9xME_args_slurp
60
+ if "x%~1" == "x" goto execute
61
+
62
+ set CMD_LINE_ARGS=%*
63
+ goto execute
64
+
65
+ :4NT_args
66
+ @rem Get arguments from the 4NT Shell from JP Software
67
+ set CMD_LINE_ARGS=%$
68
+
69
+ :execute
70
+ @rem Setup the command line
71
+
72
+ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73
+
74
+ @rem Execute Gradle
75
+ "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76
+
77
+ :end
78
+ @rem End local scope for the variables with windows NT shell
79
+ if "%ERRORLEVEL%"=="0" goto mainEnd
80
+
81
+ :fail
82
+ rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83
+ rem the _cmd.exe /c_ return code!
84
+ if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85
+ exit /b 1
86
+
87
+ :mainEnd
88
+ if "%OS%"=="Windows_NT" endlocal
89
+
90
+ :omega
@@ -0,0 +1,3 @@
1
+ Embulk::JavaPlugin.register_input(
2
+ "remote", "org.embulk.input.RemoteFileInputPlugin",
3
+ File.expand_path('../../../../classpath', __FILE__))
@@ -0,0 +1,315 @@
1
+ package org.embulk.input;
2
+
3
+ import java.io.BufferedReader;
4
+ import java.io.ByteArrayInputStream;
5
+ import java.io.ByteArrayOutputStream;
6
+ import java.io.IOException;
7
+ import java.io.InputStream;
8
+ import java.io.InputStreamReader;
9
+ import java.io.OutputStream;
10
+ import java.util.ArrayList;
11
+ import java.util.Arrays;
12
+ import java.util.Collections;
13
+ import java.util.List;
14
+ import java.util.Map;
15
+
16
+ import com.fasterxml.jackson.annotation.JsonCreator;
17
+ import com.fasterxml.jackson.annotation.JsonProperty;
18
+ import com.google.common.base.Optional;
19
+ import com.google.common.collect.ImmutableList;
20
+ import net.schmizz.sshj.SSHClient;
21
+ import net.schmizz.sshj.xfer.InMemoryDestFile;
22
+ import net.schmizz.sshj.xfer.LocalDestFile;
23
+ import org.embulk.config.CommitReport;
24
+ import org.embulk.config.Config;
25
+ import org.embulk.config.ConfigDefault;
26
+ import org.embulk.config.ConfigDiff;
27
+ import org.embulk.config.ConfigInject;
28
+ import org.embulk.config.ConfigSource;
29
+ import org.embulk.config.Task;
30
+ import org.embulk.config.TaskSource;
31
+ import org.embulk.spi.BufferAllocator;
32
+ import org.embulk.spi.Exec;
33
+ import org.embulk.spi.FileInputPlugin;
34
+ import org.embulk.spi.TransactionalFileInput;
35
+ import org.embulk.spi.util.InputStreamFileInput;
36
+ import org.slf4j.Logger;
37
+
38
+ import javax.annotation.Nullable;
39
+
40
+ public class RemoteFileInputPlugin
41
+ implements FileInputPlugin {
42
+ public interface PluginTask
43
+ extends Task {
44
+ @Config("hosts")
45
+ @ConfigDefault("[]")
46
+ public List<String> getHosts();
47
+
48
+ @Config("hosts_command")
49
+ @ConfigDefault("null")
50
+ public Optional<String> getHostsCommand();
51
+
52
+ @Config("hosts_separator")
53
+ @ConfigDefault("\" \"")
54
+ public String getHostsSeparator();
55
+
56
+ @Config("path")
57
+ @ConfigDefault("\"\"")
58
+ public String getPath();
59
+
60
+ @Config("path_command")
61
+ @ConfigDefault("null")
62
+ public Optional<String> getPathCommand();
63
+
64
+ @Config("auth")
65
+ @ConfigDefault("{}")
66
+ public Map<String, String> getAuth();
67
+
68
+ @Config("last_target")
69
+ @ConfigDefault("null")
70
+ public Optional<Target> getLastTarget();
71
+
72
+ public void setLastTarget(Optional<Target> lastTarget);
73
+
74
+ public List<Target> getTargets();
75
+
76
+ public void setTargets(List<Target> targets);
77
+
78
+ @ConfigInject
79
+ public BufferAllocator getBufferAllocator();
80
+ }
81
+
82
+ private final Logger log = Exec.getLogger(getClass());
83
+
84
+ @Override
85
+ public ConfigDiff transaction(ConfigSource config, FileInputPlugin.Control control) {
86
+ PluginTask task = config.loadConfig(PluginTask.class);
87
+
88
+ List<Target> targets = listTargets(task);
89
+ log.info("Loading targets {}", targets);
90
+ task.setTargets(targets);
91
+
92
+ // number of processors is same with number of targets
93
+ int taskCount = targets.size();
94
+ return resume(task.dump(), taskCount, control);
95
+ }
96
+
97
+ private List<Target> listTargets(PluginTask task) {
98
+ task.getHostsCommand().orNull();
99
+
100
+ final List<String> hosts = listHosts(task);
101
+ final String path = getPath(task);
102
+
103
+ final ImmutableList.Builder<Target> builder = ImmutableList.builder();
104
+ Target lastTarget = task.getLastTarget().orNull();
105
+ for (String host : hosts) {
106
+ Target target = new Target(host, path);
107
+ if (lastTarget == null || target.compareTo(lastTarget) > 0) {
108
+ builder.add(target);
109
+ }
110
+ }
111
+ return builder.build();
112
+ }
113
+
114
+ private List<String> listHosts(PluginTask task) {
115
+ final String hostsCommand = task.getHostsCommand().orNull();
116
+ if (hostsCommand != null) {
117
+ final String stdout = execCommand(hostsCommand).trim();
118
+ return Arrays.asList(stdout.split(task.getHostsSeparator()));
119
+ } else {
120
+ return task.getHosts();
121
+ }
122
+ }
123
+
124
+ private String getPath(PluginTask task) {
125
+ final String pathCommand = task.getPathCommand().orNull();
126
+ if (pathCommand != null) {
127
+ return execCommand(pathCommand).trim();
128
+ } else {
129
+ return task.getPath();
130
+ }
131
+ }
132
+
133
+ private String execCommand(String command) {
134
+ ProcessBuilder pb = new ProcessBuilder("sh", "-c", command); // TODO: windows
135
+ log.info("Running command {}", command);
136
+ try {
137
+ final Process process = pb.start();
138
+ try (InputStream stream = process.getInputStream();
139
+ BufferedReader brStdout = new BufferedReader(new InputStreamReader(stream))
140
+ ) {
141
+ String line;
142
+ StringBuilder stdout = new StringBuilder();
143
+ while ((line = brStdout.readLine()) != null) {
144
+ stdout.append(line);
145
+ }
146
+
147
+ final int code = process.waitFor();
148
+ if (code != 0) {
149
+ throw new IOException(String.format(
150
+ "Command finished with non-zero exit code. Exit code is %d.", code));
151
+ }
152
+
153
+ return stdout.toString();
154
+ }
155
+ } catch (IOException | InterruptedException e) {
156
+ throw new RuntimeException(e);
157
+ }
158
+ }
159
+
160
+ @Override
161
+ public ConfigDiff resume(TaskSource taskSource,
162
+ int taskCount,
163
+ FileInputPlugin.Control control) {
164
+ PluginTask task = taskSource.loadTask(PluginTask.class);
165
+
166
+ control.run(taskSource, taskCount);
167
+
168
+ List<Target> targets = new ArrayList<>(task.getTargets());
169
+ Collections.sort(targets);
170
+ return Exec.newConfigDiff().set("last_target", targets.get(targets.size() - 1));
171
+ }
172
+
173
+ @Override
174
+ public void cleanup(TaskSource taskSource,
175
+ int taskCount,
176
+ List<CommitReport> successCommitReports) {
177
+ }
178
+
179
+ @Override
180
+ public TransactionalFileInput open(TaskSource taskSource, int taskIndex) {
181
+ PluginTask task = taskSource.loadTask(PluginTask.class);
182
+ final Target target = task.getTargets().get(taskIndex);
183
+
184
+ try {
185
+ return new PluginFileInput(task, download(target, task.getAuth()));
186
+ } catch (IOException e) {
187
+ throw new RuntimeException(e);
188
+ }
189
+ }
190
+
191
+ public static class PluginFileInput
192
+ extends InputStreamFileInput
193
+ implements TransactionalFileInput {
194
+ // TODO create single-file InputStreamFileInput utility
195
+ private static class RemoteFileProvider
196
+ implements InputStreamFileInput.Provider {
197
+ private boolean opened = false;
198
+ private final InputStream input;
199
+
200
+ public RemoteFileProvider(InputStream input) {
201
+ this.input = input;
202
+ }
203
+
204
+ @Override
205
+ public InputStream openNext() throws IOException {
206
+ if (opened) {
207
+ return null;
208
+ }
209
+ opened = true;
210
+ return input;
211
+ }
212
+
213
+ @Override
214
+ public void close() {
215
+ }
216
+ }
217
+
218
+ public PluginFileInput(PluginTask task, InputStream input) {
219
+ super(task.getBufferAllocator(), new RemoteFileProvider(input));
220
+ }
221
+
222
+ @Override
223
+ public void abort() {
224
+ }
225
+
226
+ @Override
227
+ public CommitReport commit() {
228
+ return Exec.newCommitReport();
229
+ }
230
+
231
+
232
+ }
233
+
234
+ private InputStream download(Target target, Map<String, String> auth) throws IOException {
235
+ SSHClient client = new SSHClient();
236
+ client.loadKnownHosts();
237
+ InMemoryDestFileImpl memoryDestFile = new InMemoryDestFileImpl();
238
+ try {
239
+ client.connect(target.getHost());
240
+ final String type = auth.get("type") != null ? auth.get("type") : "public_key";
241
+ final String user = auth.get("user") != null ? auth.get("user") : System.getProperty("user.name");
242
+
243
+ if ("password".equals(type)) {
244
+ client.authPassword(auth.get("user"), auth.get("password"));
245
+ } else if ("public_key".equals(type)) {
246
+ final String key_path = auth.get("key_path");
247
+ if (key_path == null) {
248
+ client.authPublickey(user);
249
+ } else {
250
+ client.authPublickey(user, key_path);
251
+ }
252
+ } else {
253
+ throw new UnsupportedOperationException("Unsupported auth type : " + type);
254
+ }
255
+ client.newSCPFileTransfer().download(target.getPath(), memoryDestFile);
256
+ return new ByteArrayInputStream(memoryDestFile.outputStream.toByteArray());
257
+
258
+ } finally {
259
+ client.disconnect();
260
+ }
261
+ }
262
+
263
+ public static class InMemoryDestFileImpl extends InMemoryDestFile {
264
+
265
+ private ByteArrayOutputStream outputStream;
266
+
267
+ public InMemoryDestFileImpl() {
268
+ this.outputStream = new ByteArrayOutputStream();
269
+ }
270
+
271
+ @Override
272
+ public OutputStream getOutputStream() throws IOException {
273
+ return outputStream;
274
+ }
275
+
276
+ @Override
277
+ public LocalDestFile getTargetDirectory(String dirname) throws IOException {
278
+ return this;
279
+ }
280
+ }
281
+
282
+ public static class Target implements Comparable<Target> {
283
+ private final String host;
284
+ private final String path;
285
+
286
+ @JsonCreator
287
+ public Target(
288
+ @JsonProperty("host") String host,
289
+ @JsonProperty("path") String path) {
290
+ this.host = host;
291
+ this.path = path;
292
+ }
293
+
294
+ public String getHost() {
295
+ return host;
296
+ }
297
+
298
+ public String getPath() {
299
+ return path;
300
+ }
301
+
302
+ @Override
303
+ public int compareTo(@Nullable Target other) {
304
+ if (other == null) {
305
+ throw new NullPointerException();
306
+ }
307
+ return this.toString().compareTo(other.toString());
308
+ }
309
+
310
+ @Override
311
+ public String toString() {
312
+ return host + ":" + path;
313
+ }
314
+ }
315
+ }
@@ -0,0 +1,5 @@
1
+ package org.embulk.input;
2
+
3
+ public class TestRemoteFileInputPlugin
4
+ {
5
+ }
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: embulk-input-remote
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shinichi ISHIMURA
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ~>
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ name: bundler
20
+ prerelease: false
21
+ type: :development
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '10.0'
33
+ name: rake
34
+ prerelease: false
35
+ type: :development
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Reads files stored on Remote hosts.
42
+ email:
43
+ - shiketaudonko41@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - LICENSE.txt
50
+ - README.md
51
+ - build.gradle
52
+ - gradle/wrapper/gradle-wrapper.jar
53
+ - gradle/wrapper/gradle-wrapper.properties
54
+ - gradlew
55
+ - gradlew.bat
56
+ - lib/embulk/input/remote.rb
57
+ - src/main/java/org/embulk/input/RemoteFileInputPlugin.java
58
+ - src/test/java/org/embulk/input/TestRemoteFileInputPlugin.java
59
+ - classpath/bcpkix-jdk15on-1.51.jar
60
+ - classpath/bcprov-jdk15on-1.51.jar
61
+ - classpath/embulk-input-remote-0.1.0.jar
62
+ - classpath/jzlib-1.1.3.jar
63
+ - classpath/sshj-0.11.0.jar
64
+ homepage: https://github.com/kamatama41/embulk-input-remote
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.1.9
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Remote file input plugin for Embulk
88
+ test_files: []