generator-reddgen
Version:
Reddgen is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
316 lines (277 loc) • 12.9 kB
JavaScript
/**
* Copyright 2013-2022 the original author or authors from the Reddgen project.
*
* This file is part of the Reddgen project, see https://www.reddgen.tech/
* for more information.
*
* 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
*
* https://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.
*/
const chalk = require('chalk');
const shelljs = require('shelljs');
const jsyaml = require('js-yaml');
const pathjs = require('path');
const normalize = require('normalize-path');
const BaseDockerGenerator = require('../generator-base-docker');
const { INITIALIZING_PRIORITY, PROMPTING_PRIORITY, CONFIGURING_PRIORITY, LOADING_PRIORITY, PREPARING_PRIORITY, WRITING_PRIORITY } =
require('../../lib/constants/priorities.cjs').compat;
const writeFiles = require('./files').writeFiles;
const { GATEWAY, MONOLITH } = require('../../jdl/reddgen/application-types');
const { PROMETHEUS } = require('../../jdl/reddgen/monitoring-types');
const { EUREKA } = require('../../jdl/reddgen/service-discovery-types');
const { CASSANDRA, COUCHBASE, MONGODB, ORACLE, NO: NO_DATABASE } = require('../../jdl/reddgen/database-types');
const { ELASTICSEARCH } = require('../../jdl/reddgen/search-engine-types');
const { KAFKA } = require('../../jdl/reddgen/message-broker-types');
const { MEMCACHED, REDIS } = require('../../jdl/reddgen/cache-types');
const { GENERATOR_DOCKER_COMPOSE } = require('../generator-list');
/* eslint-disable consistent-return */
module.exports = class extends BaseDockerGenerator {
async _postConstruct() {
if (!this.fromBlueprint) {
await this.composeWithBlueprints(GENERATOR_DOCKER_COMPOSE);
}
}
_initializing() {
return {
...super._initializing(),
checkDockerCompose() {
if (this.skipChecks) return;
const done = this.async();
shelljs.exec('docker-compose -v', { silent: true }, (code, stdout, stderr) => {
if (stderr) {
this.log(
chalk.red(
'Docker Compose 1.6.0 or later is not installed on your computer.\n' +
' Read https://docs.docker.com/compose/install/\n'
)
);
} else {
const composeVersion = stdout.split(' ')[2].replace(/,/g, '');
const composeVersionMajor = composeVersion.split('.')[0];
const composeVersionMinor = composeVersion.split('.')[1];
if (composeVersionMajor < 1 || (composeVersionMajor === 1 && composeVersionMinor < 6)) {
this.log(
chalk.red(
`$Docker Compose version 1.6.0 or later is not installed on your computer.
Docker Compose version found: ${composeVersion}
Read https://docs.docker.com/compose/install`
)
);
}
}
done();
});
},
};
}
get [INITIALIZING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._initializing();
}
_prompting() {
return super._prompting();
}
get [PROMPTING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._prompting();
}
_configuring() {
return {
sayHello() {
this.log(chalk.white(`${chalk.bold('🐳')} Welcome to the Reddgen Docker Compose Sub-Generator ${chalk.bold('🐳')}`));
this.log(chalk.white(`Files will be generated in folder: ${chalk.yellow(this.destinationRoot())}`));
},
...super._configuring(),
saveConfig() {
this.config.set({
appsFolders: this.appsFolders,
directoryPath: this.directoryPath,
gatewayType: this.gatewayType,
clusteredDbApps: this.clusteredDbApps,
monitoring: this.monitoring,
serviceDiscoveryType: this.serviceDiscoveryType,
jwtSecretKey: this.jwtSecretKey,
});
},
};
}
get [CONFIGURING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._configuring();
}
_preparing() {
return {
loadConfig() {
this.usesOauth2 = this.appConfigs.some(appConfig => appConfig.authenticationTypeOauth2);
this.useKafka = this.appConfigs.some(appConfig => appConfig.messageBroker === KAFKA);
this.entryPort = 8080;
},
setAppsYaml() {
this.appsYaml = [];
this.keycloakRedirectUris = '';
this.appConfigs.forEach(appConfig => {
const lowercaseBaseName = appConfig.baseName.toLowerCase();
const parentConfiguration = {};
const path = this.destinationPath(this.directoryPath + appConfig.appFolder);
// Add application configuration
const yaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/app.yml`));
const yamlConfig = yaml.services[`${lowercaseBaseName}-app`];
if (appConfig.applicationType === GATEWAY || appConfig.applicationType === MONOLITH) {
this.keycloakRedirectUris += `"http://localhost:${appConfig.composePort}/*", "https://localhost:${appConfig.composePort}/*", `;
if (appConfig.devServerPort !== undefined) {
this.keycloakRedirectUris += `"http://localhost:${appConfig.devServerPort}/*", `;
}
// Split ports by ":" and take last 2 elements to skip the hostname/IP if present
const ports = yamlConfig.ports[0].split(':').slice(-2);
ports[0] = appConfig.composePort;
yamlConfig.ports[0] = ports.join(':');
}
if (appConfig.applicationType === MONOLITH && this.monitoring === PROMETHEUS) {
yamlConfig.environment.push('REDDGEN_LOGGING_LOGSTASH_ENABLED=false');
yamlConfig.environment.push('MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true');
}
if (this.serviceDiscoveryType === EUREKA) {
// Set the Reddgen Registry password
yamlConfig.environment.push(`REDDGEN_REGISTRY_PASSWORD=${this.adminPassword}`);
}
if (!this.serviceDiscoveryType && appConfig.skipClient) {
yamlConfig.environment.push('SERVER_PORT=80'); // to simplify service resolution in docker/k8s
}
parentConfiguration[`${lowercaseBaseName}`] = yamlConfig;
// Add database configuration
const database = appConfig.databaseTypeSql ? appConfig.prodDatabaseType : appConfig.databaseType;
if (database !== NO_DATABASE && database !== ORACLE) {
const relativePath = normalize(pathjs.relative(this.destinationRoot(), `${path}/src/main/docker`));
const databaseYaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/${database}.yml`));
const databaseServiceName = `${lowercaseBaseName}-${database}`;
let databaseYamlConfig = databaseYaml.services[databaseServiceName];
// Don't export database ports
delete databaseYamlConfig.ports;
if (database === CASSANDRA) {
// migration service config
const cassandraMigrationConfig = databaseYaml.services[`${databaseServiceName}-migration`];
// replace script with prod
cassandraMigrationConfig.environment = [
...cassandraMigrationConfig.environment.filter(envVariable => !envVariable.includes('CREATE_KEYSPACE_SCRIPT=')),
'CREATE_KEYSPACE_SCRIPT=create-keyspace-prod.cql',
];
cassandraMigrationConfig.build.context = relativePath;
const cqlFilesRelativePath = normalize(pathjs.relative(this.destinationRoot(), `${path}/src/main/resources/config/cql`));
cassandraMigrationConfig.volumes[0] = `${cqlFilesRelativePath}:/cql:ro`;
parentConfiguration[`${databaseServiceName}-migration`] = cassandraMigrationConfig;
}
if (database === COUCHBASE) {
databaseYamlConfig.build.context = relativePath;
}
if (appConfig.clusteredDb) {
const clusterDbYaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/${database}-cluster.yml`));
const dbNodeConfig = clusterDbYaml.services[`${databaseServiceName}-node`];
dbNodeConfig.build.context = relativePath;
databaseYamlConfig = clusterDbYaml.services[databaseServiceName];
delete databaseYamlConfig.ports;
if (database === COUCHBASE) {
databaseYamlConfig.build.context = relativePath;
}
parentConfiguration[`${databaseServiceName}-node`] = dbNodeConfig;
if (database === MONGODB) {
parentConfiguration[`${databaseServiceName}-config`] = clusterDbYaml.services[`${databaseServiceName}-config`];
}
}
parentConfiguration[databaseServiceName] = databaseYamlConfig;
}
// Add search engine configuration
const searchEngine = appConfig.searchEngine;
if (searchEngine === ELASTICSEARCH) {
const searchEngineYaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/${searchEngine}.yml`));
const searchEngineConfig = searchEngineYaml.services[`${lowercaseBaseName}-${searchEngine}`];
delete searchEngineConfig.ports;
parentConfiguration[`${lowercaseBaseName}-${searchEngine}`] = searchEngineConfig;
}
// Add Memcached support
const cacheProvider = appConfig.cacheProvider;
if (cacheProvider === MEMCACHED) {
this.useMemcached = true;
const memcachedYaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/memcached.yml`));
const memcachedConfig = memcachedYaml.services[`${lowercaseBaseName}-memcached`];
delete memcachedConfig.ports;
parentConfiguration[`${lowercaseBaseName}-memcached`] = memcachedConfig;
}
// Add Redis support
if (cacheProvider === REDIS) {
this.useRedis = true;
const redisYaml = jsyaml.load(this.fs.read(`${path}/src/main/docker/redis.yml`));
const redisConfig = redisYaml.services[`${lowercaseBaseName}-redis`];
delete redisConfig.ports;
parentConfiguration[`${lowercaseBaseName}-redis`] = redisConfig;
}
// Expose authenticationType
this.authenticationType = appConfig.authenticationType;
// Dump the file
let yamlString = jsyaml.dump(parentConfiguration, { indent: 2, lineWidth: -1 });
// Add extra indentation for each lines
const yamlArray = yamlString.split('\n');
for (let j = 0; j < yamlArray.length; j++) {
yamlArray[j] = ` ${yamlArray[j]}`;
}
yamlString = yamlArray.join('\n');
this.appsYaml.push(yamlString);
this.skipClient = appConfig.skipClient;
});
},
};
}
get [PREPARING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._preparing();
}
_loading() {
return {
loadPlatformConfig() {
this.loadDeploymentConfig(this);
},
};
}
get [LOADING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._loading();
}
_writing() {
return writeFiles();
}
get [WRITING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._writing();
}
_end() {
if (this.hasWarning) {
this.log(`\n${chalk.yellow.bold('WARNING!')} Docker Compose configuration generated, but no Jib cache found`);
this.log('If you forgot to generate the Docker image for this application, please run:');
this.log(chalk.red(this.warningMessage));
} else {
this.log(`\n${chalk.bold.green('Docker Compose configuration successfully generated!')}`);
}
this.log(`You can launch all your infrastructure by running : ${chalk.cyan('docker-compose up -d')}`);
if (this.gatewayNb + this.monolithicNb > 1) {
this.log('\nYour applications will be accessible on these URLs:');
this.appConfigs.forEach(appConfig => {
if (appConfig.applicationType === GATEWAY || appConfig.applicationType === MONOLITH) {
this.log(`\t- ${appConfig.baseName}: http://localhost:${appConfig.composePort}`);
}
});
this.log('\n');
}
}
end() {
if (this.delegateToBlueprint) return {};
return this._end();
}
};