initial commit of module launcher
This commit is contained in:
1
pom.xml
1
pom.xml
@@ -29,6 +29,7 @@
|
||||
<module>spring-cloud-stream</module>
|
||||
<module>spring-cloud-stream-binders</module>
|
||||
<module>spring-cloud-stream-codec</module>
|
||||
<module>spring-cloud-stream-module-launcher</module>
|
||||
<module>spring-cloud-stream-samples</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
|
||||
1
spring-cloud-stream-module-launcher/.gitignore
vendored
Normal file
1
spring-cloud-stream-module-launcher/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
artifacts
|
||||
7
spring-cloud-stream-module-launcher/Dockerfile
Normal file
7
spring-cloud-stream-module-launcher/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM java:8
|
||||
VOLUME /tmp
|
||||
RUN mkdir -p /opt/spring/modules
|
||||
ADD artifacts/modules /opt/spring/modules
|
||||
ADD target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT-exec.jar module-launcher.jar
|
||||
RUN bash -c 'touch /module-launcher.jar'
|
||||
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/module-launcher.jar"]
|
||||
91
spring-cloud-stream-module-launcher/README.md
Normal file
91
spring-cloud-stream-module-launcher/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Module Launcher
|
||||
|
||||
The Module Launcher provides a single entry point that bootstraps module JARs located in a home directory. A single Docker image that contains such a directory of module JARs can then be used to launch any of those JARs based on an environment variable. When running standalone, a system property may be used instead of an environment variable, so that multiple instances of the Module Launcher may run on a single machine. The following examples demonstrate running the modules for the *ticktock* stream (`time | log`).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1: clone and build the spring-cloud-stream project:
|
||||
|
||||
````
|
||||
git clone https://github.com/spring-cloud/spring-cloud-stream.git
|
||||
cd spring-cloud-stream
|
||||
mvn -s .settings.xml package
|
||||
cd ..
|
||||
````
|
||||
|
||||
2: copy the spring-cloud-stream source and sink sample JARs to `/opt/spring/modules`:
|
||||
|
||||
````
|
||||
mkdir -p /opt/spring/modules
|
||||
cp spring-cloud-stream/spring-cloud-stream-samples/source/target/spring-cloud-stream-sample-source-1.0.0.BUILD-SNAPSHOT-exec.jar /opt/spring/modules/time.jar
|
||||
cp spring-cloud-stream/spring-cloud-stream-samples/sink/target/spring-cloud-stream-sample-sink-1.0.0.BUILD-SNAPSHOT-exec.jar /opt/spring/modules/log.jar
|
||||
````
|
||||
|
||||
3: start redis locally via `redis-server` (optionally start `redis-cli` and use the `MONITOR` command to watch activity)
|
||||
|
||||
## Running Standalone
|
||||
|
||||
From the `spring-cloud-stream/spring-cloud-stream-module-launcher` directory:
|
||||
|
||||
````
|
||||
java -Dmodules=time -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT-exec.jar
|
||||
java -Dmodules=log -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT-exec.jar
|
||||
````
|
||||
|
||||
The time messages will be emitted every 5 seconds. The console for the log module will display each:
|
||||
|
||||
````
|
||||
2015-06-05 12:39:58.896 INFO 51078 --- [hannel-adapter1] config.ModuleDefinition : Received: 2015-06-05 12:39:58
|
||||
2015-06-05 12:40:02.699 INFO 51078 --- [hannel-adapter1] config.ModuleDefinition : Received: 2015-06-05 16:39:52
|
||||
2015-06-05 12:40:03.897 INFO 51078 --- [hannel-adapter1] config.ModuleDefinition : Received: 2015-06-05 12:40:03
|
||||
````
|
||||
|
||||
*NOTE:* the two modules will be launched within a single process if both are specified: `-Dmodules=time,log`
|
||||
|
||||
## Running with Docker
|
||||
|
||||
1: build the module-launcher Docker image, including a copy of the module directory:
|
||||
|
||||
From the `spring-cloud-stream/spring-cloud-stream-module-launcher` directory:
|
||||
|
||||
````
|
||||
mkdir artifacts
|
||||
cp -r /opt/spring/modules artifacts/
|
||||
./dockerize.sh
|
||||
````
|
||||
|
||||
2: run each module as a docker process by passing environment variables for the module name as well as the host machine's IP address for the redis connection to be established within the container:
|
||||
|
||||
````
|
||||
docker run -p 8080:8080 -e MODULES=time -e SPRING_REDIS_HOST=<host.ip> 192.168.59.103:5000/module-launcher
|
||||
docker run -p 8081:8081 -e MODULES=log -e SPRING_REDIS_HOST=<host.ip> 192.168.59.103:5000/module-launcher
|
||||
````
|
||||
|
||||
## Running on Lattice
|
||||
|
||||
### Initial Setup (if necessary)
|
||||
|
||||
1: Launch lattice with vagrant as described [here](http://lattice.cf/docs/getting-started/).
|
||||
|
||||
2: Run a private Docker registry, and configure Lattice to use that as described [here](http://lattice.cf/docs/private-docker-registry/).
|
||||
|
||||
### Deploying Modules
|
||||
|
||||
1: Push the Docker image to the private registry (if necessary, run `$(boot2docker shellinit)` first):
|
||||
|
||||
````
|
||||
$ docker push 192.168.59.103:5000/module-launcher
|
||||
````
|
||||
|
||||
2: Create a Redis instance on Lattice (running as root):
|
||||
|
||||
````
|
||||
$ ltc create redis redis -r
|
||||
````
|
||||
|
||||
3: Run the modules as long-running processes (LRPs) on Lattice:
|
||||
|
||||
````
|
||||
$ ltc create time 192.168.59.103:5000/module-launcher -e MODULES=time -e SPRING_PROFILES_ACTIVE=cloud
|
||||
$ ltc create log 192.168.59.103:5000/module-launcher -e MODULES=log -e SPRING_PROFILES_ACTIVE=cloud
|
||||
````
|
||||
13
spring-cloud-stream-module-launcher/dockerize.sh
Executable file
13
spring-cloud-stream-module-launcher/dockerize.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -f "Dockerfile" ]; then
|
||||
echo "Dockerfile not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT-exec.jar" ]; then
|
||||
echo "JAR not available; run mvn package first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker build -t 192.168.59.103:5000/module-launcher .
|
||||
45
spring-cloud-stream-module-launcher/pom.xml
Normal file
45
spring-cloud-stream-module-launcher/pom.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-stream-module-launcher</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-stream-module-launcher</name>
|
||||
<description>application for launching spring-cloud-stream modules</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<start-class>org.springframework.cloud.stream.module.launcher.ModuleLauncher</start-class>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.util.AsciiBytes;
|
||||
|
||||
public class ModuleJarLauncher extends ExecutableArchiveLauncher {
|
||||
|
||||
private static final AsciiBytes LIB = new AsciiBytes("lib/");
|
||||
|
||||
public ModuleJarLauncher(Archive archive) {
|
||||
super(archive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||
return !entry.isDirectory() && entry.getName().startsWith(LIB);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||
archives.add(0, getArchive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String[] args) {
|
||||
super.launch(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.module.launcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.springframework.boot.loader.ModuleJarLauncher;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
|
||||
/**
|
||||
* Bootstrap for launching one or more modules. The module path(s), relative to the module home, must be provided via
|
||||
* the "modules" system property or "MODULES" environment variable as a comma-delimited list. The module home directory
|
||||
* itself may be provided via the "module.home" system property or "MODULE_HOME" environment variable. The default
|
||||
* module home directory is: /opt/spring/modules
|
||||
* <p>
|
||||
* To pass args to a module, prefix with the module name and a dot. The arg name will be de-qualified and passed along.
|
||||
* For example: <code>--foo.bar=123</code> becomes <code>--bar=123</code> and is only passed to the 'foo' module.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public class ModuleLauncher {
|
||||
|
||||
private static final String DEFAULT_MODULE_HOME = "/opt/spring/modules";
|
||||
|
||||
private final File moduleHome;
|
||||
|
||||
public ModuleLauncher(File moduleHome) {
|
||||
this.moduleHome = moduleHome;
|
||||
}
|
||||
|
||||
public void launch(String[] modules, String[] args) {
|
||||
Executor executor = Executors.newFixedThreadPool(modules.length);
|
||||
for (String module : modules) {
|
||||
List<String> moduleArgs = new ArrayList<>();
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("--" + module + ".")) {
|
||||
moduleArgs.add("--" + arg.substring(module.length() + 3));
|
||||
}
|
||||
}
|
||||
moduleArgs.add("--spring.jmx.default-domain=" + module.replace("/", "."));
|
||||
executor.execute(new ModuleLaunchTask(moduleHome, module + ".jar", moduleArgs.toArray(new String[moduleArgs.size()])));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String modules = System.getProperty("modules");
|
||||
if (modules == null) {
|
||||
modules = System.getenv("MODULES");
|
||||
}
|
||||
if (modules == null) {
|
||||
System.err.println("Either the 'modules' system property or 'MODULES' environment variable is required.");
|
||||
System.exit(1);
|
||||
}
|
||||
String moduleHome = System.getProperty("module.home");
|
||||
if (moduleHome == null) {
|
||||
moduleHome = System.getenv("MODULE_HOME");
|
||||
}
|
||||
if (moduleHome == null) {
|
||||
moduleHome = DEFAULT_MODULE_HOME;
|
||||
}
|
||||
ModuleLauncher launcher = new ModuleLauncher(new File(moduleHome));
|
||||
launcher.launch(modules.split(","), args);
|
||||
}
|
||||
|
||||
|
||||
private static class ModuleLaunchTask implements Runnable {
|
||||
|
||||
private final File directory;
|
||||
|
||||
private final String module;
|
||||
|
||||
private final String[] args;
|
||||
|
||||
ModuleLaunchTask(File directory, String module, String[] args) {
|
||||
this.directory = directory;
|
||||
this.module = module;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
JarFileArchive jarFileArchive = new JarFileArchive(new File(directory, module));
|
||||
ModuleJarLauncher jarLauncher = new ModuleJarLauncher(jarFileArchive);
|
||||
jarLauncher.launch(args);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user