├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── generators └── app │ ├── index.js │ └── templates │ ├── _docker-compose.yml │ ├── central-server-config │ └── _application.yml │ ├── elk.yml │ ├── log-monitoring │ ├── log-config │ │ └── logstash.conf │ └── log-data │ │ └── .gitignore │ └── registry.yml └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "indent": [ 8 | 2, 9 | 4 10 | ], 11 | "linebreak-style": [ 12 | 2, 13 | "unix" 14 | ], 15 | "quotes": [ 16 | 2, 17 | "single" 18 | ], 19 | "semi": [ 20 | 2, 21 | "always" 22 | ], 23 | "eqeqeq": 2, 24 | "no-spaced-func": 2 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 William Marques 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker-Compose File generator for JHipster applications 2 | 3 | ## Usage 4 | This generator will check your subdirectories and find all the microservices and gateways you generated. 5 | Then, it will generate a docker-compose.yml file which will run all those applications in one simple command : 6 | ```bash 7 | docker-compose up 8 | ``` 9 | 10 | ## Installation 11 | 12 | This requires JHipster version greater than 3.0. 13 | Clone this project, then run 14 | 15 | ```bash 16 | npm link 17 | ``` 18 | 19 | Then go into a directory (we advice to create a directory alongside the applications directories) and run: 20 | 21 | ```bash 22 | yo jhipster-docker-compose 23 | ``` 24 | 25 | Launch the registry first by running: 26 | ```bash 27 | docker-compose up -d jhipster-registry 28 | ``` 29 | 30 | Wait a minute and finally, you can launch all the applications by doing 31 | ```bash 32 | docker-compose up -d 33 | ``` 34 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var yeoman = require('yeoman-generator'); 3 | var chalk = require('chalk'); 4 | var shelljs = require('shelljs'); 5 | var crypto = require('crypto'); 6 | var _ = require('lodash'); 7 | 8 | module.exports = yeoman.generators.Base.extend({ 9 | initializing: { 10 | printJHipsterLogo: function() { 11 | this.log(' \n' + 12 | chalk.green(' ██') + chalk.red(' ██ ██ ████████ ███████ ██████ ████████ ████████ ███████\n') + 13 | chalk.green(' ██') + chalk.red(' ██ ██ ██ ██ ██ ██ ██ ██ ██ ██\n') + 14 | chalk.green(' ██') + chalk.red(' ████████ ██ ███████ █████ ██ ██████ ███████\n') + 15 | chalk.green(' ██ ██') + chalk.red(' ██ ██ ██ ██ ██ ██ ██ ██ ██\n') + 16 | chalk.green(' ██████ ') + chalk.red(' ██ ██ ████████ ██ ██████ ██ ████████ ██ ██\n')); 17 | this.log(chalk.white.bold(' http://jhipster.github.io\n')); 18 | this.log(chalk.white('Welcome to the JHipster Docker Compose Generator ')); 19 | this.log(chalk.white('Files will be generated in folder: ' + chalk.yellow(this.destinationRoot()))); 20 | }, 21 | 22 | checkDocker: function() { 23 | var done = this.async(); 24 | 25 | shelljs.exec('docker -v', {silent:true},function(code, stdout, stderr) { 26 | if (stderr) { 27 | this.log(chalk.yellow.bold('WARNING!') + ' docker is not found on your computer.\n' + 28 | ' Read http://docs.docker.com/engine/installation/#installation\n'); 29 | } 30 | done(); 31 | }.bind(this)); 32 | }, 33 | 34 | checkDockerCompose: function() { 35 | var done = this.async(); 36 | 37 | shelljs.exec('docker-compose -v', {silent:true}, function(code, stdout, stderr) { 38 | if (stderr) { 39 | this.log(chalk.yellow.bold('WARNING!') + ' docker-compose is not found on your computer.\n' + 40 | ' Read https://docs.docker.com/compose/install/\n'); 41 | } 42 | done(); 43 | }.bind(this)); 44 | }, 45 | 46 | loadConfig: function() { 47 | 48 | this.defaultAppsFolders = this.config.get('appsFolders'); 49 | this.appConfigs = this.config.get('appConfigs'); 50 | this.useElk = this.config.get('useElk'); 51 | this.profile = this.config.get('profile'); 52 | this.jwtSecretKey = this.config.get('jwtSecretKey'); 53 | 54 | if(this.defaultAppsFolders !== undefined) { 55 | this.regenerate = true; 56 | this.log('\nFound .yo-rc.json config file...'); 57 | } 58 | } 59 | }, 60 | 61 | prompting: { 62 | askForPath: function() { 63 | var done = this.async(); 64 | 65 | var prompts = [{ 66 | type: 'input', 67 | name: 'directoryPath', 68 | message: 'Where are the applications located ?', 69 | default: '../', 70 | validate: function (input) { 71 | var path = this.destinationPath(input); 72 | if(shelljs.test('-d', path)) { 73 | var files = shelljs.ls('-l',this.destinationPath(input)); 74 | this.appsFolders = []; 75 | 76 | files.forEach(function(file) { 77 | if(file.isDirectory()) { 78 | if(shelljs.test('-f', file.name + '/.yo-rc.json')) { 79 | var fileData = this.fs.readJSON(file.name + '/.yo-rc.json'); 80 | if(fileData['generator-jhipster'] !== undefined) { 81 | this.appsFolders.push(file.name.match(/([^\/]*)\/*$/)[1]); 82 | } 83 | } 84 | } 85 | }, this); 86 | 87 | if(this.appsFolders.length === 0) { 88 | return 'No microservice or gateway found in ' + this.destinationPath(input); 89 | } else { 90 | return true; 91 | } 92 | } else { 93 | return path + ' is not a directory or doesn\'t exist'; 94 | } 95 | 96 | }.bind(this) 97 | }]; 98 | 99 | this.prompt(prompts, function (props) { 100 | this.directoryPath = props.directoryPath; 101 | 102 | //Removing monolithic apps and registry from appsFolders 103 | for(var i = 0; i < this.appsFolders.length; i++) { 104 | var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json'); 105 | var fileData = this.fs.readJSON(path); 106 | var config = fileData['generator-jhipster']; 107 | if(config.applicationType === 'monolith' || this.appsFolders[i]==='jhipster-registry' || this.appsFolders[i] === 'registry') { 108 | this.appsFolders.splice(i,1); 109 | i--; 110 | } 111 | } 112 | 113 | this.log(chalk.green(this.appsFolders.length + ' applications found at ' + this.destinationPath(this.directoryPath) + '\n')); 114 | 115 | done(); 116 | }.bind(this)); 117 | }, 118 | 119 | askForApps: function() { 120 | if(this.abort) return; 121 | var done = this.async(); 122 | 123 | var prompts = [{ 124 | type: 'checkbox', 125 | name: 'chosenApps', 126 | message: 'Which applications do you want in your DockerFile ?', 127 | choices: this.appsFolders, 128 | default: this.defaultAppsFolders, 129 | validate: function (input) { 130 | if(input.length === 0) { 131 | return 'Please choose at least one application'; 132 | } else return true; 133 | } 134 | }]; 135 | 136 | this.prompt(prompts, function (props) { 137 | this.appsFolders = props.chosenApps; 138 | 139 | this.appConfigs = []; 140 | 141 | //Loading configs 142 | for(var i = 0; i < this.appsFolders.length; i++) { 143 | var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json'); 144 | var fileData = this.fs.readJSON(path); 145 | var config = fileData['generator-jhipster']; 146 | this.appConfigs.push(config); 147 | } 148 | 149 | done(); 150 | }.bind(this)); 151 | }, 152 | 153 | askForElk: function() { 154 | if(this.abort) return; 155 | var done = this.async(); 156 | 157 | var prompts = [{ 158 | type: 'confirm', 159 | name: 'elk', 160 | message: 'Do you want ELK to monitor your applications ?', 161 | default: this.useElk && true 162 | }]; 163 | 164 | this.prompt(prompts, function(props) { 165 | this.useElk = props.elk; 166 | 167 | done(); 168 | }.bind(this)); 169 | }, 170 | 171 | askForProfile: function() { 172 | if(this.abort) return; 173 | var done = this.async(); 174 | 175 | var choices = ['dev', 'prod']; 176 | 177 | var prompts = [{ 178 | type: 'list', 179 | name: 'profile', 180 | message: 'Which profile do you want to use ?', 181 | choices: choices, 182 | default: this.profile || 'dev' 183 | }]; 184 | 185 | this.prompt(prompts, function(props) { 186 | this.profile = props.profile; 187 | 188 | done(); 189 | }.bind(this)); 190 | } 191 | }, 192 | 193 | configuring: { 194 | checkImages: function() { 195 | if(this.abort) return; 196 | 197 | this.log('\nChecking Docker images in applications directories...'); 198 | 199 | for (var i = 0; i < this.appsFolders.length; i++) { 200 | var imagePath = this.destinationPath(this.directoryPath + this.appsFolders[i] + '/target/docker/' + _.kebabCase(this.appConfigs[i].baseName) + '-0.0.1-SNAPSHOT.war'); 201 | if (!shelljs.test('-f', imagePath)) { 202 | this.log(chalk.red('\nDocker Image not found at ' + imagePath)); 203 | this.log(chalk.red('Please run "mvn package docker:build" in ' + this.destinationPath(this.directoryPath + this.appsFolders[i]) + ' to generate Docker image')); 204 | this.abort = true; 205 | } 206 | } 207 | 208 | if(!this.abort) this.log(chalk.green('Found Docker images, writing files...\n')); 209 | }, 210 | 211 | generateJwtSecret: function() { 212 | if(this.jwtSecretKey === undefined) { 213 | this.jwtSecretKey = crypto.randomBytes(20).toString('hex'); 214 | } 215 | }, 216 | 217 | setAppsFolderPaths: function() { 218 | this.appsFolderPaths = []; 219 | for (var i = 0; i < this.appsFolders.length; i++) { 220 | var path = this.destinationPath(this.directoryPath + this.appsFolders[i]); 221 | this.appsFolderPaths.push(path); 222 | } 223 | }, 224 | 225 | saveConfig: function() { 226 | if(this.abort) return; 227 | this.config.set('appsFolders', this.appsFolders); 228 | this.config.set('appConfigs', this.appConfigs); 229 | this.config.set('useElk', this.useElk); 230 | this.config.set('profile', this.profile); 231 | this.config.set('jwtSecretKey', this.jwtSecretKey); 232 | } 233 | }, 234 | 235 | writing: { 236 | writeDockerCompose: function() { 237 | if(this.abort) return; 238 | 239 | this.template('_docker-compose.yml', 'docker-compose.yml'); 240 | }, 241 | 242 | writeRegistryFiles: function() { 243 | if(this.abort) return; 244 | 245 | this.copy('registry.yml', 'registry.yml'); 246 | this.template('central-server-config/_application.yml', 'central-server-config/application.yml'); 247 | }, 248 | 249 | writeElkFiles: function() { 250 | if(!this.useElk || this.abort) return; 251 | 252 | this.copy('elk.yml', 'elk.yml'); 253 | this.copy('log-monitoring/log-config/logstash.conf', 'log-monitoring/log-config/logstash.conf'); 254 | this.copy('log-monitoring/log-data/.gitignore', 'log-monitoring/log-data/.gitignore'); 255 | } 256 | }, 257 | end: function() { 258 | if(this.abort) return; 259 | 260 | this.log('\n' + chalk.bold.green('##### USAGE #####')); 261 | this.log('First launch the JHipster Registry by running : ' + chalk.cyan('docker-compose up -d jhipster-registry')); 262 | this.log('Wait a minute, then launch all your applications by running : ' + chalk.cyan('docker-compose up -d')); 263 | } 264 | }); 265 | -------------------------------------------------------------------------------- /generators/app/templates/_docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | jhipster-registry: 4 | extends: 5 | file: registry.yml 6 | service: jhipster-registry 7 | volumes: 8 | - ./central-server-config:/central-config 9 | environment: 10 | <%_ if(profile=='dev') {_%> 11 | - SPRING_PROFILES_ACTIVE=dev,native 12 | <%_ } else { _%> 13 | - GIT_URI=https://github.com/jhipster/jhipster-registry/ 14 | - GIT_SEARCH_PATH=central-config 15 | <%_ } _%> 16 | <%_ for(var i=0; i 17 | 18 | <%= appConfigs[i].baseName.toLowerCase() %>: 19 | extends: 20 | file: <%= appsFolderPaths[i] %>/src/main/docker/app.<%= profile %>.yml 21 | service: <%= appConfigs[i].baseName.toLowerCase() %>-<% if(profile=='dev') {%><%= profile %>-<%}%>app 22 | <%_ if(profile=='prod') {_%> 23 | <%= appConfigs[i].baseName.toLowerCase() %>-<%= appConfigs[i].prodDatabaseType %>: 24 | extends: 25 | file: <%= appsFolderPaths[i] %>/src/main/docker/db.prod.yml 26 | service: <%= appConfigs[i].baseName.toLowerCase() %>-<%= appConfigs[i].prodDatabaseType %> 27 | <%_ }} _%> 28 | <%_ if (useElk) { _%> 29 | 30 | elk-elasticsearch: 31 | extends: 32 | file: elk.yml 33 | service: elk-elasticsearch 34 | elk-logstash: 35 | extends: 36 | file: elk.yml 37 | service: elk-logstash 38 | jhipster-console: 39 | extends: 40 | file: elk.yml 41 | service: jhipster-console 42 | <%_ } _%> 43 | -------------------------------------------------------------------------------- /generators/app/templates/central-server-config/_application.yml: -------------------------------------------------------------------------------- 1 | #common config shared between all apps 2 | configserver: 3 | status: ${spring.cloud.config.uri}/${spring.cloud.config.label}/${spring.cloud.config.name}-${spring.cloud.config.profile}.yml 4 | jhipster: 5 | security: 6 | authentication: 7 | jwt: 8 | secret: <%= jwtSecretKey %> 9 | <%_ if (useElk) { _%> 10 | logging: 11 | logstash: # forward logs to ELK 12 | enabled: true 13 | host: elk-logstash 14 | metrics: 15 | logs: # report metrics in the logs 16 | enabled: true 17 | reportFrequency: 60 # in seconds 18 | <%_ } _%> 19 | -------------------------------------------------------------------------------- /generators/app/templates/elk.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | elk-elasticsearch: 4 | image: elasticsearch:2.2.0 5 | ports: 6 | - "9200:9200" 7 | - "9300:9300" 8 | volumes: 9 | - ./log-monitoring/log-data:/usr/share/elasticsearch/data 10 | elk-logstash: 11 | image: logstash:2.2.2 12 | volumes: 13 | - ./log-monitoring/log-config/:/config-dir 14 | command: logstash -f /config-dir/logstash.conf 15 | ports: 16 | - "5000:5000/udp" 17 | jhipster-console: 18 | image: jhipster/jhipster-console 19 | ports: 20 | - "5601:5601" 21 | environment: 22 | - ELASTICSEARCH_URL=http://elk-elasticsearch:9200 23 | -------------------------------------------------------------------------------- /generators/app/templates/log-monitoring/log-config/logstash.conf: -------------------------------------------------------------------------------- 1 | input { 2 | udp { 3 | port => 5000 4 | type => syslog 5 | codec => json 6 | } 7 | } 8 | 9 | filter { 10 | if [logger_name] =~ "metrics" { 11 | kv { 12 | source => "message" 13 | field_split => ", " 14 | prefix => "metric_" 15 | } 16 | mutate { 17 | convert => { "metric_value" => "float" } 18 | convert => { "metric_count" => "integer" } 19 | convert => { "metric_min" => "float" } 20 | convert => { "metric_max" => "float" } 21 | convert => { "metric_mean" => "float" } 22 | convert => { "metric_stddev" => "float" } 23 | convert => { "metric_median" => "float" } 24 | convert => { "metric_p75" => "float" } 25 | convert => { "metric_p95" => "float" } 26 | convert => { "metric_p98" => "float" } 27 | convert => { "metric_p99" => "float" } 28 | convert => { "metric_p999" => "float" } 29 | convert => { "metric_mean_rate" => "float" } 30 | convert => { "metric_m1" => "float" } 31 | convert => { "metric_m5" => "float" } 32 | convert => { "metric_m15" => "float" } 33 | } 34 | } 35 | mutate { 36 | add_field => { "instance_name" => "%{app_name}-%{host}:%{app_port}" } 37 | } 38 | } 39 | 40 | output { 41 | elasticsearch { 42 | hosts => ["elk-elasticsearch"] 43 | } 44 | stdout { 45 | codec => rubydebug 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /generators/app/templates/log-monitoring/log-data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /generators/app/templates/registry.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | jhipster-registry: 4 | container_name: jhipster-registry 5 | image: jhipster/jhipster-registry 6 | ports: 7 | - "8761:8761" 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-jhipster-docker-compose", 3 | "version": "0.0.0", 4 | "description": "Generate Docker to run JHipster apps", 5 | "homepage": "", 6 | "author": { 7 | "name": "William Marques", 8 | "email": "wmarques@ippon.fr", 9 | "url": "https://github.com/wmarques" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/hipster-labs/generator-jhipster-docker-compose.git" 14 | }, 15 | "files": [ 16 | "generators" 17 | ], 18 | "main": "generators/index.js", 19 | "keywords": [ 20 | "jhipster", 21 | "yeoman-generator" 22 | ], 23 | "dependencies": { 24 | "chalk": "1.0.0", 25 | "shelljs": "0.6.0", 26 | "lodash": "4.6.1", 27 | "yeoman-generator": "0.21.1" 28 | }, 29 | "license": "Apache-2.0" 30 | } 31 | --------------------------------------------------------------------------------