embulk-input-remote 0.2.0 → 0.3.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.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-input-remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shinichi Ishimura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-22 00:00:00.000000000 Z
11
+ date: 2017-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -52,29 +52,28 @@ files:
52
52
  - README.md
53
53
  - build.gradle
54
54
  - docker-compose.yml
55
- - example/csv/sample_01.csv.gz
56
- - example/example.yml.liquid
57
55
  - gradle/wrapper/gradle-wrapper.jar
58
56
  - gradle/wrapper/gradle-wrapper.properties
59
57
  - gradlew
60
58
  - gradlew.bat
61
59
  - lib/embulk/input/remote.rb
62
- - src/main/java/org/embulk/input/RemoteFileInputPlugin.java
63
- - src/main/java/org/embulk/input/remote/SSHClient.java
64
- - src/test/java/org/embulk/input/TestRemoteFileInputPlugin.java
65
- - src/test/java/org/embulk/test/MemoryOutputPlugin.java
66
- - src/test/java/org/embulk/test/MyEmbulkTests.java
67
- - src/test/java/org/embulk/test/MyTestingEmbulk.java
60
+ - settings.gradle
61
+ - src/main/kotlin/org/embulk/input/RemoteFileInputPlugin.kt
62
+ - src/main/kotlin/org/embulk/input/remote/SSHClient.kt
63
+ - src/test/kotlin/org/embulk/input/TestRemoteFileInputPlugin.kt
68
64
  - src/test/resources/input/host1/test.csv
69
65
  - src/test/resources/input/host1/test_command.csv
70
66
  - src/test/resources/input/host2/test.csv
71
67
  - src/test/resources/input/host2/test_command.csv
68
+ - src/test/resources/script/hosts.sh
72
69
  - src/test/resources/yaml/base.yml
73
70
  - classpath/bcpkix-jdk15on-1.51.jar
74
71
  - classpath/bcprov-jdk15on-1.51.jar
75
72
  - classpath/eddsa-0.1.0.jar
76
- - classpath/embulk-input-remote-0.2.0.jar
73
+ - classpath/embulk-input-remote-0.3.0.jar
77
74
  - classpath/jzlib-1.1.3.jar
75
+ - classpath/kotlin-runtime-1.0.6.jar
76
+ - classpath/kotlin-stdlib-1.0.6.jar
78
77
  - classpath/sshj-0.19.1.jar
79
78
  homepage: https://github.com/kamatama41/embulk-input-remote
80
79
  licenses:
Binary file
@@ -1,29 +0,0 @@
1
- in:
2
- type: remote
3
- hosts:
4
- - localhost
5
- path: {{ env.PROJECT_ROOT }}/example/csv
6
- ignore_not_found_hosts: true
7
- auth:
8
- user: {{ env.USER }}
9
- type: password
10
- password: {{ env.PASSWORD }}
11
- decoders:
12
- - {type: gzip}
13
- parser:
14
- charset: UTF-8
15
- newline: CRLF
16
- type: csv
17
- delimiter: ','
18
- quote: '"'
19
- trim_if_not_quoted: false
20
- skip_header_lines: 1
21
- allow_extra_columns: false
22
- allow_optional_columns: false
23
- columns:
24
- - {name: id, type: long}
25
- - {name: account, type: long}
26
- - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
27
- - {name: purchase, type: timestamp, format: '%Y%m%d'}
28
- - {name: comment, type: string}
29
- out: {type: stdout}
@@ -1,314 +0,0 @@
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.util.ArrayList;
10
- import java.util.Arrays;
11
- import java.util.List;
12
- import java.util.Objects;
13
-
14
- import com.fasterxml.jackson.annotation.JsonCreator;
15
- import com.fasterxml.jackson.annotation.JsonProperty;
16
- import com.google.common.base.Optional;
17
- import com.google.common.collect.ImmutableList;
18
- import org.embulk.config.Config;
19
- import org.embulk.config.ConfigDefault;
20
- import org.embulk.config.ConfigDiff;
21
- import org.embulk.config.ConfigInject;
22
- import org.embulk.config.ConfigSource;
23
- import org.embulk.config.Task;
24
- import org.embulk.config.TaskReport;
25
- import org.embulk.config.TaskSource;
26
- import org.embulk.input.remote.SSHClient;
27
- import org.embulk.spi.BufferAllocator;
28
- import org.embulk.spi.Exec;
29
- import org.embulk.spi.FileInputPlugin;
30
- import org.embulk.spi.TransactionalFileInput;
31
- import org.embulk.spi.util.InputStreamTransactionalFileInput;
32
- import org.slf4j.Logger;
33
-
34
- public class RemoteFileInputPlugin
35
- implements FileInputPlugin {
36
- public interface PluginTask
37
- extends Task {
38
- @Config("hosts")
39
- @ConfigDefault("[]")
40
- List<String> getHosts();
41
-
42
- @Config("hosts_command")
43
- @ConfigDefault("null")
44
- Optional<String> getHostsCommand();
45
-
46
- @Config("hosts_separator")
47
- @ConfigDefault("\" \"")
48
- String getHostsSeparator();
49
-
50
- @Config("default_port")
51
- @ConfigDefault("22")
52
- int getDefaultPort();
53
-
54
- @Config("path")
55
- @ConfigDefault("\"\"")
56
- String getPath();
57
-
58
- @Config("path_command")
59
- @ConfigDefault("null")
60
- Optional<String> getPathCommand();
61
-
62
- @Config("auth")
63
- AuthConfig getAuthConfig();
64
-
65
- @Config("ignore_not_found_hosts")
66
- @ConfigDefault("false")
67
- boolean getIgnoreNotFoundHosts();
68
-
69
- @Config("done_targets")
70
- @ConfigDefault("[]")
71
- List<Target> getDoneTargets();
72
-
73
- void setDoneTargets(List<Target> lastTarget);
74
-
75
- List<Target> getTargets();
76
-
77
- void setTargets(List<Target> targets);
78
-
79
- @ConfigInject
80
- BufferAllocator getBufferAllocator();
81
- }
82
-
83
- public interface AuthConfig extends Task {
84
- @Config("type")
85
- @ConfigDefault("\"public_key\"")
86
- String getType();
87
-
88
- @Config("user")
89
- @ConfigDefault("null")
90
- Optional<String> getUser();
91
-
92
- @Config("key_path")
93
- @ConfigDefault("null")
94
- Optional<String> getKeyPath();
95
-
96
- @Config("password")
97
- @ConfigDefault("null")
98
- Optional<String> getPassword();
99
-
100
- @Config("skip_host_key_verification")
101
- @ConfigDefault("false")
102
- boolean getSkipHostKeyVerification();
103
- }
104
-
105
- private final Logger log = Exec.getLogger(getClass());
106
-
107
- @Override
108
- public ConfigDiff transaction(ConfigSource config, FileInputPlugin.Control control) {
109
- PluginTask task = config.loadConfig(PluginTask.class);
110
- List<Target> targets = listTargets(task);
111
- log.info("Loading targets {}", targets);
112
- task.setTargets(targets);
113
-
114
- // number of processors is same with number of targets
115
- int taskCount = targets.size();
116
- return resume(task.dump(), taskCount, control);
117
- }
118
-
119
- private List<Target> listTargets(PluginTask task) {
120
- final List<String> hosts = listHosts(task);
121
- final String path = getPath(task);
122
-
123
- final ImmutableList.Builder<Target> builder = ImmutableList.builder();
124
- List<Target> doneTargets = task.getDoneTargets();
125
- for (String host : hosts) {
126
- String[] split = host.split(":");
127
- final String targetHost = split[0];
128
- int targetPort = task.getDefaultPort();
129
- if (split.length > 1) {
130
- targetPort = Integer.valueOf(split[1]);
131
- }
132
- Target target = new Target(targetHost, targetPort, path);
133
-
134
- if (!doneTargets.contains(target)) {
135
- if (task.getIgnoreNotFoundHosts()) {
136
- try {
137
- final boolean exists = exists(target, task);
138
- if (!exists) {
139
- continue;
140
- }
141
- } catch (IOException e) {
142
- log.warn("failed to check the file exists. " + target.toString(), e);
143
- continue;
144
- }
145
- }
146
- builder.add(target);
147
- }
148
- }
149
- return builder.build();
150
- }
151
-
152
- private List<String> listHosts(PluginTask task) {
153
- final String hostsCommand = task.getHostsCommand().orNull();
154
- if (hostsCommand != null) {
155
- final String stdout = execCommand(hostsCommand).trim();
156
- return Arrays.asList(stdout.split(task.getHostsSeparator()));
157
- } else {
158
- return task.getHosts();
159
- }
160
- }
161
-
162
- private String getPath(PluginTask task) {
163
- final String pathCommand = task.getPathCommand().orNull();
164
- if (pathCommand != null) {
165
- return execCommand(pathCommand).trim();
166
- } else {
167
- return task.getPath();
168
- }
169
- }
170
-
171
- private String execCommand(String command) {
172
- ProcessBuilder pb = new ProcessBuilder("sh", "-c", command); // TODO: windows
173
- log.info("Running command {}", command);
174
- try {
175
- final Process process = pb.start();
176
- try (InputStream stream = process.getInputStream();
177
- BufferedReader brStdout = new BufferedReader(new InputStreamReader(stream))
178
- ) {
179
- String line;
180
- StringBuilder stdout = new StringBuilder();
181
- while ((line = brStdout.readLine()) != null) {
182
- stdout.append(line);
183
- }
184
-
185
- final int code = process.waitFor();
186
- if (code != 0) {
187
- throw new IOException(String.format(
188
- "Command finished with non-zero exit code. Exit code is %d.", code));
189
- }
190
-
191
- return stdout.toString();
192
- }
193
- } catch (IOException | InterruptedException e) {
194
- throw new RuntimeException(e);
195
- }
196
- }
197
-
198
- @Override
199
- public ConfigDiff resume(TaskSource taskSource,
200
- int taskCount,
201
- FileInputPlugin.Control control) {
202
- PluginTask task = taskSource.loadTask(PluginTask.class);
203
-
204
- control.run(taskSource, taskCount);
205
-
206
- List<Target> targets = new ArrayList<>(task.getTargets());
207
-
208
- return Exec.newConfigDiff().set("done_targets", targets);
209
- }
210
-
211
- @Override
212
- public void cleanup(TaskSource taskSource,
213
- int taskCount,
214
- List<TaskReport> successTaskReports) {
215
- }
216
-
217
- @Override
218
- public TransactionalFileInput open(TaskSource taskSource, int taskIndex) {
219
- final PluginTask task = taskSource.loadTask(PluginTask.class);
220
- final Target target = task.getTargets().get(taskIndex);
221
-
222
- return new InputStreamTransactionalFileInput(
223
- task.getBufferAllocator(),
224
- new InputStreamTransactionalFileInput.Opener() {
225
- @Override
226
- public InputStream open() throws IOException {
227
- return download(target, task);
228
- }
229
- }
230
- ) {
231
- @Override
232
- public void abort() {
233
-
234
- }
235
-
236
- @Override
237
- public TaskReport commit() {
238
- return Exec.newTaskReport();
239
- }
240
- };
241
- }
242
-
243
- private boolean exists(Target target, PluginTask task) throws IOException {
244
- try (SSHClient client = SSHClient.connect(target.getHost(), target.getPort(), task.getAuthConfig())) {
245
- final String checkCmd = "ls " + target.getPath(); // TODO: windows
246
- final int timeout = 5/* second */;
247
- final SSHClient.CommandResult commandResult = client.execCommand(checkCmd, timeout);
248
-
249
- if(commandResult.getStatus() != 0) {
250
- log.warn("Remote file not found. {}", target.toString());
251
- return false;
252
- } else {
253
- return true;
254
- }
255
- }
256
- }
257
-
258
- private InputStream download(Target target, PluginTask task) throws IOException {
259
- try (SSHClient client = SSHClient.connect(target.getHost(), target.getPort(), task.getAuthConfig())) {
260
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
261
- client.scpDownload(target.getPath(), stream);
262
- return new ByteArrayInputStream(stream.toByteArray());
263
- }
264
- }
265
-
266
- public static class Target {
267
- private final String host;
268
- private final int port;
269
- private final String path;
270
-
271
- @JsonCreator
272
- public Target(
273
- @JsonProperty("host") String host,
274
- @JsonProperty("port") int port,
275
- @JsonProperty("path") String path
276
- ) {
277
- this.host = host;
278
- this.port = port;
279
- this.path = path;
280
- }
281
-
282
- public String getHost() {
283
- return host;
284
- }
285
-
286
- public int getPort() {
287
- return port;
288
- }
289
-
290
- public String getPath() {
291
- return path;
292
- }
293
-
294
- @Override
295
- public boolean equals(Object o) {
296
- if (this == o) return true;
297
- if (o == null || getClass() != o.getClass()) return false;
298
- Target target = (Target) o;
299
- return port == target.port &&
300
- Objects.equals(host, target.host) &&
301
- Objects.equals(path, target.path);
302
- }
303
-
304
- @Override
305
- public int hashCode() {
306
- return Objects.hash(host, port, path);
307
- }
308
-
309
- @Override
310
- public String toString() {
311
- return host + ":" + port + ":" + path;
312
- }
313
- }
314
- }
@@ -1,116 +0,0 @@
1
- package org.embulk.input.remote;
2
-
3
- import net.schmizz.sshj.DefaultConfig;
4
- import net.schmizz.sshj.connection.channel.direct.Session;
5
- import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
6
- import net.schmizz.sshj.xfer.InMemoryDestFile;
7
- import net.schmizz.sshj.xfer.LocalDestFile;
8
- import org.embulk.input.RemoteFileInputPlugin;
9
-
10
- import java.io.Closeable;
11
- import java.io.IOException;
12
- import java.io.InputStream;
13
- import java.io.OutputStream;
14
- import java.util.concurrent.TimeUnit;
15
-
16
- public class SSHClient implements Closeable {
17
-
18
- private final net.schmizz.sshj.SSHClient client;
19
-
20
- public static SSHClient connect(
21
- String host, int port, RemoteFileInputPlugin.AuthConfig authConfig
22
- ) throws IOException {
23
-
24
- SSHClient client = new SSHClient(new net.schmizz.sshj.SSHClient(new DefaultConfig()));
25
- client.connectToHost(host, port, authConfig);
26
- return client;
27
- }
28
-
29
- private SSHClient(net.schmizz.sshj.SSHClient client) {
30
- this.client = client;
31
- }
32
-
33
- private void connectToHost(String host, int port, RemoteFileInputPlugin.AuthConfig authConfig) throws IOException {
34
- if (authConfig.getSkipHostKeyVerification()) {
35
- client.addHostKeyVerifier(new PromiscuousVerifier());
36
- }
37
- client.loadKnownHosts();
38
- client.connect(host, port);
39
-
40
- final String type = authConfig.getType();
41
- final String user = authConfig.getUser().or(System.getProperty("user.name"));
42
-
43
- if ("password".equals(type)) {
44
- if (authConfig.getPassword().isPresent()) {
45
- client.authPassword(user, authConfig.getPassword().get());
46
- } else {
47
- throw new IllegalStateException("Password is not set.");
48
- }
49
- } else if ("public_key".equals(type)) {
50
- if (authConfig.getKeyPath().isPresent()) {
51
- client.authPublickey(user, authConfig.getKeyPath().get());
52
- } else {
53
- client.authPublickey(user);
54
- }
55
- } else {
56
- throw new UnsupportedOperationException("Unsupported auth type : " + type);
57
- }
58
- }
59
-
60
- public CommandResult execCommand(String command, int timeoutSecond) throws IOException {
61
- try (final Session session = client.startSession()) {
62
- final Session.Command cmd = session.exec(command);
63
- cmd.join(timeoutSecond, TimeUnit.SECONDS);
64
- return new CommandResult(cmd.getExitStatus(), cmd.getInputStream());
65
- }
66
- }
67
-
68
- public void scpDownload(String path, OutputStream stream) throws IOException {
69
- client.useCompression();
70
- client.newSCPFileTransfer().download(path, new InMemoryDestFileImpl(stream));
71
- }
72
-
73
- private static class InMemoryDestFileImpl extends InMemoryDestFile {
74
-
75
- private OutputStream outputStream;
76
-
77
- public InMemoryDestFileImpl(OutputStream outputStream) {
78
- this.outputStream = outputStream;
79
- }
80
-
81
- @Override
82
- public OutputStream getOutputStream() throws IOException {
83
- return outputStream;
84
- }
85
-
86
- @Override
87
- public LocalDestFile getTargetDirectory(String dirname) throws IOException {
88
- return this;
89
- }
90
- }
91
-
92
- @Override
93
- public void close() throws IOException {
94
- if (client != null) {
95
- client.close();
96
- }
97
- }
98
-
99
- public static class CommandResult {
100
- int status;
101
- InputStream stdout;
102
-
103
- private CommandResult(int status, InputStream stdout) {
104
- this.status = status;
105
- this.stdout = stdout;
106
- }
107
-
108
- public int getStatus() {
109
- return status;
110
- }
111
-
112
- public InputStream getStdout() {
113
- return stdout;
114
- }
115
- }
116
- }