embulk-executor-remoteserver 0.2.1 → 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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +1 -0
  3. data/.gitignore +2 -0
  4. data/README.md +22 -3
  5. data/build.gradle +7 -7
  6. data/docker-compose.test.yml +49 -0
  7. data/example/Gemfile +5 -0
  8. data/example/README.md +32 -0
  9. data/example/config.yml +25 -0
  10. data/example/docker-compse.yml +8 -0
  11. data/example/work/input_1.json +1 -0
  12. data/example/work/input_2.json +1 -0
  13. data/gradle.properties +1 -1
  14. data/gradle/dependency-locks/compileClasspath.lockfile +3 -7
  15. data/gradle/dependency-locks/testCompileClasspath.lockfile +4 -8
  16. data/src/main/java/org/embulk/executor/remoteserver/ClientSession.java +7 -2
  17. data/src/main/java/org/embulk/executor/remoteserver/EmbulkClient.java +7 -2
  18. data/src/main/java/org/embulk/executor/remoteserver/EmbulkServer.java +7 -1
  19. data/src/main/java/org/embulk/executor/remoteserver/Launcher.java +31 -5
  20. data/src/main/java/org/embulk/executor/remoteserver/P12File.java +28 -0
  21. data/src/main/java/org/embulk/executor/remoteserver/RemoteServerExecutor.java +50 -3
  22. data/src/main/java/org/embulk/executor/remoteserver/ServerSession.java +1 -1
  23. data/src/main/java/org/embulk/executor/remoteserver/TLSConfig.java +74 -0
  24. data/src/test/java/org/embulk/executor/remoteserver/TestRemoteServerExecutor.java +42 -25
  25. data/src/test/resources/config/exec_base.yml +5 -0
  26. data/src/test/resources/config/exec_tls.yml +13 -0
  27. data/src/test/resources/config/filter.yml +3 -0
  28. data/src/test/resources/config/input.yml +6 -0
  29. data/src/test/resources/config/output.yml +7 -0
  30. data/test/setup.sh +7 -1
  31. metadata +18 -7
  32. data/docker-compose.yml +0 -24
  33. data/test/Gemfile.lock +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1e201bcff4c6f7ec2cf013c0f8dc02ae9f35bf8
4
- data.tar.gz: 91c0ae41c7453450075e834c5cccffac28b5c0c8
3
+ metadata.gz: 2c835aba3fc2c0ce9c17fa5d9fe70c756097081a
4
+ data.tar.gz: 5831d9e3268af8ef640e1910c0ed1b86eaec88d6
5
5
  SHA512:
6
- metadata.gz: 8f0aa143d8add016ae8cb70303ca782783d476766d7896a321caaa9da015aecf3098c7f5271385644caea74b3be2a9a49e6e62f39c4afd1a3a44fceffd9da848
7
- data.tar.gz: fa81fb681acf7097ef38b5699faa3cfc2777fd14ad12156527db201894d929dad8c4ac3320cd81322416e723bbeef23c300c5d2898477019a5bad1efd12d08e8
6
+ metadata.gz: 46a1920c3c054f3d7b68c05308c38d4ab3ff3d4b6dd940733e29b5917803f2ce7a5a031efa214ebce2a76f8a3eb1929756fe9f7bc5d5bc9bccbcee148497b1ef
7
+ data.tar.gz: 0de6447bb0d0b7756fdb6e2dcc30eeb82a52f589a51e50fc67b1cbfa7308a5bce4e648d4420079cc8711b5c0ec00912f024bbb1508f1b490be64accabac82d63
data/.circleci/config.yml CHANGED
@@ -10,6 +10,7 @@ jobs:
10
10
  - v1-gradle-{{ checksum "gradle/dependency-locks/compileClasspath.lockfile" }}-{{ checksum "gradle/dependency-locks/testCompileClasspath.lockfile" }}
11
11
  - v1-gradle-
12
12
  - run: ./test/setup.sh
13
+ - run: sudo chown -R circleci.circleci tmp/certs
13
14
  - run: ./gradlew check --info
14
15
  - run:
15
16
  name: Save test results
data/.gitignore CHANGED
@@ -12,3 +12,5 @@ build/
12
12
  .project
13
13
  *.iml
14
14
  .bundle
15
+
16
+ test/Gemfile.lock
data/README.md CHANGED
@@ -8,12 +8,19 @@ Embulk executor plugin to run Embulk tasks on remote servers.
8
8
 
9
9
  ### Notes
10
10
  - It's still very experimental version, so might change its spec without notification.
11
- - It might have performance issues or bugs. I would appreciate it if you use this and give me reports/feedback!
11
+ - It might have some issues or bugs. I would appreciate it if you use this and give me reports/feedback!
12
12
 
13
13
  ## Configuration
14
14
 
15
- - **hosts**: List of remote servers (`hostname` or `hostname:port`, default port is `30001`). If not specified, the executor runs as local mode, which start Embulk server on its own process (array of string)
15
+ - **hosts**: List of remote servers (`hostname` or `hostname:port`, default port is `30001`). If not specified, the executor runs as the local mode, which starts an Embulk server on its own process (array of string)
16
16
  - **timeout_seconds**: Timeout seconds of the whole execution (integer, default: `3600`)
17
+ - **tls**: Enable to connect server over TLS (boolean, default: `false`)
18
+ - **key_store_p12**: Information of a P12 file used as a [KeyManager](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/KeyManager.html) to register your client certificate. It would be needed when client auth mode is enabled on Embulk server.
19
+ - **path**: Path of the file (string, required)
20
+ - **password**: Password of the file (string, required)
21
+ - **trust_store_p12**: Information of a P12 file used as a [TrustManager](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManager.html) to register a CA certificate. It would be needed when Embulk server uses unknown CA's certificate.
22
+ - **path**: Path of the file (string, required)
23
+ - **password**: Password of the file (string, required)
17
24
 
18
25
  ## Example
19
26
 
@@ -27,7 +34,7 @@ exec:
27
34
  ```
28
35
 
29
36
  ## Embulk server
30
- The server recieves requests from client (Embulk) and run Embulk tasks, then returns results to client. It communicates with clients via `TCP 30001 port`.
37
+ The server receives requests from client (Embulk) and run Embulk tasks, then returns results to client. It communicates with clients via `TCP 30001 port`.
31
38
 
32
39
  ### Running Embulk server as a Docker container
33
40
  The image is hosted by [DockerHub](https://hub.docker.com/r/kamatama41/embulk-executor-remoteserver).
@@ -37,6 +44,18 @@ You can try running Embulk server by the following command.
37
44
  $ docker run --rm -p 30001:30001 kamatama41/embulk-executor-remoteserver
38
45
  ```
39
46
 
47
+ ### Configure Embulk server
48
+ There are some environment variables to configure the server
49
+
50
+ - `BIND_ADDRESS`: Bind address of the server (default: `0.0.0.0`)
51
+ - `PORT`: Port number to listen (default: `30001`)
52
+ - `ENABLE_TLS`: Try to connect to client via TLS if `true` (default: `false`)
53
+ - `ENABLE_TLS_CLIENT_AUTH`: Require client authentication if `true` (default: `false`)
54
+ - `KEY_P12_PATH`: Path of the P12 file used as a KeyManager
55
+ - `KEY_P12_PASSWORD`: Password of the Key P12 file
56
+ - `TRUST_P12_PATH`: Path of the P12 file used as a TrustManager
57
+ - `TRUST_P12_PASSWORD`: Password of the Trust P12 file
58
+
40
59
  ## Build
41
60
 
42
61
  ```
data/build.gradle CHANGED
@@ -21,20 +21,20 @@ configurations {
21
21
  }
22
22
 
23
23
  dependencies {
24
- compile("com.github.kamatama41:nsocket:0.2.13") {
24
+ compile("com.github.kamatama41:nsocket:0.3.4") {
25
25
  // Use embulk-core's Jackson (2.6.7) instead
26
26
  exclude group: "com.fasterxml.jackson.core", module: "jackson-databind"
27
27
  }
28
28
  // Followings are needed for running EmbulkServer
29
- implementation "org.embulk:embulk-standards:0.9.16"
30
- serverRuntime "org.embulk:embulk-standards:0.9.16"
29
+ implementation "org.embulk:embulk-standards:0.9.17"
30
+ serverRuntime "org.embulk:embulk-standards:0.9.17"
31
31
  implementation "ch.qos.logback:logback-classic:1.2.3"
32
32
  serverRuntime "ch.qos.logback:logback-classic:1.2.3"
33
33
  implementation "ch.qos.logback:logback-core:1.2.3"
34
34
  serverRuntime "ch.qos.logback:logback-core:1.2.3"
35
35
 
36
- testImplementation "org.embulk:embulk-standards:0.9.16"
37
- testImplementation "org.embulk:embulk-test:0.9.16"
36
+ testImplementation "org.embulk:embulk-standards:0.9.17"
37
+ testImplementation "org.embulk:embulk-test:0.9.17"
38
38
  testImplementation "com.github.kamatama41:embulk-test-helpers:0.8.0"
39
39
  testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.2"
40
40
  testRuntime "org.junit.jupiter:junit-jupiter-engine:5.3.2"
@@ -50,7 +50,7 @@ task executableEmbulkServer(type: Jar) {
50
50
  }
51
51
 
52
52
  embulk {
53
- version = "0.9.16"
53
+ version = "0.9.17"
54
54
  category = "executor"
55
55
  name = "docker"
56
56
  authors = ["Shinichi Ishimura"]
@@ -67,4 +67,4 @@ test {
67
67
  release {
68
68
  git { requireBranch = 'master' }
69
69
  }
70
- afterReleaseBuild.dependsOn gemPush
70
+ afterReleaseBuild.dependsOn gemPush
@@ -0,0 +1,49 @@
1
+ version: '3.2'
2
+ services:
3
+ server1: &server
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile
7
+ environment:
8
+ LOG_LEVEL: debug
9
+ ports:
10
+ - "30001:30001"
11
+ volumes:
12
+ - ./tmp/output:/output
13
+ - ./src/test/resources/json:/root/src/test/resources/json
14
+
15
+ server2:
16
+ <<: *server
17
+ ports:
18
+ - "30002:30001"
19
+
20
+ tls-server1: &tls-server
21
+ <<: *server
22
+ environment:
23
+ LOG_LEVEL: debug
24
+ ENABLE_TLS: "true"
25
+ ENABLE_TLS_CLIENT_AUTH: "true"
26
+ KEY_P12_PATH: /certs/embulk-server.local.p12
27
+ KEY_P12_PASSWORD: abcde
28
+ TRUST_P12_PATH: /certs/ca-chain.p12
29
+ TRUST_P12_PASSWORD: p@ssw0rd
30
+ ports:
31
+ - "30003:30001"
32
+ volumes:
33
+ - ./tmp/output:/output
34
+ - ./tmp/certs:/certs
35
+ - ./src/test/resources/json:/root/src/test/resources/json
36
+
37
+ tls-server2:
38
+ <<: *tls-server
39
+ ports:
40
+ - "30004:30001"
41
+
42
+ cert-generator:
43
+ image: kamatama41/test-cert-generator
44
+ volumes:
45
+ - ./tmp/certs:/root/work
46
+ environment:
47
+ SERVER_COMMON_NAME: embulk-server.local
48
+ SERVER_P12_PASSWORD: abcde
49
+ CLIENT_P12_PASSWORD: fghij
data/example/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gem 'embulk'
4
+ gem 'embulk-filter-add_time'
5
+ gem 'embulk-executor-remoteserver'
data/example/README.md ADDED
@@ -0,0 +1,32 @@
1
+ ## How to run
2
+
3
+ Run `embulk bundle`
4
+
5
+ ```sh
6
+ $ embulk bundle --path vendor/bundle
7
+ ```
8
+
9
+ Run `docker-compose up`
10
+
11
+ ```sh
12
+ $ docker-compose -f docker-compse.yml up
13
+ Creating example_remote1_1 ... done
14
+ Attaching to example_remote1_1
15
+ remote1_1 | 15:11:59.045 [main] INFO c.g.kamatama41.nsocket.SocketServer - Starting server..
16
+ ```
17
+
18
+ In another session, run `embulk run`
19
+
20
+ ```sh
21
+ $ embulk run config.yml -b .
22
+ ```
23
+
24
+ So that 2 output files would be generated in `work` directory
25
+
26
+ ```sh
27
+ $ cat work/output_*
28
+ id,name,time
29
+ 1,Scott,2019-04-06 15:12:42.029000 +0000
30
+ id,name,time
31
+ 2,Tiger,2019-04-06 15:12:42.029000 +0000
32
+ ```
@@ -0,0 +1,25 @@
1
+ in:
2
+ type: file
3
+ path_prefix: work/input_
4
+ parser:
5
+ type: json
6
+ columns:
7
+ - {name: id, type: long}
8
+ - {name: name, type: string}
9
+ filters:
10
+ - type: add_time
11
+ to_column:
12
+ name: time
13
+ type: timestamp
14
+ from_value:
15
+ mode: upload_time
16
+ exec:
17
+ type: remoteserver
18
+ hosts:
19
+ - localhost:30001
20
+ out:
21
+ type: file
22
+ path_prefix: work/output_
23
+ file_ext: csv
24
+ formatter:
25
+ type: csv
@@ -0,0 +1,8 @@
1
+ version: '3.2'
2
+ services:
3
+ remote1:
4
+ image: kamatama41/embulk-executor-remoteserver
5
+ ports:
6
+ - "30001:30001"
7
+ volumes:
8
+ - ./work:/root/work
@@ -0,0 +1 @@
1
+ {"id": 1, "name": "Scott"}
@@ -0,0 +1 @@
1
+ {"id": 2, "name": "Tiger"}
data/gradle.properties CHANGED
@@ -1 +1 @@
1
- version=0.2.1
1
+ version=0.3.0
@@ -11,16 +11,13 @@ com.fasterxml.jackson.datatype:jackson-datatype-guava:2.6.7
11
11
  com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
12
12
  com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
13
13
  com.fasterxml.jackson.module:jackson-module-guice:2.6.7
14
- com.github.kamatama41:nsocket:0.2.13
14
+ com.github.kamatama41:nsocket:0.3.4
15
15
  com.google.code.findbugs:annotations:3.0.0
16
16
  com.google.guava:guava:18.0
17
17
  com.google.inject.extensions:guice-multibindings:4.0
18
18
  com.google.inject:guice:4.0
19
19
  com.ibm.icu:icu4j:54.1.1
20
20
  commons-beanutils:commons-beanutils-core:1.8.3
21
- commons-cli:commons-cli:1.3.1
22
- commons-collections:commons-collections:3.2.1
23
- commons-lang:commons-lang:2.4
24
21
  io.airlift:slice:0.9
25
22
  io.netty:netty-buffer:4.0.44.Final
26
23
  io.netty:netty-common:4.0.44.Final
@@ -31,9 +28,8 @@ org.apache.bval:bval-core:0.5
31
28
  org.apache.bval:bval-jsr303:0.5
32
29
  org.apache.commons:commons-compress:1.10
33
30
  org.apache.commons:commons-lang3:3.4
34
- org.apache.velocity:velocity:1.7
35
- org.embulk:embulk-core:0.9.16
36
- org.embulk:embulk-standards:0.9.16
31
+ org.embulk:embulk-core:0.9.17
32
+ org.embulk:embulk-standards:0.9.17
37
33
  org.jruby:jruby-complete:9.1.15.0
38
34
  org.msgpack:msgpack-core:0.8.11
39
35
  org.slf4j:slf4j-api:1.7.25
@@ -12,16 +12,13 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
12
12
  com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
13
13
  com.fasterxml.jackson.module:jackson-module-guice:2.6.7
14
14
  com.github.kamatama41:embulk-test-helpers:0.8.0
15
- com.github.kamatama41:nsocket:0.2.13
15
+ com.github.kamatama41:nsocket:0.3.4
16
16
  com.google.code.findbugs:annotations:3.0.0
17
17
  com.google.guava:guava:18.0
18
18
  com.google.inject.extensions:guice-multibindings:4.0
19
19
  com.google.inject:guice:4.0
20
20
  com.ibm.icu:icu4j:54.1.1
21
21
  commons-beanutils:commons-beanutils-core:1.8.3
22
- commons-cli:commons-cli:1.3.1
23
- commons-collections:commons-collections:3.2.1
24
- commons-lang:commons-lang:2.4
25
22
  io.airlift:slice:0.9
26
23
  io.netty:netty-buffer:4.0.44.Final
27
24
  io.netty:netty-common:4.0.44.Final
@@ -33,11 +30,10 @@ org.apache.bval:bval-core:0.5
33
30
  org.apache.bval:bval-jsr303:0.5
34
31
  org.apache.commons:commons-compress:1.10
35
32
  org.apache.commons:commons-lang3:3.4
36
- org.apache.velocity:velocity:1.7
37
33
  org.apiguardian:apiguardian-api:1.0.0
38
- org.embulk:embulk-core:0.9.16
39
- org.embulk:embulk-standards:0.9.16
40
- org.embulk:embulk-test:0.9.16
34
+ org.embulk:embulk-core:0.9.17
35
+ org.embulk:embulk-standards:0.9.17
36
+ org.embulk:embulk-test:0.9.17
41
37
  org.hamcrest:hamcrest-core:1.3
42
38
  org.hamcrest:hamcrest-library:1.3
43
39
  org.jruby:jruby-complete:9.1.15.0
@@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit;
15
15
  import java.util.concurrent.TimeoutException;
16
16
  import java.util.stream.Collectors;
17
17
 
18
- class ClientSession {
18
+ class ClientSession implements AutoCloseable {
19
19
  private static final Logger log = LoggerFactory.getLogger(ClientSession.class);
20
20
 
21
21
  private final String id;
@@ -78,6 +78,11 @@ class ClientSession {
78
78
  return isFinished;
79
79
  }
80
80
 
81
+ @Override
82
+ public void close() {
83
+ isFinished = true;
84
+ }
85
+
81
86
  synchronized void update(UpdateTaskStateData data) {
82
87
  switch (data.getTaskState()) {
83
88
  case STARTED:
@@ -115,7 +120,7 @@ class ClientSession {
115
120
  throw new TaskExecutionException(message);
116
121
  }
117
122
  } finally {
118
- isFinished = true;
123
+ close();
119
124
  }
120
125
  }
121
126
 
@@ -29,8 +29,12 @@ class EmbulkClient extends SocketClient implements AutoCloseable {
29
29
 
30
30
  static EmbulkClient open(
31
31
  ClientSession session,
32
- List<Host> hosts) throws IOException {
32
+ List<Host> hosts,
33
+ TLSConfig tlsConfig) throws IOException {
33
34
  EmbulkClient client = new EmbulkClient(session);
35
+ if (tlsConfig != null) {
36
+ client.setSslContext(tlsConfig.getSSLContext());
37
+ }
34
38
  client.open();
35
39
 
36
40
  for (Host host : hosts) {
@@ -90,6 +94,7 @@ class EmbulkClient extends SocketClient implements AutoCloseable {
90
94
  for (Connection connection : getActiveConnections()) {
91
95
  connection.sendSyncCommand(RemoveSessionCommand.ID, session.getId());
92
96
  }
97
+ session.close();
93
98
  super.close();
94
99
  }
95
100
 
@@ -99,7 +104,7 @@ class EmbulkClient extends SocketClient implements AutoCloseable {
99
104
  if(!session.isFinished()) {
100
105
  try {
101
106
  // Try reconnecting
102
- Connection newConnection = reconnect(connection);
107
+ Connection newConnection = reconnect(connection, i -> !session.isFinished());
103
108
  newConnection.sendSyncCommand(InitializeSessionCommand.ID, toInitializeSessionData(session));
104
109
  } catch (IOException e) {
105
110
  log.warn(String.format("A connection to %s could not be reconnected.", connection.getRemoteSocketAddress()), e);
@@ -11,7 +11,7 @@ public class EmbulkServer implements AutoCloseable {
11
11
  this.server = server;
12
12
  }
13
13
 
14
- static EmbulkServer start(String host, int port, int numOfWorkers) throws IOException {
14
+ static EmbulkServer start(String host, int port, int numOfWorkers, TLSConfig tlsConfig) throws IOException {
15
15
  SocketServer server = new SocketServer();
16
16
  ServerSessionRegistry sessionRegistry = new ServerSessionRegistry();
17
17
  server.setHost(host);
@@ -21,6 +21,12 @@ public class EmbulkServer implements AutoCloseable {
21
21
  server.registerSyncCommand(new InitializeSessionCommand(sessionRegistry));
22
22
  server.registerSyncCommand(new RemoveSessionCommand(sessionRegistry));
23
23
  server.registerCommand(new StartTaskCommand(sessionRegistry));
24
+ if (tlsConfig != null) {
25
+ server.setSslContext(tlsConfig.getSSLContext());
26
+ if (tlsConfig.isEnableClientAuth()) {
27
+ server.enableSslClientAuth();
28
+ }
29
+ }
24
30
  server.start();
25
31
  return new EmbulkServer(server);
26
32
  }
@@ -12,13 +12,39 @@ public class Launcher {
12
12
  Map<String, String> envVars = System.getenv();
13
13
  String host = envVars.getOrDefault("BIND_ADDRESS", "0.0.0.0");
14
14
  int port = Integer.parseInt(envVars.getOrDefault("PORT", "30001"));
15
- int numOfWorkers = Integer.parseInt(envVars.getOrDefault("NUM_OF_WORKERS", "1"));
16
- Level logLevel = Level.toLevel(envVars.getOrDefault("LOG_LEVEL", "info"));
17
- configureLogLevel(logLevel);
18
- EmbulkServer.start(host, port, numOfWorkers);
15
+ int numOfWorkers = Integer.parseInt(envVars.getOrDefault("NUM_OF_WORKERS", "5"));
16
+ TLSConfig tlsConfig = createTLSConfig(envVars);
17
+ configureLogLevel(envVars);
18
+
19
+ EmbulkServer.start(host, port, numOfWorkers, tlsConfig);
20
+ }
21
+
22
+ private static TLSConfig createTLSConfig(Map<String, String> envVars) {
23
+ if (!"true".equals(envVars.get("ENABLE_TLS"))) {
24
+ return null;
25
+ }
26
+
27
+ TLSConfig tlsConfig = new TLSConfig();
28
+ String keyP12Path = envVars.get("KEY_P12_PATH");
29
+ String keyP12Password = envVars.get("KEY_P12_PASSWORD");
30
+ if (keyP12Path != null && keyP12Password != null) {
31
+ tlsConfig.keyStore(new P12File(keyP12Path, keyP12Password));
32
+ }
33
+
34
+ String trustP12Path = envVars.get("TRUST_P12_PATH");
35
+ String trustP12Password = envVars.get("TRUST_P12_PASSWORD");
36
+ if (trustP12Path != null && trustP12Password != null) {
37
+ tlsConfig.trustStore(new P12File(trustP12Path, trustP12Password));
38
+ }
39
+
40
+ if ("true".equals(envVars.get("ENABLE_TLS_CLIENT_AUTH"))) {
41
+ tlsConfig.enableClientAuth(true);
42
+ }
43
+ return tlsConfig;
19
44
  }
20
45
 
21
- private static void configureLogLevel(Level logLevel) {
46
+ private static void configureLogLevel(Map<String, String> envVars) {
47
+ Level logLevel = Level.toLevel(envVars.getOrDefault("LOG_LEVEL", "info"));
22
48
  Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
23
49
  rootLogger.setLevel(logLevel);
24
50
  }
@@ -0,0 +1,28 @@
1
+ package org.embulk.executor.remoteserver;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonCreator;
4
+ import com.fasterxml.jackson.annotation.JsonProperty;
5
+
6
+ class P12File {
7
+ private final String path;
8
+ private final String password;
9
+
10
+ @JsonCreator
11
+ P12File(@JsonProperty("path") String path, @JsonProperty("password") String password) {
12
+ if (path == null || password == null) {
13
+ throw new NullPointerException("Path and password must not be null");
14
+ }
15
+ this.path = path;
16
+ this.password = password;
17
+ }
18
+
19
+ @JsonProperty
20
+ String getPath() {
21
+ return path;
22
+ }
23
+
24
+ @JsonProperty
25
+ String getPassword() {
26
+ return password;
27
+ }
28
+ }
@@ -24,6 +24,7 @@ import java.io.UncheckedIOException;
24
24
  import java.nio.file.Files;
25
25
  import java.util.Collections;
26
26
  import java.util.List;
27
+ import java.util.Optional;
27
28
  import java.util.concurrent.TimeoutException;
28
29
  import java.util.stream.Collectors;
29
30
 
@@ -42,8 +43,33 @@ public class RemoteServerExecutor implements ExecutorPlugin {
42
43
  @ConfigDefault("3600")
43
44
  int getTimeoutSeconds();
44
45
 
46
+ @Config("tls")
47
+ @ConfigDefault("false")
48
+ boolean getTLS();
49
+
50
+ @Config("key_store_p12")
51
+ @ConfigDefault("null")
52
+ Optional<P12File> getKeyStoreP12();
53
+
54
+ @Config("trust_store_p12")
55
+ @ConfigDefault("null")
56
+ Optional<P12File> getTrustStoreP12();
57
+
45
58
  @ConfigInject
46
59
  ModelManager getModelManager();
60
+
61
+ // Used for the local mode (mainly for testing)
62
+ @Config("__server_key_store_p12")
63
+ @ConfigDefault("null")
64
+ Optional<P12File> getServerKeyStoreP12();
65
+
66
+ @Config("__server_trust_store_p12")
67
+ @ConfigDefault("null")
68
+ Optional<P12File> getServerTrustStoreP12();
69
+
70
+ @Config("__server_enable_client_auth")
71
+ @ConfigDefault("false")
72
+ boolean getServerEnableClientAuth();
47
73
  }
48
74
 
49
75
  @Inject
@@ -56,8 +82,8 @@ public class RemoteServerExecutor implements ExecutorPlugin {
56
82
  public void transaction(ConfigSource config, Schema outputSchema, int inputTaskCount, Control control) {
57
83
  PluginTask task = config.loadConfig(PluginTask.class);
58
84
  if (task.getHosts().isEmpty()) {
59
- log.info("Hosts is empty. Run with a local server.");
60
- try (EmbulkServer _autoclosed = EmbulkServer.start(DEFAULT_HOST.getName(), DEFAULT_HOST.getPort(), 1)) {
85
+ log.info("Hosts is empty. Run as the local mode.");
86
+ try (EmbulkServer _autoclosed = startEmbulkServer(task)) {
61
87
  control.transaction(outputSchema, inputTaskCount, new ExecutorImpl(inputTaskCount, task, Collections.singletonList(DEFAULT_HOST)));
62
88
  } catch (IOException e) {
63
89
  throw new UncheckedIOException(e);
@@ -103,7 +129,15 @@ public class RemoteServerExecutor implements ExecutorPlugin {
103
129
 
104
130
  ClientSession session = new ClientSession(
105
131
  systemConfigJson, pluginTaskJson, processTaskJson, gemSpecs, pluginArchiveBytes, state, inputTaskCount, modelManager);
106
- try (EmbulkClient client = EmbulkClient.open(session, hosts)) {
132
+
133
+ TLSConfig tlsConfig = null;
134
+ if (pluginTask.getTLS()) {
135
+ tlsConfig = new TLSConfig();
136
+ pluginTask.getKeyStoreP12().ifPresent(tlsConfig::keyStore);
137
+ pluginTask.getTrustStoreP12().ifPresent(tlsConfig::trustStore);
138
+ }
139
+
140
+ try (EmbulkClient client = EmbulkClient.open(session, hosts, tlsConfig)) {
107
141
  client.createSession();
108
142
 
109
143
  state.initialize(inputTaskCount, inputTaskCount);
@@ -132,4 +166,17 @@ public class RemoteServerExecutor implements ExecutorPlugin {
132
166
  }
133
167
  }
134
168
  }
169
+
170
+ private EmbulkServer startEmbulkServer(PluginTask task) throws IOException {
171
+ TLSConfig tlsConfig = null;
172
+ if (task.getTLS()) {
173
+ tlsConfig = new TLSConfig();
174
+ task.getServerKeyStoreP12().ifPresent(tlsConfig::keyStore);
175
+ task.getServerTrustStoreP12().ifPresent(tlsConfig::trustStore);
176
+ if (task.getServerEnableClientAuth()) {
177
+ tlsConfig.enableClientAuth(true);
178
+ }
179
+ }
180
+ return EmbulkServer.start(DEFAULT_HOST.getName(), DEFAULT_HOST.getPort(), 1, tlsConfig);
181
+ }
135
182
  }
@@ -173,7 +173,7 @@ class ServerSession implements AutoCloseable {
173
173
 
174
174
  @Override
175
175
  public void close() {
176
- log.debug("Closing the session {}", id);
176
+ log.info("Closing the session {}", id);
177
177
  sessionRunner.shutdownNow();
178
178
  }
179
179
  }
@@ -0,0 +1,74 @@
1
+ package org.embulk.executor.remoteserver;
2
+
3
+ import javax.net.ssl.KeyManager;
4
+ import javax.net.ssl.KeyManagerFactory;
5
+ import javax.net.ssl.SSLContext;
6
+ import javax.net.ssl.TrustManager;
7
+ import javax.net.ssl.TrustManagerFactory;
8
+ import java.io.FileInputStream;
9
+ import java.io.InputStream;
10
+ import java.security.KeyStore;
11
+
12
+ class TLSConfig {
13
+ private P12File keyStore = null;
14
+ private P12File trustStore = null;
15
+ private boolean enableClientAuth = false;
16
+
17
+ TLSConfig() {
18
+ }
19
+
20
+ TLSConfig keyStore(P12File keyStore) {
21
+ this.keyStore = keyStore;
22
+ return this;
23
+ }
24
+
25
+ TLSConfig trustStore(P12File trustStore) {
26
+ this.trustStore = trustStore;
27
+ return this;
28
+ }
29
+
30
+ TLSConfig enableClientAuth(boolean enableClientAuth) {
31
+ this.enableClientAuth = enableClientAuth;
32
+ return this;
33
+ }
34
+
35
+ SSLContext getSSLContext() {
36
+ try {
37
+ KeyManager[] keyManagers = null;
38
+ if (keyStore != null) {
39
+ KeyStore ks = load(keyStore);
40
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
41
+ kmf.init(ks, keyStore.getPassword().toCharArray());
42
+ keyManagers = kmf.getKeyManagers();
43
+ }
44
+
45
+ TrustManager[] trustManagers = null;
46
+ if (trustStore != null) {
47
+ KeyStore ts = load(trustStore);
48
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
49
+ tmf.init(ts);
50
+ trustManagers = tmf.getTrustManagers();
51
+ }
52
+
53
+ SSLContext context = SSLContext.getInstance("TLS");
54
+ context.init(keyManagers, trustManagers, null);
55
+ return context;
56
+ } catch (Exception e) {
57
+ throw new RuntimeException(e);
58
+ }
59
+ }
60
+
61
+ boolean isEnableClientAuth() {
62
+ return enableClientAuth;
63
+ }
64
+
65
+ private static KeyStore load(P12File file) {
66
+ try (InputStream keyStoreIS = new FileInputStream(file.getPath())) {
67
+ KeyStore ks = KeyStore.getInstance("PKCS12");
68
+ ks.load(keyStoreIS, file.getPassword().toCharArray());
69
+ return ks;
70
+ } catch (Exception e) {
71
+ throw new RuntimeException(e);
72
+ }
73
+ }
74
+ }
@@ -3,6 +3,7 @@ package org.embulk.executor.remoteserver;
3
3
  import org.embulk.config.ConfigSource;
4
4
  import org.embulk.test.EmbulkPluginTest;
5
5
  import org.embulk.test.EmbulkTest;
6
+ import org.junit.jupiter.api.BeforeEach;
6
7
  import org.junit.jupiter.api.Test;
7
8
 
8
9
  import java.io.File;
@@ -12,49 +13,65 @@ import java.nio.file.Files;
12
13
  import java.nio.file.Path;
13
14
  import java.nio.file.Paths;
14
15
  import java.util.Arrays;
15
- import java.util.Collections;
16
16
  import java.util.HashSet;
17
- import java.util.List;
18
17
  import java.util.Set;
19
18
  import java.util.stream.Collectors;
20
19
 
20
+ import static org.embulk.test.Utils.configFromResource;
21
21
  import static org.junit.jupiter.api.Assertions.assertEquals;
22
22
 
23
23
  @EmbulkTest(value = RemoteServerExecutor.class, name = "remoteserver")
24
24
  class TestRemoteServerExecutor extends EmbulkPluginTest {
25
- private static final List<String> HOSTS = Arrays.asList("localhost", "localhost:30002");
26
25
  private static final Path OUTPUT_DIR = Paths.get("tmp", "output");
27
26
  private static final Path TEST_DIR = Paths.get("test");
28
27
 
28
+ @BeforeEach
29
+ void cleanupOutputDir() {
30
+ File outputDir = OUTPUT_DIR.toFile();
31
+ if (outputDir.exists()) {
32
+ Arrays.stream(outputDir.listFiles()).forEach(File::delete);
33
+ }
34
+ }
35
+
29
36
  @Test
30
37
  void testSimpleCase() {
31
38
  setSystemConfig(config().set("jruby_global_bundler_plugin_source_directory", TEST_DIR.toFile().getAbsolutePath()));
32
39
 
33
- ConfigSource inConfig = config().set("type", "file")
34
- .set("path_prefix", "src/test/resources/json/test")
35
- .set("parser", config().set("type", "json")
36
- .set("columns", Collections.singletonList(config()
37
- .set("name", "a").set("type", "long")
38
- ))
39
- );
40
+ ConfigSource inConfig = configFromResource("config/input.yml");
41
+ ConfigSource execConfig = configFromResource("config/exec_base.yml");
42
+ ConfigSource filterConfig = configFromResource("config/filter.yml");
43
+ ConfigSource outConfig = configFromResource("config/output.yml");
40
44
 
41
- ConfigSource execConfig = config().set("type", "remoteserver")
42
- .set("hosts", HOSTS)
43
- .set("timeout_seconds", 5);
45
+ runConfig(inConfig)
46
+ .execConfig(execConfig)
47
+ .filterConfig(filterConfig)
48
+ .outConfig(outConfig).run();
44
49
 
45
- ConfigSource filterConfig = config().set("type", "hash")
46
- .set("columns", Collections.singletonList(config()
47
- .set("name", "a").set("algorithm", "MD5")
48
- ));
50
+ File[] files = OUTPUT_DIR.toFile().listFiles();
51
+ assertEquals(2, files.length);
52
+ Set<String> outputs = Arrays.stream(files).map(f -> {
53
+ try {
54
+ return String.join("", Files.readAllLines(f.toPath()));
55
+ } catch (IOException e) {
56
+ throw new UncheckedIOException(e);
57
+ }
58
+ }).collect(Collectors.toSet());
59
+
60
+ Set<String> expected = new HashSet<String>(){{
61
+ add("c4ca4238a0b923820dcc509a6f75849b"); // "1" of MD5
62
+ add("c81e728d9d4c2f636f067f89cc14862c"); // "2" of MD5
63
+ }};
64
+ assertEquals(expected, outputs);
65
+ }
66
+
67
+ @Test
68
+ void testConnectWithTLS() {
69
+ setSystemConfig(config().set("jruby_global_bundler_plugin_source_directory", TEST_DIR.toFile().getAbsolutePath()));
49
70
 
50
- ConfigSource outConfig = config().set("type", "file")
51
- .set("path_prefix", "/output/out_file_")
52
- .set("file_ext", "json")
53
- .set("formatter", config()
54
- .set("type", "csv")
55
- .set("header_line", false)
56
- .set("quote_policy", "NONE")
57
- );
71
+ ConfigSource inConfig = configFromResource("config/input.yml");
72
+ ConfigSource execConfig = configFromResource("config/exec_tls.yml");
73
+ ConfigSource filterConfig = configFromResource("config/filter.yml");
74
+ ConfigSource outConfig = configFromResource("config/output.yml");
58
75
 
59
76
  runConfig(inConfig)
60
77
  .execConfig(execConfig)
@@ -0,0 +1,5 @@
1
+ type: remoteserver
2
+ timeout_seconds: 5
3
+ hosts:
4
+ - localhost
5
+ - localhost:30002
@@ -0,0 +1,13 @@
1
+ type: remoteserver
2
+ timeout_seconds: 5
3
+ hosts:
4
+ - localhost:30003
5
+ - localhost:30004
6
+ tls: true
7
+ key_store_p12:
8
+ path: tmp/certs/client.p12
9
+ password: fghij
10
+ trust_store_p12:
11
+ path: tmp/certs/ca-chain.p12
12
+ password: p@ssw0rd
13
+
@@ -0,0 +1,3 @@
1
+ type: hash
2
+ columns:
3
+ - {name: a, algorithm: MD5}
@@ -0,0 +1,6 @@
1
+ type: file
2
+ path_prefix: src/test/resources/json/test
3
+ parser:
4
+ type: json
5
+ columns:
6
+ - {name: a, type: long}
@@ -0,0 +1,7 @@
1
+ type: file
2
+ path_prefix: /output/out_file_
3
+ file_ext: csv
4
+ formatter:
5
+ type: csv
6
+ header_line: false
7
+ quote_policy: NONE
data/test/setup.sh CHANGED
@@ -4,5 +4,11 @@ project_root="$(cd $(dirname $0)/..; pwd -P)"
4
4
  cd ${project_root}
5
5
 
6
6
  rm -rf tmp/output && mkdir -p tmp/output
7
+ rm -rf tmp/certs && mkdir -p tmp/certs
7
8
  ./gradlew embulk_bundle_--clean -Pgemfile=test/Gemfile -PbundlePath=${project_root}/tmp/vendor/bundle
8
- docker-compose up -d --build
9
+
10
+ if [[ "${SKIP_DOCKER_BUILD}" != "true" ]]; then
11
+ docker-compose -f docker-compose.test.yml build
12
+ fi
13
+ docker-compose -f docker-compose.test.yml run cert-generator
14
+ docker-compose -f docker-compose.test.yml up -d server1 server2 tls-server1 tls-server2
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-executor-remoteserver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
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: 2019-04-06 00:00:00.000000000 Z
11
+ date: 2019-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -51,12 +51,18 @@ files:
51
51
  - LICENSE
52
52
  - README.md
53
53
  - build.gradle
54
- - classpath/embulk-executor-remoteserver-0.2.1.jar
54
+ - classpath/embulk-executor-remoteserver-0.3.0.jar
55
55
  - classpath/msgpack-core-0.8.16.jar
56
- - classpath/nsocket-0.2.13.jar
56
+ - classpath/nsocket-0.3.4.jar
57
57
  - classpath/slf4j-api-1.7.26.jar
58
- - docker-compose.yml
58
+ - docker-compose.test.yml
59
59
  - docker/run_embulk_server.sh
60
+ - example/Gemfile
61
+ - example/README.md
62
+ - example/config.yml
63
+ - example/docker-compse.yml
64
+ - example/work/input_1.json
65
+ - example/work/input_2.json
60
66
  - gradle.properties
61
67
  - gradle/dependency-locks/compileClasspath.lockfile
62
68
  - gradle/dependency-locks/testCompileClasspath.lockfile
@@ -72,22 +78,28 @@ files:
72
78
  - src/main/java/org/embulk/executor/remoteserver/Host.java
73
79
  - src/main/java/org/embulk/executor/remoteserver/InitializeSessionCommand.java
74
80
  - src/main/java/org/embulk/executor/remoteserver/Launcher.java
81
+ - src/main/java/org/embulk/executor/remoteserver/P12File.java
75
82
  - src/main/java/org/embulk/executor/remoteserver/PluginArchive.java
76
83
  - src/main/java/org/embulk/executor/remoteserver/RemoteServerExecutor.java
77
84
  - src/main/java/org/embulk/executor/remoteserver/RemoveSessionCommand.java
78
85
  - src/main/java/org/embulk/executor/remoteserver/ServerSession.java
79
86
  - src/main/java/org/embulk/executor/remoteserver/ServerSessionRegistry.java
80
87
  - src/main/java/org/embulk/executor/remoteserver/StartTaskCommand.java
88
+ - src/main/java/org/embulk/executor/remoteserver/TLSConfig.java
81
89
  - src/main/java/org/embulk/executor/remoteserver/TaskExecutionException.java
82
90
  - src/main/java/org/embulk/executor/remoteserver/TaskState.java
83
91
  - src/main/java/org/embulk/executor/remoteserver/UpdateTaskStateCommand.java
84
92
  - src/main/java/org/embulk/executor/remoteserver/UpdateTaskStateData.java
85
93
  - src/main/resources/logback.xml
86
94
  - src/test/java/org/embulk/executor/remoteserver/TestRemoteServerExecutor.java
95
+ - src/test/resources/config/exec_base.yml
96
+ - src/test/resources/config/exec_tls.yml
97
+ - src/test/resources/config/filter.yml
98
+ - src/test/resources/config/input.yml
99
+ - src/test/resources/config/output.yml
87
100
  - src/test/resources/json/test1.json
88
101
  - src/test/resources/json/test2.json
89
102
  - test/Gemfile
90
- - test/Gemfile.lock
91
103
  - test/setup.sh
92
104
  homepage: https://github.com/kamatama41/embulk-executor-remoteserver
93
105
  licenses:
@@ -115,5 +127,4 @@ specification_version: 4
115
127
  summary: Docker executor plugin for Embulk
116
128
  test_files:
117
129
  - test/Gemfile
118
- - test/Gemfile.lock
119
130
  - test/setup.sh
data/docker-compose.yml DELETED
@@ -1,24 +0,0 @@
1
- version: '3.2'
2
- services:
3
- server1:
4
- build:
5
- context: .
6
- dockerfile: Dockerfile
7
- environment:
8
- LOG_LEVEL: debug
9
- ports:
10
- - "30001:30001"
11
- volumes:
12
- - ./tmp/output:/output
13
- - ./src/test/resources/json:/root/src/test/resources/json
14
- server2:
15
- build:
16
- context: .
17
- dockerfile: Dockerfile
18
- environment:
19
- LOG_LEVEL: debug
20
- ports:
21
- - "30002:30001"
22
- volumes:
23
- - ./tmp/output:/output
24
- - ./src/test/resources/json:/root/src/test/resources/json
data/test/Gemfile.lock DELETED
@@ -1,20 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- embulk (0.9.16-java)
5
- bundler (>= 1.10.6)
6
- liquid (~> 4.0.0)
7
- msgpack (~> 1.1.0)
8
- embulk-filter-hash (0.5.0)
9
- liquid (4.0.0)
10
- msgpack (1.1.0-java)
11
-
12
- PLATFORMS
13
- java
14
-
15
- DEPENDENCIES
16
- embulk
17
- embulk-filter-hash (= 0.5.0)
18
-
19
- BUNDLED WITH
20
- 1.16.0