embulk-executor-remoteserver 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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