├── scheduler ├── start-scheduler.sh ├── Dockerfile ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── containersolutions │ └── mesoshelloworld │ └── scheduler │ ├── Main.java │ ├── Configuration.java │ ├── TaskInfoFactory.java │ └── Scheduler.java ├── executor ├── start-executor.sh ├── Dockerfile ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── containersolutions │ └── mesoshelloworld │ └── executor │ └── Executor.java ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── quality.gradle └── spock.gradle ├── settings.gradle ├── gradle.properties ├── .gitignore ├── system-test ├── src │ └── test │ │ ├── resources │ │ └── log4j.xml │ │ └── java │ │ └── com │ │ └── containersolutions │ │ └── mesoshelloworld │ │ └── systemtest │ │ ├── SchedulerContainer.java │ │ ├── HelloWorldResponse.java │ │ ├── DiscoverySystemTest.java │ │ └── DockerContainersUtil.java └── build.gradle ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /scheduler/start-scheduler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | java $JAVA_OPTS -Djava.library.path=/usr/lib -jar /tmp/mesos-hello-world-scheduler.jar $@ -------------------------------------------------------------------------------- /executor/start-executor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | java $JAVA_OPTS -Djava.library.path=/usr/local/lib -jar /tmp/mesos-hello-world-executor.jar $@ -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ContainerSolutions/mesos-hello-world/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mesos-hello-world' 2 | include "scheduler" 3 | include "executor" 4 | include "system-test" 5 | 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1024M -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 2 | 3 | # faster builds: gradle build -x findBugsM 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 30 10:06:18 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # IDEA files 4 | *.i?? 5 | out/ 6 | .idea/ 7 | 8 | #gradle 9 | .gradle/ 10 | build/ 11 | target/ 12 | system-test/*.log 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.war 19 | *.ear 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | # vagrant 25 | .vagrant/ 26 | 27 | *.pem -------------------------------------------------------------------------------- /gradle/quality.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'findbugs' 2 | apply plugin: 'checkstyle' 3 | apply plugin: 'pmd' 4 | 5 | tasks.withType(FindBugs) { 6 | excludeFilter = file("$rootProject.projectDir/config/findbugs/excludeFilter.xml") 7 | } 8 | 9 | checkstyle { 10 | toolVersion = "6.6" 11 | } 12 | 13 | pmd { 14 | toolVersion = "5.1.3" 15 | ruleSets = [ 16 | 'java-basic', 17 | 'java-braces', 18 | 'java-clone', 19 | 'java-codesize', 20 | 'java-finalizers' 21 | ] 22 | } -------------------------------------------------------------------------------- /executor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mesosphere/mesos-slave:0.22.1-1.0.ubuntu1404 2 | 3 | RUN apt-get update && apt-get install -y software-properties-common 4 | 5 | RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \ 6 | add-apt-repository -y ppa:webupd8team/java && \ 7 | apt-get update && \ 8 | apt-get install -y oracle-java8-installer && \ 9 | rm -rf /var/lib/apt/lists/* && \ 10 | rm -rf /var/cache/oracle-jdk8-installer 11 | 12 | ADD ./build/docker/mesos-hello-world-executor.jar /tmp/mesos-hello-world-executor.jar 13 | ADD ./build/docker/start-executor.sh /tmp/start-executor.sh 14 | 15 | ENTRYPOINT ["/tmp/start-executor.sh"] 16 | -------------------------------------------------------------------------------- /scheduler/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mesosphere/mesos:0.22.1-1.0.ubuntu1404 2 | 3 | RUN apt-get update && apt-get install -y software-properties-common 4 | 5 | RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \ 6 | add-apt-repository -y ppa:webupd8team/java && \ 7 | apt-get update && \ 8 | apt-get install -y oracle-java8-installer && \ 9 | rm -rf /var/lib/apt/lists/* && \ 10 | rm -rf /var/cache/oracle-jdk8-installer 11 | 12 | ADD ./build/docker/mesos-hello-world-scheduler.jar /tmp/mesos-hello-world-scheduler.jar 13 | ADD ./build/docker/start-scheduler.sh /tmp/start-scheduler.sh 14 | 15 | ENTRYPOINT ["/tmp/start-scheduler.sh"] 16 | -------------------------------------------------------------------------------- /system-test/src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /system-test/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | repositories { 4 | maven { 5 | url "https://jitpack.io" 6 | } 7 | } 8 | 9 | dependencies { 10 | 11 | compile project(':scheduler') 12 | compile 'com.github.docker-java:docker-java:1.3.0' 13 | compile 'com.mashape.unirest:unirest-java:1.4.5' 14 | compile 'com.jayway.awaitility:awaitility:1.6.3' 15 | 16 | compile 'com.github.ContainerSolutions:mini-mesos:0.4.0' 17 | 18 | compile 'org.slf4j:jcl-over-slf4j:1.7.12' 19 | compile 'org.slf4j:log4j-over-slf4j:1.7.12' 20 | compile 'org.slf4j:jul-to-slf4j:1.7.12' 21 | 22 | compile 'org.slf4j:slf4j-api:1.7.12' 23 | 24 | } 25 | 26 | task systemTest(type: Test) { 27 | testClassesDir = sourceSets.test.output.classesDir 28 | classpath = sourceSets.test.runtimeClasspath 29 | testLogging { 30 | showStandardStreams = true 31 | } 32 | outputs.upToDateWhen { false } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /gradle/spock.gradle: -------------------------------------------------------------------------------- 1 | // used for unit tests 2 | apply plugin: 'groovy' 3 | 4 | def spockVersion = '1.0-groovy-2.4' 5 | def powermockVersion = "1.6.1" 6 | 7 | dependencies { 8 | 9 | testCompile "org.codehaus.groovy:groovy-all:2.4.1" 10 | testCompile "org.spockframework:spock-core:$spockVersion" 11 | 12 | testCompile 'cglib:cglib-nodep:2.2.2' // need to mock classes 13 | 14 | // // useful to mock out statics and final classes in Java. 15 | // testCompile "org.powermock:powermock-module-junit4:$powermockVersion" 16 | // testCompile "org.powermock:powermock-module-junit4-rule:$powermockVersion" 17 | // testCompile "org.powermock:powermock-classloading-xstream:$powermockVersion" 18 | // testCompile "org.powermock:powermock-api-mockito:$powermockVersion" 19 | } 20 | 21 | // for spock to live in test java tree 22 | sourceSets { 23 | test { 24 | groovy { srcDir 'src/test/java' } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /executor/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = "com.containersolutions.mesoshelloworld.executor.Executor" 4 | ext { 5 | imageName = imagePrefix + '/mesos-hello-world-executor' 6 | } 7 | 8 | buildscript { 9 | repositories { 10 | jcenter() 11 | } 12 | } 13 | 14 | repositories { 15 | maven { 16 | url "https://jitpack.io" 17 | } 18 | } 19 | 20 | jar { 21 | baseName = "mesos-hello-world-executor" 22 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } // Include dependencies 23 | manifest { 24 | attributes( 25 | 'Main-Class': mainClassName, 26 | 'Implementation-Version': project.version 27 | ) 28 | } 29 | } 30 | 31 | dependencies { 32 | } 33 | 34 | task taskCopyFilesForDocker(type: Copy) { 35 | dependsOn "copyShellScript", "copyJar" 36 | } 37 | 38 | task copyJar(type: Copy) { 39 | from "build/libs/mesos-hello-world-executor-${project.version}.jar" 40 | into 'build/docker' 41 | rename { String fileName -> 42 | fileName.replace("-${project.version}", "") 43 | } 44 | } 45 | 46 | task copyShellScript(type: Copy) { 47 | from "start-executor.sh" 48 | into 'build/docker' 49 | } 50 | -------------------------------------------------------------------------------- /scheduler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = "com.containersolutions.mesoshelloworld.scheduler.Main" 4 | ext { 5 | imageName = imagePrefix + '/mesos-hello-world-scheduler' 6 | } 7 | 8 | buildscript { 9 | repositories { 10 | jcenter() 11 | } 12 | } 13 | repositories { 14 | maven { 15 | url "https://jitpack.io" 16 | } 17 | } 18 | dependencies { 19 | compile "com.beust:jcommander:1.48" 20 | } 21 | 22 | jar { 23 | baseName = "mesos-hello-world-scheduler" 24 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } // Include dependencies 25 | from { project(":executor").getTasksByName("copyJar", false)[0].outputs.files[0] } // Include executor, just in case 26 | manifest { 27 | attributes( 28 | 'Main-Class': mainClassName, 29 | 'Implementation-Version': project.version 30 | ) 31 | } 32 | } 33 | 34 | task taskCopyFilesForDocker(type: Copy) { 35 | dependsOn "copyShellScript", "copyJar" 36 | } 37 | 38 | task copyJar(type: Copy) { 39 | from "build/libs/mesos-hello-world-scheduler-${project.version}.jar" 40 | into 'build/docker' 41 | rename { String fileName -> 42 | fileName.replace("-${project.version}", "") 43 | } 44 | } 45 | 46 | task copyShellScript(type: Copy) { 47 | from "start-scheduler.sh" 48 | into 'build/docker' 49 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mesos-hello-world 2 | Very simple hello world mesos framework to demonstrate http://github.com/containersolutions/mini-mesos. 3 | 4 | ## NOTE - This project has been integrated into minimesos 5 | 6 | Checkout the [test-framework module in minimesos](https://github.com/ContainerSolutions/minimesos/tree/master/test-framework-docker) 7 | 8 | ## Introduction 9 | This project creates a very simple Apache Mesos framework, with dockerized scheduler and exector containers. 10 | The goal is to use the scheduler to start a number of dummy "webapps" (webservers that show "Hello world" on /) 11 | then test the framework using the mini-mesos project. 12 | 13 | ## Non-linux users 14 | If you are developing on an envrionment other than Linux, then you will need to use a VM. We use docker-machine. 15 | You will then need to export the docker settings. E.g.: 16 | ``` 17 | docker-machine create -d virtualbox --virtualbox-memory 4096 --virtualbox-cpu-count 2 dev 18 | eval "$(docker-machine env dev)" 19 | ``` 20 | To run the system tests, non-linux users will have to route all communications to the docker containers via the VM IP address: 21 | ``` 22 | sudo route -n delete 172.17.0.0/16 $(docker-machine ip dev) ; sudo route -n add 172.17.0.0/16 $(docker-machine ip dev) 23 | ``` 24 | 25 | ## Compiling 26 | To build the project (you will need to build the docker images before you run the system tests) simply run: 27 | ``` 28 | ./gradlew build -x test 29 | ``` 30 | 31 | ## System test 32 | To run the system tests, run: 33 | ``` 34 | ./gradlew :system-test:test 35 | ``` 36 | It is also possible to run the system test in Idea, by setting the JUnit environmental variables to point to your docker daemon. 37 | -------------------------------------------------------------------------------- /system-test/src/test/java/com/containersolutions/mesoshelloworld/systemtest/SchedulerContainer.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.systemtest; 2 | 3 | import com.containersolutions.mesoshelloworld.scheduler.Configuration; 4 | import com.github.dockerjava.api.DockerClient; 5 | import com.github.dockerjava.api.command.CreateContainerCmd; 6 | import com.containersol.minimesos.container.AbstractContainer; 7 | 8 | import java.security.SecureRandom; 9 | import java.util.stream.IntStream; 10 | 11 | /** 12 | * Container for the Scheduler 13 | */ 14 | public class SchedulerContainer extends AbstractContainer { 15 | 16 | public static final String SCHEDULER_IMAGE = "containersol/mesos-hello-world-scheduler"; 17 | 18 | public static final String SCHEDULER_NAME = "hello-world-scheduler"; 19 | 20 | private final String mesosIp; 21 | 22 | protected SchedulerContainer(DockerClient dockerClient, String mesosIp) { 23 | super(dockerClient); 24 | this.mesosIp = mesosIp; 25 | } 26 | 27 | @Override 28 | protected void pullImage() { 29 | dockerClient.pullImageCmd(SCHEDULER_IMAGE); 30 | } 31 | 32 | @Override 33 | protected CreateContainerCmd dockerCommand() { 34 | return dockerClient 35 | .createContainerCmd(SCHEDULER_IMAGE) 36 | .withName(SCHEDULER_NAME + "_" + new SecureRandom().nextInt()) 37 | .withEnv("JAVA_OPTS=-Xms128m -Xmx256m") 38 | .withExtraHosts(IntStream.rangeClosed(1, 3).mapToObj(value -> "slave" + value + ":" + mesosIp).toArray(String[]::new)) 39 | .withCmd(Configuration.MESOS_MASTER, getMesosUrl()); 40 | } 41 | 42 | public String getMesosUrl() { 43 | return mesosIp + ":5050"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /scheduler/src/main/java/com/containersolutions/mesoshelloworld/scheduler/Main.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.scheduler; 2 | 3 | import com.google.protobuf.ByteString; 4 | import org.apache.mesos.MesosSchedulerDriver; 5 | import org.apache.mesos.Protos; 6 | 7 | /** 8 | */ 9 | public class Main { 10 | public static void main(String[] args) throws Exception { 11 | 12 | Configuration configuration = new Configuration(args); 13 | 14 | Protos.FrameworkInfo.Builder frameworkBuilder = Protos.FrameworkInfo.newBuilder() 15 | .setUser("") // Have Mesos fill in the current user. 16 | .setName("Hello world example") 17 | .setCheckpoint(true); 18 | 19 | String principal = configuration.getFrameworkPrincipal(); 20 | if( principal != null ) { 21 | frameworkBuilder.setPrincipal(principal); 22 | } 23 | 24 | org.apache.mesos.Scheduler scheduler = new Scheduler(configuration); 25 | 26 | Protos.FrameworkInfo frameworkInfo = frameworkBuilder.build(); 27 | String mesosMaster = configuration.getMesosMaster(); 28 | 29 | MesosSchedulerDriver driver = 30 | principal != null 31 | ? new MesosSchedulerDriver( 32 | scheduler, 33 | frameworkInfo, 34 | mesosMaster, 35 | Protos.Credential.newBuilder() 36 | .setPrincipal( principal ) 37 | .setSecret(ByteString.copyFromUtf8(configuration.getFrameworkSecret())) 38 | .build() 39 | ) 40 | : new MesosSchedulerDriver( 41 | scheduler, 42 | frameworkInfo, 43 | mesosMaster 44 | ); 45 | 46 | int status = driver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1; 47 | 48 | // Ensure that the driver process terminates. 49 | driver.stop(); 50 | 51 | System.exit(status); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /system-test/src/test/java/com/containersolutions/mesoshelloworld/systemtest/HelloWorldResponse.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.systemtest; 2 | 3 | import com.mashape.unirest.http.Unirest; 4 | import com.mashape.unirest.http.exceptions.UnirestException; 5 | 6 | import java.util.*; 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import static com.jayway.awaitility.Awaitility.await; 11 | 12 | /** 13 | * Response which waits until endpoint is ready 14 | */ 15 | public class HelloWorldResponse { 16 | 17 | private boolean discoverySuccessful; 18 | 19 | public HelloWorldResponse(Set ipAddresses, List ports, long timeout) { 20 | await().atMost(timeout, TimeUnit.SECONDS).until(new TasksCall(ipAddresses, ports)); 21 | } 22 | 23 | public boolean isDiscoverySuccessful() { 24 | return discoverySuccessful; 25 | } 26 | 27 | class TasksCall implements Callable { 28 | 29 | private final Set ipAddresses; 30 | private final List ports; 31 | 32 | public TasksCall(Set ipAddresses, List ports) { 33 | this.ipAddresses = ipAddresses; 34 | this.ports = ports; 35 | } 36 | 37 | @Override 38 | public Boolean call() throws Exception { 39 | 40 | final Set goodHosts = new HashSet<>(ipAddresses.size()); 41 | 42 | ipAddresses.forEach(ip -> { 43 | 44 | if( !goodHosts.contains(ip) ) { 45 | ports.forEach(p -> { 46 | 47 | if (!goodHosts.contains(ip)) { 48 | String url = "http://" + ip + ":" + p; 49 | try { 50 | System.out.println( goodHosts.size() + ". " + url + " => " + Unirest.get(url).asString().getBody()); 51 | goodHosts.add( ip ); 52 | } catch (UnirestException e) { 53 | // do nothing 54 | } 55 | } 56 | }); 57 | 58 | } 59 | 60 | }); 61 | 62 | discoverySuccessful = (goodHosts.size() == ipAddresses.size()); 63 | return discoverySuccessful; 64 | 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /system-test/src/test/java/com/containersolutions/mesoshelloworld/systemtest/DiscoverySystemTest.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.systemtest; 2 | 3 | import com.containersolutions.mesoshelloworld.scheduler.Configuration; 4 | import org.apache.log4j.Logger; 5 | import com.containersol.minimesos.MesosCluster; 6 | import com.containersol.minimesos.mesos.MesosClusterConfig; 7 | import org.junit.AfterClass; 8 | import org.junit.BeforeClass; 9 | import org.junit.ClassRule; 10 | import org.junit.Test; 11 | 12 | import java.util.Arrays; 13 | import java.util.Set; 14 | 15 | import static org.junit.Assert.assertTrue; 16 | 17 | /** 18 | * Tests REST node discovery 19 | */ 20 | public class DiscoverySystemTest { 21 | 22 | public static final Logger LOGGER = Logger.getLogger(DiscoverySystemTest.class); 23 | 24 | protected static final MesosClusterConfig CONFIG = MesosClusterConfig.builder() 25 | .slaveResources(new String[]{"ports(*):[8080-8082]", "ports(*):[8080-8082]", "ports(*):[8080-8082]"}) 26 | .build(); 27 | 28 | @ClassRule 29 | public static final MesosCluster CLUSTER = new MesosCluster(CONFIG); 30 | 31 | @BeforeClass 32 | public static void startScheduler() throws Exception { 33 | 34 | LOGGER.info("Starting Scheduler"); 35 | String ipAddress = CLUSTER.getMesosMasterContainer().getIpAddress(); 36 | SchedulerContainer scheduler = new SchedulerContainer(CONFIG.dockerClient, ipAddress); 37 | 38 | // Cluster now has responsibility to shut down container 39 | CLUSTER.addAndStartContainer(scheduler); 40 | 41 | LOGGER.info("Started Scheduler on " + scheduler.getIpAddress()); 42 | } 43 | 44 | @Test 45 | public void testNodeDiscoveryRest() { 46 | 47 | DockerContainersUtil util = new DockerContainersUtil(CONFIG.dockerClient); 48 | Set ipAddresses = util.getContainers(false).filterByImage(Configuration.DEFAULT_EXECUTOR_IMAGE).getIpAddresses(); 49 | 50 | long timeout = 120; 51 | HelloWorldResponse helloWorldResponse = new HelloWorldResponse( ipAddresses, Arrays.asList(8080, 8081, 8082), timeout ); 52 | assertTrue("Elasticsearch nodes did not discover each other within " + timeout + " seconds", helloWorldResponse.isDiscoverySuccessful()); 53 | } 54 | 55 | @AfterClass 56 | public static void removeExecutors() { 57 | DockerContainersUtil util = new DockerContainersUtil(CONFIG.dockerClient); 58 | util.getContainers(false).filterByImage(Configuration.DEFAULT_EXECUTOR_IMAGE).kill().remove(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /scheduler/src/main/java/com/containersolutions/mesoshelloworld/scheduler/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.scheduler; 2 | 3 | import com.beust.jcommander.IParameterValidator; 4 | import com.beust.jcommander.JCommander; 5 | import com.beust.jcommander.Parameter; 6 | import com.beust.jcommander.ParameterException; 7 | 8 | /** 9 | */ 10 | public class Configuration { 11 | public static final String MESOS_MASTER = "--mesosMaster"; 12 | public static final String EXECUTOR_IMAGE = "--executorImage"; 13 | public static final String DEFAULT_EXECUTOR_IMAGE = "containersol/mesos-hello-world-executor"; 14 | public static final String EXECUTOR_NUMBER = "--executorNumber"; 15 | public static final String EXECUTOR_FORCE_PULL_IMAGE = "--executorForcePullImage"; 16 | @Parameter(names = {MESOS_MASTER}, description = "The Mesos master IP", validateWith = NotEmptyString.class) 17 | private String mesosMaster = ""; 18 | @Parameter(names = {EXECUTOR_IMAGE}, description = "The docker executor image to use.") 19 | private String executorImage = DEFAULT_EXECUTOR_IMAGE; 20 | @Parameter(names = {EXECUTOR_NUMBER}, description = "Number of executors") 21 | private Integer executorNumber = 3; 22 | @Parameter(names = {EXECUTOR_FORCE_PULL_IMAGE}, arity = 1, description = "Option to force pull the executor image.") 23 | private Boolean executorForcePullImage = false; 24 | 25 | @Parameter(names = {"--frameworkPrincipal"}, description = "The principal to authenticate as") 26 | private String frameworkPrincipal = null; 27 | @Parameter(names = {"--frameworkSecret"}, description = "The secret to authenticate with, if authenticating as a principal") 28 | private String frameworkSecret = null; 29 | 30 | public Configuration(String[] args) { 31 | final JCommander jCommander = new JCommander(); 32 | jCommander.addObject(this); 33 | try { 34 | jCommander.parse(args); // Parse command line args into configuration class. 35 | } catch (com.beust.jcommander.ParameterException ex) { 36 | System.out.println(ex); 37 | jCommander.setProgramName("(Options preceded by an asterisk are required)"); 38 | jCommander.usage(); 39 | throw ex; 40 | } 41 | } 42 | 43 | public String getMesosMaster() { 44 | if (mesosMaster.isEmpty()) { 45 | throw new IllegalArgumentException("You must pass the mesos master IP address"); 46 | } 47 | return mesosMaster; 48 | } 49 | 50 | public String getExecutorImage() { 51 | return executorImage; 52 | } 53 | 54 | public Integer getExecutorNumber() { 55 | return executorNumber; 56 | } 57 | 58 | public Boolean getExecutorForcePullImage() { 59 | return executorForcePullImage; 60 | } 61 | 62 | public String getFrameworkPrincipal() { 63 | return frameworkPrincipal; 64 | } 65 | 66 | public String getFrameworkSecret() { 67 | return frameworkSecret; 68 | } 69 | 70 | /** 71 | * Ensures that the string is not empty. Will strip spaces. 72 | */ 73 | public static class NotEmptyString implements IParameterValidator { 74 | @Override 75 | public void validate(String name, String value) throws ParameterException { 76 | if (value.replace(" ", "").isEmpty()) { 77 | throw new ParameterException("Parameter " + name + " cannot be empty"); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /scheduler/src/main/java/com/containersolutions/mesoshelloworld/scheduler/TaskInfoFactory.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.scheduler; 2 | 3 | import org.apache.mesos.Protos.*; 4 | 5 | import java.util.UUID; 6 | 7 | 8 | public class TaskInfoFactory { 9 | 10 | private final Configuration configuration; 11 | 12 | public TaskInfoFactory(Configuration configuration) { 13 | this.configuration = configuration; 14 | } 15 | 16 | TaskInfo newTask(Offer offer, Scheduler.ResourceOffer currentOffer) { 17 | 18 | TaskID taskId = TaskID.newBuilder().setValue(UUID.randomUUID().toString()).build(); 19 | Long port = currentOffer.offerPorts.get(0); 20 | 21 | System.out.println("Launching task " + taskId.getValue() + " for port " + port + 22 | " using offer " + offer.getId().getValue()); 23 | 24 | Value.Range singlePortRange = Value.Range.newBuilder().setBegin(port).setEnd(port).build(); 25 | 26 | TaskInfo task = TaskInfo.newBuilder() 27 | .setName("task " + taskId.getValue()) 28 | .setTaskId(taskId) 29 | .setSlaveId(offer.getSlaveId()) 30 | .addResources(Resource.newBuilder() 31 | .setName("cpus") 32 | .setType(Value.Type.SCALAR) 33 | .setScalar(Value.Scalar.newBuilder().setValue(Scheduler.CPUS_PER_TASK))) 34 | .addResources(Resource.newBuilder() 35 | .setName("mem") 36 | .setType(Value.Type.SCALAR) 37 | .setScalar(Value.Scalar.newBuilder().setValue(Scheduler.MEM_PER_TASK))) 38 | .addResources(Resource.newBuilder() 39 | .setName("ports") 40 | .setType(Value.Type.RANGES) 41 | .setRanges(Value.Ranges.newBuilder().addRange(singlePortRange)) 42 | .build() 43 | ) 44 | .setExecutor(newExecutorInfo(configuration)) 45 | .setDiscovery(newDiscoveryInfo(port.intValue())) 46 | .build(); 47 | 48 | 49 | currentOffer.offerCpus -= Scheduler.CPUS_PER_TASK; 50 | currentOffer.offerMem -= Scheduler.MEM_PER_TASK; 51 | currentOffer.offerPorts.remove(0); 52 | return task; 53 | } 54 | 55 | DiscoveryInfo.Builder newDiscoveryInfo(Integer port) { 56 | DiscoveryInfo.Builder discovery = DiscoveryInfo.newBuilder(); 57 | Ports.Builder discoveryPorts = Ports.newBuilder(); 58 | discoveryPorts.addPorts(0, Port.newBuilder().setNumber(port).setName("port")); 59 | discovery.setPorts(discoveryPorts); 60 | discovery.setVisibility(DiscoveryInfo.Visibility.EXTERNAL); 61 | return discovery; 62 | } 63 | 64 | ExecutorInfo.Builder newExecutorInfo(Configuration configuration) { 65 | 66 | ContainerInfo.DockerInfo.Builder dockerBuilder = ContainerInfo.DockerInfo.newBuilder() 67 | .setNetwork(ContainerInfo.DockerInfo.Network.BRIDGE) 68 | .setImage(configuration.getExecutorImage()) 69 | .setForcePullImage(configuration.getExecutorForcePullImage()); 70 | 71 | return ExecutorInfo.newBuilder() 72 | .setExecutorId(ExecutorID.newBuilder().setValue(UUID.randomUUID().toString())) 73 | .setName("hello-world-executor-" + UUID.randomUUID().toString()) 74 | .setCommand(newCommandInfo(configuration)) 75 | .setContainer(ContainerInfo.newBuilder() 76 | .setType(ContainerInfo.Type.DOCKER) 77 | .setDocker( dockerBuilder ) 78 | .build()); 79 | } 80 | 81 | CommandInfo.Builder newCommandInfo(Configuration configuration) { 82 | return CommandInfo.newBuilder() 83 | .setShell(false) 84 | .setContainer(CommandInfo.ContainerInfo.newBuilder().setImage(configuration.getExecutorImage()).build()); 85 | } 86 | 87 | 88 | } -------------------------------------------------------------------------------- /executor/src/main/java/com/containersolutions/mesoshelloworld/executor/Executor.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.executor; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | import com.sun.net.httpserver.HttpServer; 6 | import org.apache.mesos.ExecutorDriver; 7 | import org.apache.mesos.MesosExecutorDriver; 8 | import org.apache.mesos.Protos.*; 9 | 10 | import java.io.IOException; 11 | import java.io.OutputStream; 12 | import java.net.InetSocketAddress; 13 | 14 | /** 15 | * Adapted from https://github.com/apache/mesos/blob/0.22.1/src/examples/java/TestExecutor.java 16 | */ 17 | public class Executor implements org.apache.mesos.Executor { 18 | private Thread thread; 19 | 20 | public static void main(String[] args) throws Exception { 21 | MesosExecutorDriver driver = new MesosExecutorDriver(new Executor()); 22 | System.exit(driver.run() == Status.DRIVER_STOPPED ? 0 : 1); 23 | } 24 | 25 | @Override 26 | public void registered(ExecutorDriver driver, 27 | ExecutorInfo executorInfo, 28 | FrameworkInfo frameworkInfo, 29 | SlaveInfo slaveInfo) { 30 | System.out.println("Registered executor on " + slaveInfo.getHostname()); 31 | } 32 | 33 | @Override 34 | public void reregistered(ExecutorDriver driver, SlaveInfo executorInfo) { 35 | } 36 | 37 | @Override 38 | public void disconnected(ExecutorDriver driver) { 39 | } 40 | 41 | @Override 42 | public void launchTask(final ExecutorDriver driver, final TaskInfo task) { 43 | thread = new Thread() { 44 | public void run() { 45 | try { 46 | Integer port = task.getDiscovery().getPorts().getPorts(0).getNumber(); 47 | System.out.println("Starting webserver on port " + port); 48 | startWebServer(port); 49 | 50 | TaskStatus status = TaskStatus.newBuilder() 51 | .setTaskId(task.getTaskId()) 52 | .setState(TaskState.TASK_RUNNING).build(); 53 | 54 | driver.sendStatusUpdate(status); 55 | 56 | System.out.println("Running task " + task.getTaskId().getValue()); 57 | } catch (Exception e) { 58 | System.out.println("Unable to start webserver:" + e); 59 | 60 | TaskStatus status = TaskStatus.newBuilder() 61 | .setTaskId(task.getTaskId()) 62 | .setState(TaskState.TASK_FINISHED).build(); 63 | 64 | driver.sendStatusUpdate(status); 65 | } 66 | } 67 | }; 68 | thread.start(); 69 | } 70 | 71 | private void startWebServer(Integer port) throws IOException { 72 | HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); 73 | server.createContext("/", new MyHandler()); 74 | server.setExecutor(null); // creates a default executor 75 | server.start(); 76 | } 77 | 78 | @Override 79 | public void killTask(ExecutorDriver driver, TaskID taskId) { 80 | if (thread != null && thread.isAlive()) { 81 | thread.interrupt(); 82 | } 83 | } 84 | 85 | @Override 86 | public void frameworkMessage(ExecutorDriver driver, byte[] data) { 87 | } 88 | 89 | @Override 90 | public void shutdown(ExecutorDriver driver) { 91 | } 92 | 93 | @Override 94 | public void error(ExecutorDriver driver, String message) { 95 | } 96 | 97 | private class MyHandler implements HttpHandler { 98 | @Override 99 | public void handle(HttpExchange t) throws IOException { 100 | String response = "Hello world"; 101 | t.sendResponseHeaders(200, response.length()); 102 | OutputStream os = t.getResponseBody(); 103 | os.write(response.getBytes()); 104 | os.close(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /system-test/src/test/java/com/containersolutions/mesoshelloworld/systemtest/DockerContainersUtil.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.systemtest; 2 | 3 | 4 | import com.github.dockerjava.api.DockerClient; 5 | import com.github.dockerjava.api.command.InspectContainerResponse; 6 | import com.github.dockerjava.api.model.Container; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | /** 12 | * Immutable utility class, which represents set of docker containers with filters and operations on this list 13 | * TODO: delete this class when minimesos 0.5.0 is released. It will be present there 14 | */ 15 | public class DockerContainersUtil { 16 | 17 | private final DockerClient dockerClient; 18 | private final Set containers; 19 | 20 | public DockerContainersUtil(DockerClient dockerClient) { 21 | this.dockerClient = dockerClient; 22 | this.containers = null; 23 | } 24 | 25 | private DockerContainersUtil(DockerClient dockerClient, Set containers) { 26 | this.dockerClient = dockerClient; 27 | this.containers = containers; 28 | } 29 | 30 | /** 31 | * @param showAll should the list include stopped containers 32 | * @return set of docker containers 33 | */ 34 | public DockerContainersUtil getContainers(boolean showAll) { 35 | Set containers = new HashSet<>(dockerClient.listContainersCmd().withShowAll(showAll).exec()); 36 | return new DockerContainersUtil( dockerClient, containers ); 37 | } 38 | 39 | /** 40 | * Filters the set based on the constainer name 41 | * @param pattern regular expression pattern of the container name 42 | * @return filtered set 43 | */ 44 | public DockerContainersUtil filterByName( String pattern ) { 45 | 46 | if( this.containers == null ) { 47 | return this; 48 | } 49 | 50 | Set matched = new HashSet<>(); 51 | for (Container container : containers) { 52 | String[] names = container.getNames(); 53 | for (String name : names) { 54 | // all names start with '/' 55 | if (name.substring(1).matches(pattern)) { 56 | matched.add(container); 57 | } 58 | } 59 | } 60 | 61 | return new DockerContainersUtil(dockerClient, matched); 62 | } 63 | 64 | /** 65 | * Filters the set based on the constainer name 66 | * @param pattern regular expression pattern of the container name 67 | * @return filtered set 68 | */ 69 | public DockerContainersUtil filterByImage( String pattern ) { 70 | 71 | if( this.containers == null ) { 72 | return this; 73 | } 74 | 75 | Set matched = new HashSet<>(); 76 | for (Container container : containers) { 77 | if( container.getImage().matches(pattern) ) { 78 | matched.add( container ); 79 | } 80 | } 81 | 82 | return new DockerContainersUtil(dockerClient, matched); 83 | } 84 | 85 | /** 86 | * Removes all containers in the util object 87 | */ 88 | public void remove() { 89 | if( containers != null ) { 90 | for (Container container : containers) { 91 | dockerClient.removeContainerCmd(container.getId()).exec(); 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * Removes all containers in the util object 98 | */ 99 | public DockerContainersUtil kill() { 100 | if( containers != null ) { 101 | for (Container container : containers) { 102 | dockerClient.killContainerCmd(container.getId()).exec(); 103 | } 104 | } 105 | return this; 106 | } 107 | 108 | /** 109 | * @return IP addresses of containers 110 | */ 111 | public Set getIpAddresses() { 112 | Set ips = new HashSet<>(); 113 | if( containers != null ) { 114 | for (Container container : containers) { 115 | InspectContainerResponse response = dockerClient.inspectContainerCmd(container.getId()).exec(); 116 | ips.add( response.getNetworkSettings().getIpAddress() ); 117 | } 118 | } 119 | return ips; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /scheduler/src/main/java/com/containersolutions/mesoshelloworld/scheduler/Scheduler.java: -------------------------------------------------------------------------------- 1 | package com.containersolutions.mesoshelloworld.scheduler; 2 | 3 | import org.apache.mesos.Protos.*; 4 | import org.apache.mesos.SchedulerDriver; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * Adapted from: https://github.com/apache/mesos/blob/0.22.1/src/examples/java/TestFramework.java 12 | */ 13 | public class Scheduler implements org.apache.mesos.Scheduler { 14 | 15 | public static final double CPUS_PER_TASK = 0.1; 16 | public static final double MEM_PER_TASK = 128; 17 | 18 | private final Configuration configuration; 19 | 20 | public Scheduler(Configuration configuration) { 21 | this.configuration = configuration; 22 | } 23 | 24 | @Override 25 | public void resourceOffers(SchedulerDriver driver, 26 | List offers) { 27 | 28 | for (Offer offer : offers) { 29 | 30 | ResourceOffer currentOffer = new ResourceOffer(offer.getResourcesList()); 31 | if( currentOffer.isAcceptable() ) { 32 | 33 | System.out.println( 34 | "Received offer " + offer.getId().getValue() + " with cpus: " + currentOffer.offerCpus + 35 | " and mem: " + currentOffer.offerMem + " with ports: " + currentOffer.offerPorts); 36 | 37 | List newTaskList = new ArrayList<>(); 38 | 39 | while (newTaskList.size() < configuration.getExecutorNumber() && currentOffer.isAcceptable()) { 40 | 41 | TaskInfo task = new TaskInfoFactory(configuration).newTask(offer, currentOffer); 42 | newTaskList.add(task); 43 | 44 | } 45 | 46 | Status status = driver.launchTasks(Collections.singletonList(offer.getId()), newTaskList); 47 | System.out.println(String.format("Launched %d tasks. Status is %s", newTaskList.size(), status.toString())); 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | @Override 55 | public void offerRescinded(SchedulerDriver driver, OfferID offerId) { 56 | } 57 | 58 | @Override 59 | public void statusUpdate(SchedulerDriver driver, TaskStatus status) { 60 | 61 | System.out.println("Status update: task " + status.getTaskId().getValue() + 62 | " is in state " + status.getState().getValueDescriptor().getName()); 63 | 64 | if (status.getState() == TaskState.TASK_LOST || 65 | status.getState() == TaskState.TASK_KILLED || 66 | status.getState() == TaskState.TASK_FAILED) { 67 | 68 | System.err.println("Aborting because task " + status.getTaskId().getValue() + 69 | " is in unexpected state " + 70 | status.getState().getValueDescriptor().getName() + 71 | " with reason '" + 72 | status.getReason().getValueDescriptor().getName() + "'" + 73 | " from source '" + 74 | status.getSource().getValueDescriptor().getName() + "'" + 75 | " with message '" + status.getMessage() + "'"); 76 | 77 | driver.stop(); 78 | 79 | } 80 | 81 | } 82 | 83 | @Override 84 | public void frameworkMessage(SchedulerDriver driver, 85 | ExecutorID executorId, 86 | SlaveID slaveId, 87 | byte[] data) { 88 | } 89 | 90 | @Override 91 | public void slaveLost(SchedulerDriver driver, SlaveID slaveId) { 92 | } 93 | 94 | @Override 95 | public void executorLost(SchedulerDriver driver, 96 | ExecutorID executorId, 97 | SlaveID slaveId, 98 | int status) { 99 | } 100 | 101 | @Override 102 | public void registered(SchedulerDriver driver, 103 | FrameworkID frameworkId, 104 | MasterInfo masterInfo) { 105 | System.out.println("Registered! ID = " + frameworkId.getValue()); 106 | } 107 | 108 | @Override 109 | public void reregistered(SchedulerDriver driver, MasterInfo masterInfo) { 110 | } 111 | 112 | @Override 113 | public void disconnected(SchedulerDriver driver) { 114 | } 115 | 116 | @Override 117 | public void error(SchedulerDriver driver, String message) { 118 | System.out.println("Error: " + message); 119 | } 120 | 121 | class ResourceOffer { 122 | 123 | final List offerPorts; 124 | double offerCpus = 0; 125 | double offerMem = 0; 126 | 127 | public ResourceOffer(List resourcesList) { 128 | offerPorts = new ArrayList<>(resourcesList.size()); 129 | for (Resource resource : resourcesList) { 130 | if (resource.getName().equals("cpus")) { 131 | offerCpus += resource.getScalar().getValue(); 132 | } else if (resource.getName().equals("mem")) { 133 | offerMem += resource.getScalar().getValue(); 134 | } else if (resource.getName().equals("ports")) { 135 | for (Long p = resource.getRanges().getRange(0).getBegin(); p <= resource.getRanges().getRange(0).getEnd(); p++) { 136 | offerPorts.add(p); 137 | } 138 | } 139 | } 140 | } 141 | 142 | public boolean isAcceptable() { 143 | return offerCpus >= CPUS_PER_TASK && offerMem >= MEM_PER_TASK && (offerPorts.size() >= 1); 144 | } 145 | 146 | } 147 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------