diff --git a/docs/pom.xml b/docs/pom.xml index 7fc9069..b95ab7e 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-cli-parent - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT pom Spring Cloud Cli Docs diff --git a/pom.xml b/pom.xml index dd36198..e5d5a92 100644 --- a/pom.xml +++ b/pom.xml @@ -3,11 +3,11 @@ 4.0.0 org.springframework.cloud spring-cloud-cli-parent - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT org.springframework.cloud spring-cloud-build - 1.1.2.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT pom @@ -26,6 +26,7 @@ ${basedir}/.. + spring-cloud-launcher spring-cloud-cli spring-cloud-cli-integration-tests docs diff --git a/spring-cloud-cli-integration-tests/pom.xml b/spring-cloud-cli-integration-tests/pom.xml index 4a25eef..517fa67 100644 --- a/spring-cloud-cli-integration-tests/pom.xml +++ b/spring-cloud-cli-integration-tests/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-cli-integration-tests - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT jar spring-cloud-cli-integration-tests @@ -14,7 +14,7 @@ org.springframework.cloud spring-cloud-cli-parent - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT diff --git a/spring-cloud-cli/pom.xml b/spring-cloud-cli/pom.xml index 9c0dbc8..1d7200e 100644 --- a/spring-cloud-cli/pom.xml +++ b/spring-cloud-cli/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-cli - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT jar spring-cloud-cli @@ -14,10 +14,15 @@ org.springframework.cloud spring-cloud-cli-parent - 1.1.3.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT + + org.springframework.cloud.launcher + spring-cloud-launcher-cli + ${project.version} + org.springframework.boot spring-boot-cli diff --git a/spring-cloud-cli/src/main/java/org/springframework/cloud/cli/command/CloudCommandFactory.java b/spring-cloud-cli/src/main/java/org/springframework/cloud/cli/command/CloudCommandFactory.java index a249715..1230862 100644 --- a/spring-cloud-cli/src/main/java/org/springframework/cloud/cli/command/CloudCommandFactory.java +++ b/spring-cloud-cli/src/main/java/org/springframework/cloud/cli/command/CloudCommandFactory.java @@ -22,6 +22,7 @@ import org.springframework.boot.cli.command.Command; import org.springframework.boot.cli.command.CommandFactory; import org.springframework.cloud.cli.command.encrypt.DecryptCommand; import org.springframework.cloud.cli.command.encrypt.EncryptCommand; +import org.springframework.cloud.launcher.cli.LauncherCommand; /** * @author Dave Syer @@ -31,7 +32,7 @@ public class CloudCommandFactory implements CommandFactory { @Override public Collection getCommands() { - return Arrays.asList(new EncryptCommand(), new DecryptCommand()); + return Arrays.asList(new EncryptCommand(), new DecryptCommand(), new LauncherCommand()); } } diff --git a/spring-cloud-launcher/README.md b/spring-cloud-launcher/README.md new file mode 100644 index 0000000..bf5821c --- /dev/null +++ b/spring-cloud-launcher/README.md @@ -0,0 +1,70 @@ +## Spring Cloud Launcher + +### Building + +`./mvnw clean install` from parent directory + +### Installing + +Install Spring CLI first: https://github.com/spring-cloud/spring-cloud-cli/blob/master/docs/src/main/asciidoc/install.adoc + +``` +$ spring install org.springframework.cloud:spring-cloud-cli:1.2.0.BUILD-SNAPSHOT +``` + +### Running + +``` +$ spring cloud +``` + +Currently starts configserver, eureka, h2 (db server), hystrixdashboard and kafka. [Here](https://github.com/spring-cloud/spring-cloud-cli/blob/devtools/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/cloud.yml) is the full configuration. + +### Configuring + +Spring Cloud Launcher use normal Spring Boot configuration mechanisms. The config name is `cloud`, so configuration can go in `cloud.yml` or `cloud.properties`. + +For example, to run configserver and eureka, create a `cloud.yml` that looks like: +``` +spring: + cloud: + launcher: + deployables: + - name: configserver + coordinates: maven://org.springframework.cloud.launcher:spring-cloud-launcher-configserver:1.1.0.BUILD-SNAPSHOT + port: 8888 + waitUntilStarted: true + order: -10 + - name: eureka + coordinates: maven://org.springframework.cloud.launcher:spring-cloud-launcher-eureka:1.1.0.BUILD-SNAPSHOT + port: 8761 +``` + +The `name` attribute is required. If `waitUntilStarted` is true, Launcher will block until the application has reached the `deployed` state. Before commands are deployed, the list is sorted using Spring's `OrderComparator`. In the above case, `configserver` is deployed before any other app is deployed. Currently only `maven:` coordinates and standard Spring Resources (`file:`, etc...) are supported. + +You can also use the `--deploy` option to select from the [predefined deployables](https://github.com/spring-cloud/spring-cloud-cli/blob/devtools/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/cloud.yml). For example to run Spring Cloud Data Flow execute: +``` +spring cloud --launch=configserver,h2,kafka,dataflow +``` + +### Stopping + +`Ctrl-C` in the same terminal `spring cloud` was run. + +### TODO + +- [X] Eureka +- [X] Configserver +- [X] Hystrix Dashboard +- [X] Kafka Broker +- [X] Kafka Bus +- [X] Easy inclusion of default deployables +- [X] H2 Database +- [X] Spring Cloud Dataflow server +- [X] Launcher landing page (Eureka Dashboard works for now) +- [ ] Support external rabbit +- [ ] Speedup startup (parallel start?, retry for config server, db and kafka?) +- [ ] Sleuth/Zipkin +- [ ] Cassandra Database +- [ ] Client Side Library +- [ ] Spring Boot Admin (Not compatible with Brixton) diff --git a/spring-cloud-launcher/pom.xml b/spring-cloud-launcher/pom.xml new file mode 100644 index 0000000..fd6d3e5 --- /dev/null +++ b/spring-cloud-launcher/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + pom + + spring-cloud-launcher + Spring Cloud Launcher + + + org.springframework.cloud + spring-cloud-cli-parent + 1.2.0.BUILD-SNAPSHOT + + + + spring-cloud-launcher-cli + spring-cloud-launcher-configserver + spring-cloud-launcher-dataflow + spring-cloud-launcher-deployer + spring-cloud-launcher-eureka + spring-cloud-launcher-h2 + spring-cloud-launcher-hystrixdashboard + spring-cloud-launcher-kafka + + + + 1.1.0.BUILD-SNAPSHOT + 1.0.0.BUILD-SNAPSHOT + 1.0.0.BUILD-SNAPSHOT + + + + + + org.springframework.cloud + spring-cloud-bus + ${spring-cloud-bus.version} + pom + import + + + org.springframework.cloud + spring-cloud-dataflow-dependencies + ${spring-cloud-dataflow.version} + pom + import + + + org.springframework.cloud + spring-cloud-deployer-local + ${spring-cloud-deployer.version} + + + org.springframework.cloud + spring-cloud-deployer-resource-support + ${spring-cloud-deployer.version} + + + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-cli/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-cli/pom.xml new file mode 100644 index 0000000..76ac8dd --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-cli/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-cli + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-cli + Spring Cloud Launcher CLI + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-cli + ${spring-boot.version} + provided + + + commons-logging + commons-logging + 1.2 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-cli/src/main/java/org/springframework/cloud/launcher/cli/LauncherCommand.java b/spring-cloud-launcher/spring-cloud-launcher-cli/src/main/java/org/springframework/cloud/launcher/cli/LauncherCommand.java new file mode 100644 index 0000000..1b5e33d --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-cli/src/main/java/org/springframework/cloud/launcher/cli/LauncherCommand.java @@ -0,0 +1,109 @@ +/* + * Copyright 2013-2016 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.launcher.cli; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.springframework.boot.cli.command.AbstractCommand; +import org.springframework.boot.cli.command.status.ExitStatus; +import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; +import org.springframework.boot.cli.compiler.grape.AetherGrapeEngine; +import org.springframework.boot.cli.compiler.grape.AetherGrapeEngineFactory; +import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; +import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; + +import groovy.lang.GroovyClassLoader; + +/** + * @author Spencer Gibb + */ +public class LauncherCommand extends AbstractCommand { + + public static final Log log = LogFactory.getLog(LauncherCommand.class); + + public LauncherCommand() { + super("cloud", "Start Spring Cloud Launcher"); + } + + @Override + public ExitStatus run(String... args) throws Exception { + + try { + URLClassLoader classLoader = populateClassloader(); + + String name = "org.springframework.cloud.launcher.deployer.DeployerThread"; + Class threadClass = classLoader.loadClass(name); + + Constructor constructor = threadClass.getConstructor(ClassLoader.class, String[].class); + Thread thread = (Thread) constructor.newInstance(classLoader, args); + thread.start(); + thread.join(); + } catch (Exception e) { + e.printStackTrace(); + } + + return ExitStatus.OK; + } + + URLClassLoader populateClassloader() throws MalformedURLException { + DependencyResolutionContext resolutionContext = new DependencyResolutionContext(); + + GroovyClassLoader loader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader(), new CompilerConfiguration()); + + List repositoryConfiguration = RepositoryConfigurationFactory + .createDefaultRepositoryConfiguration(); + repositoryConfiguration.add(0, new RepositoryConfiguration("local", + new File("repository").toURI(), true)); + + String[] classpaths = {"."}; + for (String classpath : classpaths) { + loader.addClasspath(classpath); + } + + System.setProperty("groovy.grape.report.downloads", "true"); + //System.setProperty("grape.root", "."); + + AetherGrapeEngine grapeEngine = AetherGrapeEngineFactory.create(loader, + repositoryConfiguration, resolutionContext); + + //GrapeEngineInstaller.install(grapeEngine); + + //TODO: get version dynamically? + HashMap dependency = new HashMap<>(); + dependency.put("group", "org.springframework.cloud.launcher"); + dependency.put("module", "spring-cloud-launcher-deployer"); + dependency.put("version", "1.2.0.BUILD-SNAPSHOT"); + URI[] uris = grapeEngine.resolve(null, dependency); + //System.out.println("resolved URI's " + Arrays.asList(uris)); + for (URI uri : uris) { + loader.addURL(uri.toURL()); + } + log.debug("resolved URI's " + Arrays.asList(loader.getURLs())); + return loader; + } + +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-cli/src/test/java/org/springframework/cloud/launcher/cli/LauncherCommandTests.java b/spring-cloud-launcher/spring-cloud-launcher-cli/src/test/java/org/springframework/cloud/launcher/cli/LauncherCommandTests.java new file mode 100644 index 0000000..b6ea09e --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-cli/src/test/java/org/springframework/cloud/launcher/cli/LauncherCommandTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2016 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.launcher.cli; + +import org.junit.Ignore; +import org.junit.Test; + +/** + * @author Spencer Gibb + */ +public class LauncherCommandTests { + + @Test + @Ignore + public void testLoadDeployer() throws Exception { + //new LauncherCommand().populateClassloader(); + new LauncherCommand().run(); + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-configserver/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-configserver/pom.xml new file mode 100644 index 0000000..1cf9fed --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-configserver/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-configserver + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-configserver + Spring Cloud Launcher ConfigServer + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-bus-kafka + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/java/org/springframework/cloud/launcher/configserver/ConfigServerApplication.java b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/java/org/springframework/cloud/launcher/configserver/ConfigServerApplication.java new file mode 100644 index 0000000..3f613ae --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/java/org/springframework/cloud/launcher/configserver/ConfigServerApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2016 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.launcher.configserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.config.server.EnableConfigServer; + +/** + * @author Spencer Gibb + */ +@EnableConfigServer +@EnableDiscoveryClient +@SpringBootApplication +public class ConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigServerApplication.class, args); + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/application.yml new file mode 100644 index 0000000..513b02a --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/application.yml @@ -0,0 +1,26 @@ +spring: + application: + name: configserver + profiles: + active: native + cloud: + config: + server: + bootstrap: true + native: + search-locations: classpath:/launcher/, file:./launcher/ + +server: + port: 8888 + +management: + context-path: /admin + +eureka: + instance: + status-page-url-path: ${management.context-path}/info + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ \ No newline at end of file diff --git a/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/launcher/application.yml b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/launcher/application.yml new file mode 100644 index 0000000..3aafd1c --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-configserver/src/main/resources/launcher/application.yml @@ -0,0 +1,28 @@ +info: + description: Spring Cloud Launcher + +eureka: + client: + instance-info-replication-interval-seconds: 5 + initial-instance-info-replication-interval-seconds: 5 + serviceUrl: + defaultZone: http://localhost:8761/eureka/ + +endpoints: + restart: enabled + +ribbon: + ConnectTimeout: 3000 + ReadTimeout: 60000 + +spring: + datasource: + url: jdbc:h2:tcp://localhost:9096/~/launcher + +logging: + level: + kafka: WARN + org.apache.zookeeper: WARN + org.apache.zookeeper.ClientCnxn: ERROR + org.apache.kafka: WARN + org.I0Itec: WARN diff --git a/spring-cloud-launcher/spring-cloud-launcher-dataflow/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-dataflow/pom.xml new file mode 100644 index 0000000..1944cff --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-dataflow/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-dataflow + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-dataflow + Spring Cloud Launcher Data Flow + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-deployer-local + + + org.springframework.cloud + spring-cloud-dataflow-server-core + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/java/org/springframework/cloud/launcher/dataflow/DataFlowApplication.java b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/java/org/springframework/cloud/launcher/dataflow/DataFlowApplication.java new file mode 100644 index 0000000..37984d3 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/java/org/springframework/cloud/launcher/dataflow/DataFlowApplication.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2016 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.launcher.dataflow; + +import javax.sql.DataSource; + +import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.batch.BatchDatabaseInitializer; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.dataflow.server.EnableDataFlowServer; +import org.springframework.cloud.task.repository.support.TaskRepositoryInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +/** + * @author Spencer Gibb + */ +@EnableDataFlowServer +@EnableDiscoveryClient +@SpringBootApplication +public class DataFlowApplication { + public static void main(String[] args) { + SpringApplication.run(DataFlowApplication.class, args); + } + + @Bean + public JobRepositoryFactoryBean jobRepositoryFactoryBeanForServer( + DataSource dataSource, + DataSourceTransactionManager dataSourceTransactionManager) { + JobRepositoryFactoryBean repositoryFactoryBean = new JobRepositoryFactoryBean(); + repositoryFactoryBean.setDataSource(dataSource); + repositoryFactoryBean.setTransactionManager(dataSourceTransactionManager); + return repositoryFactoryBean; + } + + @Bean + public DataSourceTransactionManager transactionManagerForServer( + DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + + @Bean + public BatchDatabaseInitializer batchRepositoryInitializerForDefaultDBForServer() { + return new BatchDatabaseInitializer(); + } + + @Bean + public TaskRepositoryInitializer taskRepositoryInitializerForDefaultDB( + DataSource dataSource) { + TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer(); + taskRepositoryInitializer.setDataSource(dataSource); + return taskRepositoryInitializer; + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/application.yml new file mode 100644 index 0000000..cfbe078 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 9393 + +eureka: + instance: + status-page-url-path: /admin-ui #allows you to click on the link in eureka dashboard + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ + +security: + basic: + enabled: false + +management: + context-path: /admin + security: + enabled: false \ No newline at end of file diff --git a/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/bootstrap.yml b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..6ef41f9 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-dataflow/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + application: + name: dataflow diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-deployer/pom.xml new file mode 100644 index 0000000..0652015 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-deployer + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-deployer + Spring Cloud Launcher Deployer + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.cloud + spring-cloud-deployer-local + + + org.springframework.cloud + spring-cloud-deployer-resource-support + + + org.springframework.boot + spring-boot-starter-test + test + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerConfiguration.java b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerConfiguration.java new file mode 100644 index 0000000..f3c72cb --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2016 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.launcher.deployer; + +import java.util.HashMap; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.deployer.resource.maven.MavenProperties; +import org.springframework.cloud.deployer.resource.maven.MavenResourceLoader; +import org.springframework.cloud.deployer.resource.support.DelegatingResourceLoader; +import org.springframework.cloud.deployer.spi.app.AppDeployer; +import org.springframework.cloud.deployer.spi.local.LocalAppDeployer; +import org.springframework.cloud.deployer.spi.local.LocalDeployerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; + +/** + * @author Spencer Gibb + */ +@Configuration +@EnableConfigurationProperties +public class DeployerConfiguration { + + @Bean + public DeployerProperties deployerProperties() { + return new DeployerProperties(); + } + + @Bean + public LocalDeployerProperties localDeployerProperties() { + return new LocalDeployerProperties(); + } + + @Bean + @ConditionalOnMissingBean + public AppDeployer appDeployer() { + return new LocalAppDeployer(localDeployerProperties()); + } + + @Bean + public MavenProperties mavenProperties() { + return new MavenProperties(); //TODO: exposed as config properties? + } + + @Bean + public MavenResourceLoader mavenResourceLoader(MavenProperties mavenProperties) { + return new MavenResourceLoader(mavenProperties); + } + + @Bean + public DelegatingResourceLoader delegatingResourceLoader(MavenResourceLoader mavenResourceLoader) { + HashMap map = new HashMap<>(); + map.put("maven", mavenResourceLoader); + return new DelegatingResourceLoader(map); + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerProperties.java b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerProperties.java new file mode 100644 index 0000000..e63c1e5 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerProperties.java @@ -0,0 +1,148 @@ +/* + * Copyright 2013-2016 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.launcher.deployer; + +import java.util.ArrayList; +import java.util.List; + +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.constraints.NotEmpty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.Ordered; + +/** + * @author Spencer Gibb + */ +@ConfigurationProperties(prefix = "spring.cloud.launcher") +public class DeployerProperties { + + @NotNull + private List deployables = new ArrayList<>(); + + @NotNull + private List deploy = new ArrayList<>(); + + private int statusSleepMillis = 300; + + public List getDeployables() { + return deployables; + } + + public void setDeployables(List deployables) { + this.deployables = deployables; + } + + public List getDeploy() { + return deploy; + } + + public void setDeploy(List deploy) { + this.deploy = deploy; + } + + public int getStatusSleepMillis() { + return statusSleepMillis; + } + + public void setStatusSleepMillis(int statusSleepMillis) { + this.statusSleepMillis = statusSleepMillis; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("DeployerProperties{"); + sb.append("deployables=").append(deployables); + sb.append("deploy=").append(deploy); + sb.append("statusSleepMillis=").append(statusSleepMillis); + sb.append('}'); + return sb.toString(); + } + + public static class Deployable implements Ordered { + @NotEmpty + private String coordinates; + @NotEmpty + private String name; + private int port = 0; + private boolean waitUntilStarted; + private int order = 0; + private String message; + + public String getCoordinates() { + return coordinates; + } + + public void setCoordinates(String coordinates) { + this.coordinates = coordinates; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isWaitUntilStarted() { + return waitUntilStarted; + } + + public void setWaitUntilStarted(boolean waitUntilStarted) { + this.waitUntilStarted = waitUntilStarted; + } + + @Override + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("Deployable{"); + sb.append("coordinates='").append(coordinates).append('\''); + sb.append(", name='").append(name).append('\''); + sb.append(", port=").append(port); + sb.append(", waitUntilStarted=").append(waitUntilStarted); + sb.append(", order=").append(order); + sb.append(", message=").append(message); + sb.append('}'); + return sb.toString(); + } + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerThread.java b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerThread.java new file mode 100644 index 0000000..506029b --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/java/org/springframework/cloud/launcher/deployer/DeployerThread.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013-2016 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.launcher.deployer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.deployer.resource.support.DelegatingResourceLoader; +import org.springframework.cloud.deployer.spi.app.AppDeployer; +import org.springframework.cloud.deployer.spi.app.AppStatus; +import org.springframework.cloud.deployer.spi.app.DeploymentState; +import org.springframework.cloud.deployer.spi.core.AppDefinition; +import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest; +import org.springframework.cloud.launcher.deployer.DeployerProperties.Deployable; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.OrderComparator; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.StringUtils; + +/** + * @author Spencer Gibb + */ +@SuppressWarnings("unused") +public class DeployerThread extends Thread { + + private static final Logger logger = LoggerFactory.getLogger(DeployerThread.class); + + private Map deployed = new ConcurrentHashMap<>(); + + private String[] args; + + public DeployerThread(ClassLoader classLoader, String[] args) { + super("spring-cloud-launcher"); + this.args = args; + setContextClassLoader(classLoader); + setDaemon(true); + } + + @Override + public void run() { + final ConfigurableApplicationContext context = new SpringApplicationBuilder(PropertyPlaceholderAutoConfiguration.class, DeployerConfiguration.class) + .web(false) + .properties("spring.config.name=cloud", "banner.location=launcher-banner.txt") + .run(args); + + final AppDeployer deployer = context.getBean(AppDeployer.class); + + ResourceLoader resourceLoader = context.getBean(DelegatingResourceLoader.class); + + DeployerProperties properties = context.getBean(DeployerProperties.class); + + ArrayList deployables = new ArrayList<>(properties.getDeployables()); + OrderComparator.sort(deployables); + + logger.debug("toDeploy {}", properties.getDeployables()); + + for (Deployable deployable : deployables) { + deploy(deployer, resourceLoader, deployable, properties); + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + logger.info("\n\nShutting down ...\n"); + for (String id : DeployerThread.this.deployed.keySet()) { + logger.info("Undeploying {}", id); + deployer.undeploy(id); + } + context.close(); + } + }); + + for (Deployable deployable : deployables) { + if (shouldDeploy(deployable, properties) && StringUtils.hasText(deployable.getMessage())) { + logger.info("\n\n{}: {}\n", deployable.getName(), deployable.getMessage()); + } + } + + logger.info("\n\nType Ctrl-C to quit.\n"); + + while (true) { + for (Map.Entry entry : this.deployed.entrySet()) { + String id = entry.getKey(); + DeploymentState state = entry.getValue(); + AppStatus status = deployer.status(id); + DeploymentState newState = status.getState(); + if (state != newState) { + logger.info("{} change status from {} to {}", id, state, newState); + this.deployed.put(id, newState); + } + } + try { + Thread.sleep(properties.getStatusSleepMillis()); + } catch (InterruptedException e) { + logger.error("error sleeping", e); + } + } + } + + private String deploy(AppDeployer deployer, ResourceLoader resourceLoader, Deployable deployable, DeployerProperties properties) { + if (!shouldDeploy(deployable, properties)) { + // this deployable isn't in the list of things to deploy + logger.info("Skipping deploy of {}", deployable.getName()); + return null; + } + + logger.debug("getting resource {} = {}", deployable.getName(), deployable.getCoordinates()); + Resource resource = resourceLoader.getResource(deployable.getCoordinates()); + + Map appDefProps = new HashMap<>(); + appDefProps.put("server.port", String.valueOf(deployable.getPort())); + + //TODO: move to notDeployedProps or something + if (!shouldDeploy("kafka", properties)) { + appDefProps.put("spring.cloud.bus.enabled", Boolean.FALSE.toString()); + } + + AppDefinition definition = new AppDefinition(deployable.getName(), appDefProps); + + Map environmentProperties = Collections.singletonMap(AppDeployer.GROUP_PROPERTY_KEY, "launcher"); + AppDeploymentRequest request = new AppDeploymentRequest(definition, resource, environmentProperties); + + logger.debug("deploying resource {} = {}", deployable.getName(), deployable.getCoordinates()); + String id = deployer.deploy(request); + AppStatus appStatus = getAppStatus(deployer, id); + logger.info("Status of {}: {}", id, appStatus); + //TODO: stream stdout/stderr like docker-compose (with colors and prefix) + + if (deployable.isWaitUntilStarted()) { + try { + logger.info("\n\nWaiting for {} to start.\n", deployable.getName()); + + while (appStatus.getState() != DeploymentState.deployed + && appStatus.getState() != DeploymentState.failed) { + Thread.sleep(properties.getStatusSleepMillis()); + appStatus = getAppStatus(deployer, id); + logger.debug("State of {} = {}", id, appStatus.getState()); + } + } catch (Exception e) { + logger.error("error updating status of " + id, e); + } + } + + return id; + } + + private boolean shouldDeploy(Deployable deployable, DeployerProperties properties) { + return shouldDeploy(deployable.getName(), properties); + } + + private boolean shouldDeploy(String name, DeployerProperties properties) { + boolean deploy = properties.getDeploy().contains(name); + logger.debug("shouldDeploy {} = {}", name, deploy); + return deploy; + } + + private AppStatus getAppStatus(AppDeployer deployer, String id) { + AppStatus appStatus = deployer.status(id); + this.deployed.put(id, appStatus.getState()); + return appStatus; + } + + +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/cloud.yml b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/cloud.yml new file mode 100644 index 0000000..4451b63 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/cloud.yml @@ -0,0 +1,33 @@ +dt: + pre: maven://org.springframework.cloud.launcher:spring-cloud-launcher- + ver: 1.2.0.BUILD-SNAPSHOT +spring: + cloud: + launcher: + deployables: + - name: configserver + coordinates: ${dt.pre}configserver:${dt.ver} + port: 8888 + waitUntilStarted: true + order: -100 + - name: dataflow + coordinates: ${dt.pre}dataflow:${dt.ver} + port: 9393 + - name: eureka + coordinates: ${dt.pre}eureka:${dt.ver} + port: 8761 + message: To see the dashboard open http://localhost:8761 + - name: h2 + coordinates: ${dt.pre}h2:${dt.ver} + port: 9095 + waitUntilStarted: true + order: -50 + - name: hystrixdashboard + coordinates: ${dt.pre}hystrixdashboard:${dt.ver} + port: 7979 + - name: kafka + coordinates: ${dt.pre}kafka:${dt.ver} + port: 9091 + waitUntilStarted: true + order: -200 + deploy: ${launch:configserver,eureka,h2, hystrixdashboard,kafka} diff --git a/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/launcher-banner.txt b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/launcher-banner.txt new file mode 100644 index 0000000..d8d3646 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-deployer/src/main/resources/launcher-banner.txt @@ -0,0 +1,9 @@ +${Ansi.GREEN} +███████╗██████╗ ██████╗ ██╗███╗ ██╗ ██████╗ ██████╗██╗ ██████╗ ██╗ ██╗██████╗ +██╔════╝██╔══██╗██╔══██╗██║████╗ ██║██╔════╝ ██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗ +███████╗██████╔╝██████╔╝██║██╔██╗ ██║██║ ███╗ ██║ ██║ ██║ ██║██║ ██║██║ ██║ +╚════██║██╔═══╝ ██╔══██╗██║██║╚██╗██║██║ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ +███████║██║ ██║ ██║██║██║ ╚████║╚██████╔╝ ╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝ +╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ +${Ansi.WHITE} +-- Spring Cloud Launcher -- diff --git a/spring-cloud-launcher/spring-cloud-launcher-eureka/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-eureka/pom.xml new file mode 100644 index 0000000..b7ea80e --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-eureka/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-eureka + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-eureka + Spring Cloud Launcher Eureka + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-bus-kafka + + + org.springframework.cloud + spring-cloud-netflix-eureka-server + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/java/org/springframework/cloud/launcher/eureka/EurekaApplication.java b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/java/org/springframework/cloud/launcher/eureka/EurekaApplication.java new file mode 100644 index 0000000..2dc1a64 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/java/org/springframework/cloud/launcher/eureka/EurekaApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2016 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.launcher.eureka; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +/** + * @author Spencer Gibb + */ +@EnableEurekaServer +@SpringBootApplication +public class EurekaApplication { + public static void main(String[] args) { + SpringApplication.run(EurekaApplication.class, args); + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/application.yml new file mode 100644 index 0000000..b79d75c --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 8761 + +eureka: + client: + registerWithEureka: false + fetchRegistry: false + server: + renewal-percent-threshold: 0.01 + waitTimeInMsWhenSyncEmpty: 0 + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ diff --git a/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/bootstrap.yml b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..076a289 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-eureka/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + application: + name: eurekaserver diff --git a/spring-cloud-launcher/spring-cloud-launcher-h2/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-h2/pom.xml new file mode 100644 index 0000000..bed2405 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-h2/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-h2 + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-h2 + Spring Cloud Launcher H2 + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-bus-kafka + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/java/org/springframework/cloud/launcher/h2/H2Application.java b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/java/org/springframework/cloud/launcher/h2/H2Application.java new file mode 100644 index 0000000..653ba95 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/java/org/springframework/cloud/launcher/h2/H2Application.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013-2016 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.launcher.h2; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.h2.tools.Console; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.SmartLifecycle; +import org.springframework.stereotype.Controller; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +/** + * @author Spencer Gibb + */ +@EnableDiscoveryClient +@SpringBootApplication +@Controller +public class H2Application { + private static final Log log = LogFactory.getLog(H2Application.class); + + public static void main(String[] args) { + SpringApplication.run(H2Application.class, args); + } + + @SuppressWarnings("unused") + @Service + static class H2Server implements SmartLifecycle { + private AtomicBoolean running = new AtomicBoolean(false); + + private Console console; + + @Value("${spring.datasource.url:9096}") + private String dataSourceUrl; + + @Override + public boolean isAutoStartup() { + return true; + } + + @Override + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + @Override + public void start() { + if (this.running.compareAndSet(false, true)) { + try { + log.info("Starting H2 Server"); + this.console = new Console(); + this.console.runTool("-tcp", "-pg", "-web", "-tcpAllowOthers", "-tcpPort", getH2Port(dataSourceUrl)); + } catch (Exception e) { + ReflectionUtils.rethrowRuntimeException(e); + } + } + } + + private String getH2Port(String url) { + String[] tokens = StringUtils.tokenizeToStringArray(url, ":"); + Assert.isTrue(tokens.length >= 5, "URL not properly formatted"); + return tokens[4].substring(0, tokens[4].indexOf("/")); + } + + @Override + public void stop() { + if (this.running.compareAndSet(true, false)) { + log.info("Stoping H2 Server"); + this.console.shutdown(); + } + } + + @Override + public boolean isRunning() { + return this.running.get(); + } + + @Override + public int getPhase() { + return 0; + } + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/application.yml new file mode 100644 index 0000000..6994fba --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 9095 + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ + +eureka: + instance: + # port of the h2 console + non-secure-port: 8082 + status-page-url-path: / #allows you to click on the link in eureka dashboard \ No newline at end of file diff --git a/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/bootstrap.yml b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..52cbd0f --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-h2/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + application: + name: h2server diff --git a/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/pom.xml new file mode 100644 index 0000000..030c474 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-hystrixdashboard + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-hystrixdashboard + Spring Cloud Launcher Hystrix Dashboard + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-bus-kafka + + + org.springframework.cloud + spring-cloud-starter-hystrix-dashboard + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/java/org/springframework/cloud/launcher/hystrixdashboard/HystrixDashboardApplication.java b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/java/org/springframework/cloud/launcher/hystrixdashboard/HystrixDashboardApplication.java new file mode 100644 index 0000000..8a8ec58 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/java/org/springframework/cloud/launcher/hystrixdashboard/HystrixDashboardApplication.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2016 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.launcher.hystrixdashboard; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * @author Spencer Gibb + */ +@EnableDiscoveryClient +@EnableHystrixDashboard +@SpringBootApplication +@Controller +public class HystrixDashboardApplication { + + @RequestMapping(path = "/", method = RequestMethod.GET) + public String home() { + return "redirect:/hystrix"; + } + + public static void main(String[] args) { + SpringApplication.run(HystrixDashboardApplication.class, args); + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/application.yml new file mode 100644 index 0000000..b6cd473 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/application.yml @@ -0,0 +1,11 @@ +server: + port: 7979 + +eureka: + instance: + status-page-url-path: /hystrix #allows you to click on the link in eureka dashboard + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ diff --git a/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/bootstrap.yml b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..0970919 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-hystrixdashboard/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + application: + name: hystrixdashboard diff --git a/spring-cloud-launcher/spring-cloud-launcher-kafka/pom.xml b/spring-cloud-launcher/spring-cloud-launcher-kafka/pom.xml new file mode 100644 index 0000000..7ac132b --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-kafka/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + org.springframework.cloud.launcher + spring-cloud-launcher-kafka + 1.2.0.BUILD-SNAPSHOT + jar + + spring-cloud-launcher-kafka + Spring Cloud Launcher Kafka + + + org.springframework.cloud + spring-cloud-launcher + 1.2.0.BUILD-SNAPSHOT + + + + 0.9.0.1 + 2.10.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.apache.curator + curator-recipes + ${curator.version} + + + org.apache.kafka + kafka_2.11 + ${kafka.version} + + + org.slf4j + slf4j-log4j12 + + + + + org.apache.kafka + kafka_2.11 + test + ${kafka.version} + + + org.slf4j + slf4j-log4j12 + + + + + org.apache.curator + curator-test + ${curator.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/java/org/springframework/cloud/launcher/kafka/KafkaApplication.java b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/java/org/springframework/cloud/launcher/kafka/KafkaApplication.java new file mode 100644 index 0000000..acc4d24 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/java/org/springframework/cloud/launcher/kafka/KafkaApplication.java @@ -0,0 +1,206 @@ +/* + * Copyright 2013-2016 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.launcher.kafka; + +import java.io.File; +import java.net.InetSocketAddress; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.I0Itec.zkclient.ZkClient; +import org.I0Itec.zkclient.exception.ZkInterruptedException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.kafka.common.protocol.SecurityProtocol; +import org.apache.kafka.common.utils.Utils; +import org.apache.zookeeper.server.NIOServerCnxnFactory; +import org.apache.zookeeper.server.ZooKeeperServer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.SmartLifecycle; +import org.springframework.stereotype.Service; +import org.springframework.util.ReflectionUtils; + +import kafka.server.KafkaConfig; +import kafka.server.KafkaServer; +import kafka.server.NotRunning; +import kafka.utils.CoreUtils; +import kafka.utils.SystemTime$; +import kafka.utils.TestUtils; +import kafka.utils.ZKStringSerializer$; + +/** + * @author Spencer Gibb + */ +@SpringBootApplication +public class KafkaApplication { + + private static final Log log = LogFactory.getLog(KafkaApplication.class); + + public static void main(String[] args) { + new SpringApplicationBuilder(KafkaApplication.class) + .run(args); + } + + @SuppressWarnings("unused") + @Service + static class KafkaDevServer implements SmartLifecycle { + private AtomicBoolean running = new AtomicBoolean(false); + private ZkClient zkClient; + + private EmbeddedZookeeper zookeeper; + + private KafkaServer kafkaServer; + + @Value("${kafka.port:${KAFKA_PORT:9092}}") + private int port; + + @Value("${zk.port:${ZK_PORT:2181}}") + private int zkPort; + + @Override + public boolean isAutoStartup() { + return true; + } + + @Override + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + @Override + public void start() { + if (this.running.compareAndSet(false, true)) { + try { + log.info("Starting Zookeeper"); + this.zookeeper = new EmbeddedZookeeper(this.zkPort); + String zkConnectString = "127.0.0.1:" + this.zookeeper.getPort(); + log.info("Started Zookeeper at " + zkConnectString); + try { + int zkConnectionTimeout = 10000; + int zkSessionTimeout = 10000; + zkClient = new ZkClient(zkConnectString, zkSessionTimeout, zkConnectionTimeout, ZKStringSerializer$.MODULE$); + } + catch (Exception e) { + zookeeper.shutdown(); + throw e; + } + try { + log.info("Creating Kafka server"); + //TODO: move to properties? + int nodeId = 0; + boolean enableControlledShutdown = true; + Properties brokerConfigProperties = TestUtils.createBrokerConfig(nodeId, zkConnectString, enableControlledShutdown, + true, port, + scala.Option.apply(null), + scala.Option.apply(null), + true, false, 0, false, 0, false, 0); + brokerConfigProperties.setProperty("replica.socket.timeout.ms", "1000"); + brokerConfigProperties.setProperty("controller.socket.timeout.ms", "1000"); + brokerConfigProperties.setProperty("offsets.topic.replication.factor", "1"); + brokerConfigProperties.put("zookeeper.connect", zkConnectString); + kafkaServer = TestUtils.createServer(new KafkaConfig(brokerConfigProperties), SystemTime$.MODULE$); + log.info("Created Kafka server at " + kafkaServer.config().hostName() + ":" + kafkaServer.config().port()); + } + catch (Exception e) { + zookeeper.shutdown(); + zkClient.close(); + throw e; + } + } catch (Exception e) { + ReflectionUtils.rethrowRuntimeException(e); + } + } + } + + @Override + public void stop() { + if (this.running.compareAndSet(true, false)) { + log.info("Stoping Kafka"); + try { + if (kafkaServer.brokerState().currentState() != (NotRunning.state())) { + kafkaServer.shutdown(); + kafkaServer.awaitShutdown(); + } + } + catch (Exception e) { + // do nothing + } + try { + CoreUtils.rm(kafkaServer.config().logDirs()); + } + catch (Exception e) { + // do nothing + } + log.info("Stoping Zookeeper"); + try { + this.zkClient.close(); + } + catch (ZkInterruptedException e) { + // do nothing + } + try { + this.zookeeper.shutdown(); + } + catch (Exception e) { + // do nothing + } + } + } + + @Override + public boolean isRunning() { + return this.running.get(); + } + + @Override + public int getPhase() { + return 0; + } + } + + static class EmbeddedZookeeper { + private File snapshotDir = TestUtils.tempDir(); + private File logDir = TestUtils.tempDir(); + private int tickTime = 500; + private NIOServerCnxnFactory factory = new NIOServerCnxnFactory(); + private ZooKeeperServer zookeeper; + private InetSocketAddress addr; + private int port; + + public EmbeddedZookeeper(int port) throws Exception { + this.port = port; + zookeeper = new ZooKeeperServer(snapshotDir, logDir, tickTime); + addr = new InetSocketAddress("127.0.0.1", port); + factory.configure(addr, 0); + factory.startup(zookeeper); + } + + public int getPort() { + return port; + } + + void shutdown() throws Exception { + zookeeper.shutdown(); + factory.shutdown(); + Utils.delete(logDir); + Utils.delete(snapshotDir); + } + } +} diff --git a/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/application.yml b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/application.yml new file mode 100644 index 0000000..8890030 --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 9091 + +kafka: + port: 9092 + +zk: + port: 2181 + +info: + artifactId: @project.artifactId@ + description: @project.description@ + version: @project.version@ diff --git a/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/bootstrap.yml b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..fccd3ad --- /dev/null +++ b/spring-cloud-launcher/spring-cloud-launcher-kafka/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + application: + name: kafka